From 04f30c6892c8ddf8794ed8561fa6f00beefeec28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pekka=20Hyv=C3=B6nen?= Date: Thu, 15 Dec 2016 13:46:05 +0200 Subject: [PATCH] Allow defining a focus delegate component for CustomField (#20336) --- .../com/vaadin/client/ui/VCustomField.java | 59 +++++++ .../ui/customfield/CustomFieldConnector.java | 45 +++++- .../com/vaadin/v7/client/ui/VCustomField.java | 60 ++++++++ .../ui/customfield/CustomFieldConnector.java | 35 ++++- .../java/com/vaadin/v7/ui/CustomField.java | 51 ++++++- .../vaadin/v7/shared/AbstractFieldState.java | 10 ++ .../main/java/com/vaadin/ui/CustomField.java | 50 ++++++ .../ui/customfield/CustomFieldState.java | 9 +- .../grid/GridEditorCustomField.java | 144 ++++++++++++++++++ .../tests/fieldgroup/ComplexPerson.java | 20 +++ .../grid/GridEditorCustomField.java | 100 ------------ .../grid/GridEditorCustomFieldTest.java | 19 ++- 12 files changed, 487 insertions(+), 115 deletions(-) create mode 100644 client/src/main/java/com/vaadin/client/ui/VCustomField.java create mode 100644 compatibility-client/src/main/java/com/vaadin/v7/client/ui/VCustomField.java create mode 100644 uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorCustomField.java delete mode 100644 uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridEditorCustomField.java rename uitest/src/test/java/com/vaadin/{v7 => }/tests/components/grid/GridEditorCustomFieldTest.java (73%) 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 index 00000000000..2d5039fcd8d --- /dev/null +++ b/client/src/main/java/com/vaadin/client/ui/VCustomField.java @@ -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); + } + }; + + } + +} diff --git a/client/src/main/java/com/vaadin/client/ui/customfield/CustomFieldConnector.java b/client/src/main/java/com/vaadin/client/ui/customfield/CustomFieldConnector.java index c058522c4e5..47c9da6732d 100644 --- a/client/src/main/java/com/vaadin/client/ui/customfield/CustomFieldConnector.java +++ b/client/src/main/java/com/vaadin/client/ui/customfield/CustomFieldConnector.java @@ -17,15 +17,18 @@ 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 CustomFieldConnector() { } @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 void updateCaption(ComponentConnector connector) { // 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 @@ protected Widget getContentWidget() { } } - @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 index 00000000000..f826e5d6209 --- /dev/null +++ b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VCustomField.java @@ -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); + } + }; + + } + +} diff --git a/compatibility-client/src/main/java/com/vaadin/v7/client/ui/customfield/CustomFieldConnector.java b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/customfield/CustomFieldConnector.java index 2d26c7c040b..f72672b43d0 100644 --- a/compatibility-client/src/main/java/com/vaadin/v7/client/ui/customfield/CustomFieldConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/customfield/CustomFieldConnector.java @@ -17,16 +17,19 @@ 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 CustomFieldConnector() { } @Override - public VCustomComponent getWidget() { - return (VCustomComponent) super.getWidget(); + public VCustomField getWidget() { + return (VCustomField) super.getWidget(); } @Override @@ -52,6 +55,32 @@ public void updateCaption(ComponentConnector connector) { // 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) { diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/CustomField.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/CustomField.java index 989bef22ec7..c2e6c3f40fc 100644 --- a/compatibility-server/src/main/java/com/vaadin/v7/ui/CustomField.java +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/CustomField.java @@ -130,7 +130,7 @@ public void setWidth(float width, Unit unit) { private class ComponentIterator implements Iterator, Serializable { - boolean first = (root != null); + boolean first = root != null; @Override public boolean hasNext() { @@ -153,4 +153,53 @@ public void remove() { public Iterator iterator() { return new ComponentIterator(); } + + /** + * Sets the component to which all methods from the {@link Focusable} + * interface should be delegated. + *

+ * 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. + *

+ * 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); + } + } } diff --git a/compatibility-shared/src/main/java/com/vaadin/v7/shared/AbstractFieldState.java b/compatibility-shared/src/main/java/com/vaadin/v7/shared/AbstractFieldState.java index 36e65acabed..8f69abc302e 100644 --- a/compatibility-shared/src/main/java/com/vaadin/v7/shared/AbstractFieldState.java +++ b/compatibility-shared/src/main/java/com/vaadin/v7/shared/AbstractFieldState.java @@ -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. + *

