]> source.dussan.org Git - vaadin-framework.git/commitdiff
Fix keyboard navigating in combo box (#11333).
authorDmitrii Rogozin <dmitrii@vaadin.com>
Fri, 9 May 2014 16:13:59 +0000 (19:13 +0300)
committerVaadin Code Review <review@vaadin.com>
Wed, 21 May 2014 05:18:35 +0000 (05:18 +0000)
Extract code which focuses on item after changing the page. Deferring  this method allows to update the list of items before focusing.

Change-Id: I7d249c2abbd5c24ca2d798736e483f2b7dfa59f1

client/src/com/vaadin/client/ui/VFilterSelect.java
client/src/com/vaadin/client/ui/combobox/ComboBoxConnector.java
uitest/src/com/vaadin/tests/components/combobox/ComboBoxScrollingWithArrows.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/combobox/ComboBoxScrollingWithArrowsTest.java [new file with mode: 0644]

index cfa128fb15c5d2d0e2dd1484b718447fa36a99ff..b945b4eb00ec40915b5f36540df1e345a0fafbd2 100644 (file)
@@ -1,12 +1,12 @@
 /*
  * Copyright 2000-2014 Vaadin Ltd.
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
  * the License at
- * 
+ *
  * http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -80,7 +80,7 @@ import com.vaadin.shared.ui.combobox.FilteringMode;
 
 /**
  * Client side implementation of the Select component.
- * 
+ *
  * TODO needs major refactoring (to be extensible etc)
  */
 @SuppressWarnings("deprecation")
@@ -100,7 +100,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
 
         /**
          * Constructor
-         * 
+         *
          * @param uidl
          *            The UIDL recieved from the server
          */
@@ -149,7 +149,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
 
         /**
          * Get the option key which represents the item on the server side.
-         * 
+         *
          * @return The key of the item
          */
         public String getOptionKey() {
@@ -158,7 +158,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
 
         /**
          * Get the URI of the icon. Used when constructing the displayed option.
-         * 
+         *
          * @return
          */
         public String getIconUri() {
@@ -252,7 +252,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
 
         /**
          * Shows the popup where the user can see the filtered options
-         * 
+         *
          * @param currentSuggestions
          *            The filtered suggestions
          * @param currentPage
@@ -275,6 +275,12 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
              * correctly. This issue manifests when a Combobox is placed in
              * another popupView which also needs to calculate the absoluteTop()
              * to position itself. #9768
+             *
+             * After deferring the showSuggestions method, a problem with
+             * navigating in the combo box occurs. Because of that the method
+             * navigateItemAfterPageChange in ComboBoxConnector class, which
+             * navigates to the exact item after page was changed also was
+             * marked as deferred. #11333
              */
             final SuggestionPopup popup = this;
             Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@@ -332,7 +338,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
 
         /**
          * Should the next page button be visible to the user?
-         * 
+         *
          * @param active
          */
         private void setNextButtonActive(boolean active) {
@@ -352,7 +358,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
 
         /**
          * Should the previous page button be visible to the user
-         * 
+         *
          * @param active
          */
         private void setPrevButtonActive(boolean active) {
@@ -511,7 +517,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
          * amount of items are visible at a time and a scrollbar or buttons are
          * visible to change page. If paging is turned of then all options are
          * rendered into the popup menu.
-         * 
+         *
          * @param paging
          *            Should the paging be turned on?
          */
@@ -616,7 +622,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
 
         /**
          * Was the popup just closed?
-         * 
+         *
          * @return true if popup was just closed
          */
         public boolean isJustClosed() {
@@ -645,7 +651,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
 
         /**
          * Updates style names in suggestion popup to help theme building.
-         * 
+         *
          * @param uidl
          *            UIDL for the whole combo box
          * @param componentState
@@ -725,7 +731,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
 
         /**
          * Sets the suggestions rendered in the menu
-         * 
+         *
          * @param suggestions
          *            The suggestions to be rendered in the menu
          */
@@ -920,7 +926,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
     /**
      * TextBox variant used as input element for filter selects, which prevents
      * selecting text when disabled.
-     * 
+     *
      * @since 7.1.5
      */
     public class FilterSelectTextBox extends TextBox {
@@ -1172,7 +1178,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
      * It is invoked during the Constructor and should only be overridden if a
      * custom TextBox shall be used. The overriding method cannot use any
      * instance variables.
-     * 
+     *
      * @since 7.1.5
      * @return TextBox instance used by this VFilterSelect
      */
@@ -1185,7 +1191,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
      * instance. It is invoked during the Constructor and should only be
      * overridden if a custom SuggestionPopup shall be used. The overriding
      * method cannot use any instance variables.
-     * 
+     *
      * @since 7.1.5
      * @return SuggestionPopup instance used by this VFilterSelect
      */
@@ -1213,7 +1219,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
 
     /**
      * Does the Select have more pages?
-     * 
+     *
      * @return true if a next page exists, else false if the current page is the
      *         last page
      */
@@ -1228,7 +1234,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
     /**
      * Filters the options at a certain page. Uses the text box input as a
      * filter
-     * 
+     *
      * @param page
      *            The page which items are to be filtered
      */
@@ -1238,7 +1244,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
 
     /**
      * Filters the options at certain page using the given filter
-     * 
+     *
      * @param page
      *            The page to filter
      * @param filter
@@ -1250,7 +1256,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
 
     /**
      * Filters the options at certain page using the given filter
-     * 
+     *
      * @param page
      *            The page to filter
      * @param filter
@@ -1315,7 +1321,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
 
     /**
      * Sets the text in the text box.
-     * 
+     *
      * @param text
      *            the text to set in the text box
      */
@@ -1344,7 +1350,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
      * shown in the text box if nothing has been entered.
      * <p>
      * For internal use only. May be removed or replaced in the future.
-     * 
+     *
      * @param text
      *            The text the text box should contain.
      */
@@ -1359,7 +1365,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
 
     /**
      * Triggered when a suggestion is selected
-     * 
+     *
      * @param suggestion
      *            The suggestion that just got selected.
      */
@@ -1399,7 +1405,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
     /**
      * Sets the icon URI of the selected item. The icon is shown on the left
      * side of the item caption text. Set the URI to null to remove the icon.
-     * 
+     *
      * @param iconUri
      *            The URI of the icon
      */
@@ -1525,7 +1531,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
 
     /**
      * Triggered when a key is pressed in the text box
-     * 
+     *
      * @param event
      *            The KeyDownEvent
      */
@@ -1570,7 +1576,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
 
     /**
      * Triggered when a key was pressed in the suggestion popup.
-     * 
+     *
      * @param event
      *            The KeyDownEvent of the key
      */
@@ -1652,7 +1658,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
 
     /**
      * Triggered when a key was depressed
-     * 
+     *
      * @param event
      *            The KeyUpEvent of the key depressed
      */
@@ -1987,7 +1993,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
     /**
      * Get the width of the select in pixels where the text area and icon has
      * been included.
-     * 
+     *
      * @return The width in pixels
      */
     private int getMainWidth() {
@@ -2004,7 +2010,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
 
     /**
      * Handles special behavior of the mouse down event
-     * 
+     *
      * @param event
      */
     private void handleMouseDownEvent(Event event) {
index 84d147518569cfcd0241b470d7c9be522960a686..97e8f8363b3c5b967712e8199616736e697aa9ff 100644 (file)
@@ -1,12 +1,12 @@
 /*
  * Copyright 2000-2014 Vaadin Ltd.
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
  * the License at
- * 
+ *
  * http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -19,6 +19,8 @@ import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.core.client.Scheduler.ScheduledCommand;
 import com.vaadin.client.ApplicationConnection;
 import com.vaadin.client.Paintable;
 import com.vaadin.client.UIDL;
@@ -48,7 +50,7 @@ public class ComboBoxConnector extends AbstractFieldConnector implements
 
     /*
      * (non-Javadoc)
-     * 
+     *
      * @see com.vaadin.client.Paintable#updateFromUIDL(com.vaadin.client.UIDL,
      * com.vaadin.client.ApplicationConnection)
      */
@@ -179,28 +181,14 @@ public class ComboBoxConnector extends AbstractFieldConnector implements
             if (!getWidget().popupOpenerClicked
                     && getWidget().selectPopupItemWhenResponseIsReceived != VFilterSelect.Select.NONE) {
                 // we're paging w/ arrows
-                if (getWidget().selectPopupItemWhenResponseIsReceived == VFilterSelect.Select.LAST) {
-                    getWidget().suggestionPopup.menu.selectLastItem();
-                } else {
-                    getWidget().suggestionPopup.menu.selectFirstItem();
-                }
-
-                // This is used for paging so we update the keyboard selection
-                // variable as well.
-                MenuItem activeMenuItem = getWidget().suggestionPopup.menu
-                        .getSelectedItem();
-                getWidget().suggestionPopup.menu
-                        .setKeyboardSelectedItem(activeMenuItem);
-
-                // Update text field to contain the correct text
-                getWidget().setTextboxText(activeMenuItem.getText());
-                getWidget().tb.setSelectionRange(
-                        getWidget().lastFilter.length(),
-                        activeMenuItem.getText().length()
-                                - getWidget().lastFilter.length());
-
-                getWidget().selectPopupItemWhenResponseIsReceived = VFilterSelect.Select.NONE; // reset
+                Scheduler.get().scheduleDeferred(new ScheduledCommand() {
+                    @Override
+                    public void execute() {
+                        navigateItemAfterPageChange();
+                    }
+                });
             }
+
             if (getWidget().updateSelectionWhenReponseIsReceived) {
                 getWidget().suggestionPopup.menu
                         .doPostFilterSelectedItemAction();
@@ -231,6 +219,38 @@ public class ComboBoxConnector extends AbstractFieldConnector implements
         getWidget().initDone = true;
     }
 
+    /*
+     * This method navigates to the proper item in the combobox page. This
+     * should be executed after setSuggestions() method which is called from
+     * vFilterSelect.showSuggestions(). ShowSuggestions() method builds the page
+     * content. As far as setSuggestions() method is called as deferred,
+     * navigateItemAfterPageChange method should be also be called as deferred.
+     * #11333
+     */
+    private void navigateItemAfterPageChange() {
+        if (getWidget().selectPopupItemWhenResponseIsReceived == VFilterSelect.Select.LAST) {
+            getWidget().suggestionPopup.menu.selectLastItem();
+        } else {
+            getWidget().suggestionPopup.menu.selectFirstItem();
+        }
+
+        // This is used for paging so we update the keyboard selection
+        // variable as well.
+        MenuItem activeMenuItem = getWidget().suggestionPopup.menu
+                .getSelectedItem();
+        getWidget().suggestionPopup.menu
+                .setKeyboardSelectedItem(activeMenuItem);
+
+        // Update text field to contain the correct text
+        getWidget().setTextboxText(activeMenuItem.getText());
+        getWidget().tb.setSelectionRange(
+                getWidget().lastFilter.length(),
+                activeMenuItem.getText().length()
+                        - getWidget().lastFilter.length());
+
+        getWidget().selectPopupItemWhenResponseIsReceived = VFilterSelect.Select.NONE; // reset
+    }
+
     private void performSelection(String selectedKey) {
         // some item selected
         for (FilterSelectSuggestion suggestion : getWidget().currentSuggestions) {
diff --git a/uitest/src/com/vaadin/tests/components/combobox/ComboBoxScrollingWithArrows.java b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxScrollingWithArrows.java
new file mode 100644 (file)
index 0000000..d9ae7da
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.components.combobox;
+
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.AbstractLayout;
+import com.vaadin.ui.ComboBox;
+import com.vaadin.ui.VerticalLayout;
+
+/**
+ * Test UI verifying navigating in combobox via arrow keys.
+ */
+public class ComboBoxScrollingWithArrows extends AbstractTestUI {
+    final String DESCRIPTION = "When positioned on last item in the page and press downArrow key - should open new page and set focus on the first item.";
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.tests.components.AbstractTestUI#setup(com.vaadin.server.
+     * VaadinRequest)
+     */
+    @Override
+    protected void setup(VaadinRequest request) {
+        VerticalLayout layout = new VerticalLayout();
+        addComponent(layout);
+        addComboBox(layout);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.tests.components.AbstractTestUI#getTestDescription()
+     */
+    @Override
+    protected String getTestDescription() {
+        return DESCRIPTION;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.tests.components.AbstractTestUI#getTicketNumber()
+     */
+    @Override
+    protected Integer getTicketNumber() {
+        return 11333;
+    }
+
+    private void addComboBox(AbstractLayout layout) {
+        ComboBox box = new ComboBox();
+        for (int i = 0; i < 100; i++) {
+            box.addItem("item " + i);
+        }
+        box.setPageLength(10);
+        box.setNullSelectionAllowed(false);
+        layout.addComponent(box);
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/components/combobox/ComboBoxScrollingWithArrowsTest.java b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxScrollingWithArrowsTest.java
new file mode 100644 (file)
index 0000000..bc03593
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.components.combobox;
+
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.WebElement;
+
+import com.vaadin.testbench.By;
+import com.vaadin.tests.tb3.MultiBrowserTest;
+
+/**
+ * When pressed down key, while positioned on the last item - should show next
+ * page and focus on the first item of the next page.
+ */
+public class ComboBoxScrollingWithArrowsTest extends MultiBrowserTest {
+
+    @Before
+    public void openURL() {
+        openTestURL();
+    }
+
+    @Test
+    public void scrollDownArrowKeyTest() throws InterruptedException {
+        final int ITEMS_PER_PAGE = 10;
+        // Selenium is used instead of TestBench4, because there is no method to
+        // access the popup of the combobox
+        // The method ComboBoxElement.openPopup() opens the popup, but doesn't
+        // provide any way to access the popup and send keys to it.
+        // Ticket #13756
+        WebElement dropDownComboBox = driver.findElement(By
+                .className("v-filterselect-input"));
+        // opens Lookup
+        dropDownComboBox.sendKeys(Keys.DOWN);
+        // go to the last item and then one more
+        for (int i = 0; i < ITEMS_PER_PAGE + 1; i++) {
+            dropDownComboBox.sendKeys(Keys.DOWN);
+        }
+        String expected = "item " + ITEMS_PER_PAGE;// item 10
+
+        List<WebElement> items = driver.findElements(By
+                .className("gwt-MenuItem-selected"));
+        String actual = items.get(0).getText();
+        Assert.assertEquals(expected, actual);
+    }
+
+    @Test
+    public void scrollUpArrowKeyTest() throws InterruptedException {
+        final int ITEMS_PER_PAGE = 10;
+        WebElement dropDownComboBox = driver.findElement(By
+                .className("v-filterselect-input"));
+        // opens Lookup
+        dropDownComboBox.sendKeys(Keys.DOWN);
+        // go to the last item and then one more
+        for (int i = 0; i < ITEMS_PER_PAGE + 1; i++) {
+            dropDownComboBox.sendKeys(Keys.DOWN);
+        }
+        // move to one item up
+        dropDownComboBox.sendKeys(Keys.UP);
+        String expected = "item " + (ITEMS_PER_PAGE - 1);// item 9
+        List<WebElement> items = driver.findElements(By
+                .className("gwt-MenuItem-selected"));
+        String actual = items.get(0).getText();
+        Assert.assertEquals(expected, actual);
+    }
+}