]> source.dussan.org Git - vaadin-framework.git/commitdiff
Allow defining a focus delegate component for CustomField (#20336)
authorPekka Hyvönen <pekka@vaadin.com>
Thu, 15 Dec 2016 11:46:05 +0000 (13:46 +0200)
committerAleksi Hietanen <aleksi@vaadin.com>
Thu, 15 Dec 2016 11:46:05 +0000 (13:46 +0200)
13 files changed:
client/src/main/java/com/vaadin/client/ui/VCustomField.java [new file with mode: 0644]
client/src/main/java/com/vaadin/client/ui/customfield/CustomFieldConnector.java
compatibility-client/src/main/java/com/vaadin/v7/client/ui/VCustomField.java [new file with mode: 0644]
compatibility-client/src/main/java/com/vaadin/v7/client/ui/customfield/CustomFieldConnector.java
compatibility-server/src/main/java/com/vaadin/v7/ui/CustomField.java
compatibility-shared/src/main/java/com/vaadin/v7/shared/AbstractFieldState.java
server/src/main/java/com/vaadin/ui/CustomField.java
shared/src/main/java/com/vaadin/shared/ui/customfield/CustomFieldState.java
uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorCustomField.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/fieldgroup/ComplexPerson.java
uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridEditorCustomField.java [deleted file]
uitest/src/test/java/com/vaadin/tests/components/grid/GridEditorCustomFieldTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridEditorCustomFieldTest.java [deleted file]

diff --git a/client/src/main/java/com/vaadin/client/ui/VCustomField.java b/client/src/main/java/com/vaadin/client/ui/VCustomField.java
new file mode 100644 (file)
index 0000000..2d5039f
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2000-2016 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.client.ui;
+
+import com.vaadin.client.Focusable;
+
+public class VCustomField extends VCustomComponent implements Focusable {
+
+    private Focusable focusDelegate;
+
+    @Override
+    public void focus() {
+        if (focusDelegate != null) {
+            focusDelegate.focus();
+        }
+    }
+
+    /**
+     * Sets the focusable widget to focus instead of this custom field.
+     *
+     * @param focusDelegate
+     *            the widget to delegate focus to
+     */
+    public void setFocusDelegate(Focusable focusDelegate) {
+        this.focusDelegate = focusDelegate;
+
+    }
+
+    /**
+     * Sets the focusable widget to focus instead of this custom field.
+     *
+     * @param focusDelegate
+     *            the widget to delegate focus to
+     */
+    public void setFocusDelegate(
+            final com.google.gwt.user.client.ui.Focusable focusDelegate) {
+        this.focusDelegate = new Focusable() {
+            @Override
+            public void focus() {
+                focusDelegate.setFocus(true);
+            }
+        };
+
+    }
+
+}
index c058522c4e5418abf5201f3dec9fd3ca46fa7846..47c9da6732d6de1af7b1a0ad07cd3fdbd3100fba 100644 (file)
@@ -17,15 +17,18 @@ package com.vaadin.client.ui.customfield;
 
 import java.util.Collections;
 import java.util.List;
+import java.util.logging.Logger;
 
 import com.google.gwt.event.shared.HandlerRegistration;
 import com.google.gwt.user.client.ui.Widget;
 import com.vaadin.client.ComponentConnector;
 import com.vaadin.client.ConnectorHierarchyChangeEvent;
 import com.vaadin.client.ConnectorHierarchyChangeEvent.ConnectorHierarchyChangeHandler;
+import com.vaadin.client.Focusable;
 import com.vaadin.client.HasComponentsConnector;
+import com.vaadin.client.communication.StateChangeEvent;
 import com.vaadin.client.ui.AbstractFieldConnector;
-import com.vaadin.client.ui.VCustomComponent;
+import com.vaadin.client.ui.VCustomField;
 import com.vaadin.shared.ui.Connect;
 import com.vaadin.shared.ui.customfield.CustomFieldState;
 import com.vaadin.ui.CustomField;
@@ -44,8 +47,13 @@ public class CustomFieldConnector extends AbstractFieldConnector
     }
 
     @Override
-    public VCustomComponent getWidget() {
-        return (VCustomComponent) super.getWidget();
+    public CustomFieldState getState() {
+        return (CustomFieldState) super.getState();
+    }
+
+    @Override
+    public VCustomField getWidget() {
+        return (VCustomField) super.getWidget();
     }
 
     @Override
@@ -53,6 +61,32 @@ public class CustomFieldConnector extends AbstractFieldConnector
         // NOP, custom field does not render the caption of its content
     }
 