+ * This is not used in all fields, but needs to be here for the time being + * (#20468). + */ + public Connector focusDelegate; } diff --git a/server/src/main/java/com/vaadin/ui/CustomField.java b/server/src/main/java/com/vaadin/ui/CustomField.java index 397346a8cbd..8738bad2dc8 100644 --- a/server/src/main/java/com/vaadin/ui/CustomField.java +++ b/server/src/main/java/com/vaadin/ui/CustomField.java @@ -155,4 +155,54 @@ public void remove() { public Iterator iterator() { return new ComponentIterator(); } + + /** + * Sets the component to which all methods from the {@link Focusable} + * interface should be delegated. + *

+ * 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. + *

+ * 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); + } + } + } diff --git a/shared/src/main/java/com/vaadin/shared/ui/customfield/CustomFieldState.java b/shared/src/main/java/com/vaadin/shared/ui/customfield/CustomFieldState.java index 467a5ebe994..2884f77d6b4 100644 --- a/shared/src/main/java/com/vaadin/shared/ui/customfield/CustomFieldState.java +++ b/shared/src/main/java/com/vaadin/shared/ui/customfield/CustomFieldState.java @@ -16,14 +16,21 @@ 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 index 00000000000..409147ff9b2 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorCustomField.java @@ -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 grid = createGrid(); + + ListDataProvider dataProvider = ComplexPerson + .createDataProvider(100); + + grid.setDataProvider(dataProvider); + + Set 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 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 createGrid() { + Grid 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 { + private HorizontalLayout fieldLayout; + private String[] values; + private ComboBox 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); + } + } + +} diff --git a/uitest/src/main/java/com/vaadin/tests/fieldgroup/ComplexPerson.java b/uitest/src/main/java/com/vaadin/tests/fieldgroup/ComplexPerson.java index c2aeb51bab3..8e355484b91 100644 --- a/uitest/src/main/java/com/vaadin/tests/fieldgroup/ComplexPerson.java +++ b/uitest/src/main/java/com/vaadin/tests/fieldgroup/ComplexPerson.java @@ -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 void setFirstName(String firstName) { this.firstName = firstName; } + public void setLastName(String firstName) { + this.firstName = firstName; + } + public String getLastName() { return lastName; } @@ -90,6 +98,18 @@ public static BeanItemContainer createContainer(int size) { return bic; } + public static ListDataProvider createDataProvider(int size) { + List 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 index 22efd2b6c66..00000000000 --- a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridEditorCustomField.java +++ /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 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 { - 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 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/v7/tests/components/grid/GridEditorCustomFieldTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/GridEditorCustomFieldTest.java similarity index 73% rename from uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridEditorCustomFieldTest.java rename to uitest/src/test/java/com/vaadin/tests/components/grid/GridEditorCustomFieldTest.java index f7b56f53d6d..77d54dd93b7 100644 --- a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridEditorCustomFieldTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/GridEditorCustomFieldTest.java @@ -13,17 +13,18 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.v7.tests.components.grid; +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; -import com.vaadin.testbench.customelements.GridElement; @TestCategory("grid") public class GridEditorCustomFieldTest extends MultiBrowserTest { @@ -43,4 +44,18 @@ public void testCustomFieldWorksInEditorRow() { 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()); + } }