+    @Override
+    public void onStateChanged(StateChangeEvent stateChangeEvent) {
+        super.onStateChanged(stateChangeEvent);
+        if (getState().focusDelegate != null) {
+            Widget widget = ((ComponentConnector) getState().focusDelegate)
+                    .getWidget();
+            if (widget instanceof Focusable) {
+                getWidget().setFocusDelegate((Focusable) widget);
+            } else if (widget instanceof com.google.gwt.user.client.ui.Focusable) {
+                getWidget().setFocusDelegate(
+                        (com.google.gwt.user.client.ui.Focusable) widget);
+            } else {
+                getLogger().warning(
+                        "The given focus delegate does not implement Focusable: "
+                                + widget.getClass().getName());
+            }
+        } else {
+            getWidget().setFocusDelegate((Focusable) null);
+        }
+
+    }
+
+    private static Logger getLogger() {
+        return Logger.getLogger(CustomFieldConnector.class.getName());
+    }
+
     @Override
     public void onConnectorHierarchyChange(
             ConnectorHierarchyChangeEvent event) {
@@ -122,9 +156,4 @@ public class CustomFieldConnector extends AbstractFieldConnector
         }
     }
 
-    @Override
-    public CustomFieldState getState() {
-        return (CustomFieldState) super.getState();
-    }
-
 }
diff --git a/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VCustomField.java b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VCustomField.java
new file mode 100644 (file)
index 0000000..f826e5d
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2000-2016 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.v7.client.ui;
+
+import com.vaadin.client.Focusable;
+
+@Deprecated
+public class VCustomField extends VCustomComponent implements Focusable {
+
+    private Focusable focusDelegate;
+
+    @Override
+    public void focus() {
+        if (focusDelegate != null) {
+            focusDelegate.focus();
+        }
+    }
+
+    /**
+     * Sets the focusable widget to focus instead of this custom field.
+     *
+     * @param focusDelegate
+     *            the widget to delegate focus to
+     */
+    public void setFocusDelegate(Focusable focusDelegate) {
+        this.focusDelegate = focusDelegate;
+
+    }
+
+    /**
+     * Sets the focusable widget to focus instead of this custom field.
+     *
+     * @param focusDelegate
+     *            the widget to delegate focus to
+     */
+    public void setFocusDelegate(
+            final com.google.gwt.user.client.ui.Focusable focusDelegate) {
+        this.focusDelegate = new Focusable() {
+            @Override
+            public void focus() {
+                focusDelegate.setFocus(true);
+            }
+        };
+
+    }
+
+}
index 2d26c7c040b443b02864b000b83290b65f24fc62..f72672b43d04300ae13396a3e7380e4d8b9e1ddf 100644 (file)
@@ -17,16 +17,19 @@ package com.vaadin.v7.client.ui.customfield;
 
 import java.util.Collections;
 import java.util.List;
+import java.util.logging.Logger;
 
 import com.google.gwt.event.shared.HandlerRegistration;
 import com.google.gwt.user.client.ui.Widget;
 import com.vaadin.client.ComponentConnector;
 import com.vaadin.client.ConnectorHierarchyChangeEvent;
 import com.vaadin.client.ConnectorHierarchyChangeEvent.ConnectorHierarchyChangeHandler;
+import com.vaadin.client.Focusable;
 import com.vaadin.client.HasComponentsConnector;
+import com.vaadin.client.communication.StateChangeEvent;
 import com.vaadin.shared.ui.Connect;
 import com.vaadin.v7.client.ui.AbstractFieldConnector;
-import com.vaadin.v7.client.ui.VCustomComponent;
+import com.vaadin.v7.client.ui.VCustomField;
 import com.vaadin.v7.ui.CustomField;
 
 @Connect(value = CustomField.class)
@@ -43,8 +46,8 @@ public class CustomFieldConnector extends AbstractFieldConnector
     }
 
     @Override
-    public VCustomComponent getWidget() {
-        return (VCustomComponent) super.getWidget();
+    public VCustomField getWidget() {
+        return (VCustomField) super.getWidget();
     }
 
     @Override
@@ -52,6 +55,32 @@ public class CustomFieldConnector extends AbstractFieldConnector
         // NOP, custom field does not render the caption of its content
     }
 
+    @Override
+    public void onStateChanged(StateChangeEvent stateChangeEvent) {
+        super.onStateChanged(stateChangeEvent);
+        if (getState().focusDelegate != null) {
+            Widget widget = ((ComponentConnector) getState().focusDelegate)
+                    .getWidget();
+            if (widget instanceof Focusable) {
+                getWidget().setFocusDelegate((Focusable) widget);
+            } else if (widget instanceof com.google.gwt.user.client.ui.Focusable) {
+                getWidget().setFocusDelegate(
+                        (com.google.gwt.user.client.ui.Focusable) widget);
+            } else {
+                getLogger().warning(
+                        "The given focus delegate does not implement Focusable: "
+                                + widget.getClass().getName());
+            }
+        } else {
+            getWidget().setFocusDelegate((Focusable) null);
+        }
+
+    }
+
+    private static Logger getLogger() {
+        return Logger.getLogger(CustomFieldConnector.class.getName());
+    }
+
     @Override
     public void onConnectorHierarchyChange(
             ConnectorHierarchyChangeEvent event) {
index 989bef22ec7db7aced947645654d418e681b09a4..c2e6c3f40fc7c9043a2ae9c1c77a57703b5f7f41 100644 (file)
@@ -130,7 +130,7 @@ public abstract class CustomField<T> extends AbstractField<T>
 
     private class ComponentIterator
             implements Iterator<Component>, Serializable {
-        boolean first = (root != null);
+        boolean first = root != null;
 
         @Override
         public boolean hasNext() {
@@ -153,4 +153,53 @@ public abstract class CustomField<T> extends AbstractField<T>
     public Iterator<Component> iterator() {
         return new ComponentIterator();
     }
+
+    /**
+     * Sets the component to which all methods from the {@link Focusable}
+     * interface should be delegated.
+     * <p>
+     * Set this to a wrapped field to include that field in the tabbing order,
+     * to make it receive focus when {@link #focus()} is called and to make it
+     * be correctly focused when used as a Grid editor component.
+     * <p>
+     * By default, {@link Focusable} events are handled by the super class and
+     * ultimately ignored.
+     *
+     * @param focusDelegate
+     *            the focusable component to which focus events are redirected
+     */
+    public void setFocusDelegate(Focusable focusDelegate) {
+        getState().focusDelegate = focusDelegate;
+    }
+
+    private Focusable getFocusable() {
+        return (Focusable) getState(false).focusDelegate;
+    }
+
+    @Override
+    public void focus() {
+        if (getFocusable() != null) {
+            getFocusable().focus();
+        } else {
+            super.focus();
+        }
+    }
+
+    @Override
+    public int getTabIndex() {
+        if (getFocusable() != null) {
+            return getFocusable().getTabIndex();
+        } else {
+            return super.getTabIndex();
+        }
+    }
+
+    @Override
+    public void setTabIndex(int tabIndex) {
+        if (getFocusable() != null) {
+            getFocusable().setTabIndex(tabIndex);
+        } else {
+            super.setTabIndex(tabIndex);
+        }
+    }
 }
index 36e65acabedfb895caada7f5b382b65e473e8f08..8f69abc302e185fe614e4da0c24b375f3bcfcf35 100644 (file)
@@ -15,6 +15,7 @@
  */
 package com.vaadin.v7.shared;
 
+import com.vaadin.shared.Connector;
 import com.vaadin.shared.annotations.NoLayout;
 
 /**
@@ -34,4 +35,13 @@ public class AbstractFieldState extends AbstractLegacyComponentState {
      */
     @NoLayout
     public int tabIndex = 0;
+
+    /**
+     * The component which should receive focus events instead of the custom
+     * field wrapper.
+     * <p>
+     * This is not used in all fields, but needs to be here for the time being
+     * (#20468).
+     */
+    public Connector focusDelegate;
 }
index 397346a8cbd588821bc604d40d50d28e034cab5d..8738bad2dc8ac8ca6fe3696f9094a124f0cb8e6f 100644 (file)
@@ -155,4 +155,54 @@ public abstract class CustomField<T> extends AbstractField<T>
     public Iterator<Component> iterator() {
         return new ComponentIterator();
     }
+
+    /**
+     * Sets the component to which all methods from the {@link Focusable}
+     * interface should be delegated.
+     * <p>
+     * Set this to a wrapped field to include that field in the tabbing order,
+     * to make it receive focus when {@link #focus()} is called and to make it
+     * be correctly focused when used as a Grid editor component.
+     * <p>
+     * By default, {@link Focusable} events are handled by the super class and
+     * ultimately ignored.
+     * 
+     * @param focusDelegate
+     *            the focusable component to which focus events are redirected
+     */
+    public void setFocusDelegate(Focusable focusDelegate) {
+        getState().focusDelegate = focusDelegate;
+    }
+
+    private Focusable getFocusable() {
+        return (Focusable) getState(false).focusDelegate;
+    }
+
+    @Override
+    public void focus() {
+        if (getFocusable() != null) {
+            getFocusable().focus();
+        } else {
+            super.focus();
+        }
+    }
+
+    @Override
+    public int getTabIndex() {
+        if (getFocusable() != null) {
+            return getFocusable().getTabIndex();
+        } else {
+            return super.getTabIndex();
+        }
+    }
+
+    @Override
+    public void setTabIndex(int tabIndex) {
+        if (getFocusable() != null) {
+            getFocusable().setTabIndex(tabIndex);
+        } else {
+            super.setTabIndex(tabIndex);
+        }
+    }
+
 }
index 467a5ebe994f1fc6a42cde57d48d94d993ca38e1..2884f77d6b43740dd9fddfa63152c2d5c1e3f213 100644 (file)
 package com.vaadin.shared.ui.customfield;
 
 import com.vaadin.shared.AbstractFieldState;
+import com.vaadin.shared.Connector;
 
 /**
  * State class for CustomField.
- * 
+ *
  * @author Vaadin Ltd
  * @since 8.0
  *
  */
 public class CustomFieldState extends AbstractFieldState {
 
+    /**
+     * The component which should receive focus events instead of the custom
+     * field wrapper.
+     */
+    public Connector focusDelegate;
+
 }
diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorCustomField.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorCustomField.java
new file mode 100644 (file)
index 0000000..409147f
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2000-2016 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.grid;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.vaadin.annotations.Theme;
+import com.vaadin.data.Binder;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.server.data.ListDataProvider;
+import com.vaadin.server.data.Query;
+import com.vaadin.tests.components.AbstractTestUIWithLog;
+import com.vaadin.tests.fieldgroup.ComplexPerson;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.ComboBox;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.CustomField;
+import com.vaadin.ui.Grid;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.TextField;
+
+@Theme("tests-valo-disabled-animations")
+public class GridEditorCustomField extends AbstractTestUIWithLog {
+
+    private static final String LAST_NAME_IDENTIFIER = "lastName";
+    private static final String FIRST_NAME_IDENTIFIER = "firstName";
+    private static final String ADDRESS_CITY_IDENTIFIER = "address.city";
+
+    @Override
+    protected void setup(VaadinRequest request) {
+        Grid<ComplexPerson> grid = createGrid();
+
+        ListDataProvider<ComplexPerson> dataProvider = ComplexPerson
+                .createDataProvider(100);
+
+        grid.setDataProvider(dataProvider);
+
+        Set<String> cities = new HashSet<>();
+        dataProvider.fetch(new Query<>()).forEach(person -> {
+            cities.add(person.getAddress().getCity());
+        });
+        CustomCitySelect cityEditor = new CustomCitySelect(
+                cities.toArray(new String[cities.size()]));
+
+        TextField firstNameField = new TextField();
+        TextField lastNameField = new TextField();
+        Binder<ComplexPerson> binder = new Binder<>();
+
+        binder.bind(firstNameField, ComplexPerson::getFirstName,
+                ComplexPerson::setFirstName);
+        binder.bind(lastNameField, ComplexPerson::getLastName,
+                ComplexPerson::setLastName);
+        binder.bind(cityEditor, person -> person.getAddress().getCity(),
+                (person, city) -> person.getAddress().setCity(city));
+
+        grid.getEditor().setBinder(binder);
+        grid.getColumn(ADDRESS_CITY_IDENTIFIER).setEditorComponent(cityEditor);
+        grid.getColumn(FIRST_NAME_IDENTIFIER)
+                .setEditorComponent(firstNameField);
+        grid.getColumn(LAST_NAME_IDENTIFIER).setEditorComponent(lastNameField);
+
+        addComponent(grid);
+    }
+
+    private Grid<ComplexPerson> createGrid() {
+        Grid<ComplexPerson> grid = new Grid<>();
+        grid.setWidth("800px");
+        grid.addColumn(FIRST_NAME_IDENTIFIER, person -> person.getFirstName())
+                .setCaption("First Name");
+        grid.addColumn(LAST_NAME_IDENTIFIER, person -> person.getLastName())
+                .setCaption("Last Name");
+        grid.addColumn(ADDRESS_CITY_IDENTIFIER,
+                person -> person.getAddress().getCity())
+                .setCaption("City Name");
+        grid.getEditor().setEnabled(true);
+
+        return grid;
+    }
+
+    public static class CustomCitySelect extends CustomField<String> {
+        private HorizontalLayout fieldLayout;
+        private String[] values;
+        private ComboBox<String> cityComboBox;
+        private String cachedValue;
+
+        public CustomCitySelect(String... values) {
+            this.values = values;
+        }
+
+        @Override
+        protected Component initContent() {
+            fieldLayout = new HorizontalLayout();
+            fieldLayout.setWidth("100%");
+
+            cityComboBox = new ComboBox<>();
+            cityComboBox.setItems(values);
+            if (cachedValue != null) {
+                cityComboBox.setValue(cachedValue);
+                cachedValue = null;
+            }
+
+            fieldLayout.addComponent(cityComboBox);
+            fieldLayout.setExpandRatio(cityComboBox, 1.0f);
+
+            Button addCountryButton = new Button("New");
+            fieldLayout.addComponent(addCountryButton);
+
+            setFocusDelegate(cityComboBox);
+
+            return fieldLayout;
+        }
+
+        @Override
+        public String getValue() {
+            if (cityComboBox == null) {
+                return null;
+            }
+            return cityComboBox.getValue();
+        }
+
+        @Override
+        protected void doSetValue(String value) {
+            if (cityComboBox == null) {
+                getContent();
+            }
+            cityComboBox.setValue(value);
+        }
+    }
+
+}
index c2aeb51bab3a1ca9eabd047764777c9e62934058..8e355484b91152544de5a5ce18960cc3900a46fd 100644 (file)
@@ -1,9 +1,13 @@
 package com.vaadin.tests.fieldgroup;
 
 import java.math.BigDecimal;
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.List;
 import java.util.Random;
 
+import com.vaadin.server.data.DataProvider;
+import com.vaadin.server.data.ListDataProvider;
 import com.vaadin.tests.util.TestDataGenerator;
 import com.vaadin.v7.data.util.BeanItemContainer;
 
@@ -25,6 +29,10 @@ public class ComplexPerson {
         this.firstName = firstName;
     }
 
+    public void setLastName(String firstName) {
+        this.firstName = firstName;
+    }
+
     public String getLastName() {
         return lastName;
     }
@@ -90,6 +98,18 @@ public class ComplexPerson {
         return bic;
     }
 
+    public static ListDataProvider<ComplexPerson> createDataProvider(int size) {
+        List<ComplexPerson> list = new ArrayList<>();
+        Random r = new Random(size);
+
+        for (int i = 0; i < size; i++) {
+            ComplexPerson cp = ComplexPerson.create(r);
+            list.add(cp);
+        }
+
+        return DataProvider.create(list);
+    }
+
     public static ComplexPerson create(Random r) {
         ComplexPerson cp = new ComplexPerson();
         cp.setFirstName(TestDataGenerator.getFirstName(r));
diff --git a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridEditorCustomField.java b/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridEditorCustomField.java
deleted file mode 100644 (file)
index 22efd2b..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2000-2016 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.v7.tests.components.grid;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import com.vaadin.server.VaadinRequest;
-import com.vaadin.tests.components.AbstractTestUIWithLog;
-import com.vaadin.tests.fieldgroup.ComplexPerson;
-import com.vaadin.ui.Button;
-import com.vaadin.ui.Component;
-import com.vaadin.ui.HorizontalLayout;
-import com.vaadin.v7.ui.ComboBox;
-import com.vaadin.v7.ui.CustomField;
-import com.vaadin.v7.ui.Grid;
-
-public class GridEditorCustomField extends AbstractTestUIWithLog {
-
-    @Override
-    protected void setup(VaadinRequest request) {
-        Grid grid = new PersonTestGrid(100);
-        grid.setWidth("800px");
-        grid.setColumns("firstName", "lastName", "address.city");
-        grid.setEditorEnabled(true);
-        Set<String> cities = new HashSet<>();
-        for (Object o : grid.getContainerDataSource().getItemIds()) {
-            ComplexPerson p = (ComplexPerson) o;
-            cities.add(p.getAddress().getCity());
-        }
-        CustomCitySelect cityEditor = new CustomCitySelect(
-                cities.toArray(new String[cities.size()]));
-        grid.getColumn("address.city").setEditorField(cityEditor);
-        addComponent(grid);
-    }
-
-    public static class CustomCitySelect extends CustomField<String> {
-        private HorizontalLayout fieldLayout;
-        private String[] values;
-        private ComboBox cityComboBox;
-
-        public CustomCitySelect(String... values) {
-            this.values = values;
-        }
-
-        @Override
-        protected Component initContent() {
-            fieldLayout = new HorizontalLayout();
-            fieldLayout.setWidth("100%");
-
-            cityComboBox = new ComboBox();
-            for (String value : values) {
-                cityComboBox.addItem(value);
-            }
-            fieldLayout.addComponent(cityComboBox);
-            fieldLayout.setExpandRatio(cityComboBox, 1.0f);
-
-            Button addCountryButton = new Button("New");
-            fieldLayout.addComponent(addCountryButton);
-
-            return fieldLayout;
-        }
-
-        @Override
-        public Class<String> getType() {
-            return String.class;
-        }
-
-        @Override
-        protected void setInternalValue(String newValue) {
-            super.setInternalValue(newValue);
-            if (cityComboBox == null) {
-                return;
-            }
-            cityComboBox.setValue(newValue);
-        }
-
-        @Override
-        public String getInternalValue() {
-            if (cityComboBox == null) {
-                return null;
-            }
-            return (String) cityComboBox.getValue();
-        }
-    }
-
-}
diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/GridEditorCustomFieldTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/GridEditorCustomFieldTest.java
new file mode 100644 (file)
index 0000000..77d54dd
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2016 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.grid;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.openqa.selenium.Keys;
+
+import com.vaadin.testbench.TestBenchElement;
+import com.vaadin.testbench.customelements.ComboBoxElement;
+import com.vaadin.testbench.customelements.GridElement;
+import com.vaadin.testbench.elements.GridElement.GridEditorElement;
+import com.vaadin.testbench.parallel.TestCategory;
+import com.vaadin.tests.tb3.MultiBrowserTest;
+
+@TestCategory("grid")
+public class GridEditorCustomFieldTest extends MultiBrowserTest {
+
+    @Test
+    public void testCustomFieldWorksInEditorRow() {
+        openTestURL();
+        GridElement grid = $(GridElement.class).first();
+        Assert.assertEquals("Stockholm", grid.getCell(0, 2).getText());
+        grid.getCell(0, 1).doubleClick();
+        GridEditorElement editor = grid.getEditor();
+        TestBenchElement customField = editor.getField(2);
+
+        ComboBoxElement comboBox = customField.$(ComboBoxElement.class).first();
+        comboBox.selectByText("Oslo");
+        editor.save();
+        Assert.assertEquals("Oslo", grid.getCell(0, 2).getText());
+
+    }
+
+    @Test
+    public void tabReachesCustomField() {
+        openTestURL();
+        GridElement grid = $(GridElement.class).first();
+        grid.getCell(0, 1).doubleClick();
+        GridEditorElement editor = grid.getEditor();
+        editor.getField(0).sendKeys(Keys.TAB, Keys.TAB);
+
+        ComboBoxElement comboBoxInCustomField = editor.getField(2)
+                .$(ComboBoxElement.class).first();
+        assertElementsEquals(comboBoxInCustomField.getInputField(),
+                getActiveElement());
+    }
+}
diff --git a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridEditorCustomFieldTest.java b/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridEditorCustomFieldTest.java
deleted file mode 100644 (file)
index f7b56f5..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2000-2016 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.v7.tests.components.grid;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-import com.vaadin.testbench.TestBenchElement;
-import com.vaadin.testbench.customelements.ComboBoxElement;
-import com.vaadin.testbench.elements.GridElement.GridEditorElement;
-import com.vaadin.testbench.parallel.TestCategory;
-import com.vaadin.tests.tb3.MultiBrowserTest;
-import com.vaadin.testbench.customelements.GridElement;
-
-@TestCategory("grid")
-public class GridEditorCustomFieldTest extends MultiBrowserTest {
-
-    @Test
-    public void testCustomFieldWorksInEditorRow() {
-        openTestURL();
-        GridElement grid = $(GridElement.class).first();
-        Assert.assertEquals("Stockholm", grid.getCell(0, 2).getText());
-        grid.getCell(0, 1).doubleClick();
-        GridEditorElement editor = grid.getEditor();
-        TestBenchElement customField = editor.getField(2);
-
-        ComboBoxElement comboBox = customField.$(ComboBoxElement.class).first();
-        comboBox.selectByText("Oslo");
-        editor.save();
-        Assert.assertEquals("Oslo", grid.getCell(0, 2).getText());
-
-    }
-}