From 287614dbabc4ec8f2e72f56067312ad47ce46f5d Mon Sep 17 00:00:00 2001 From: Denis Anisimov Date: Sun, 30 Oct 2016 18:11:21 +0200 Subject: [PATCH] Implement focus/blur events for RadioButtonGroup. Fixes vaadin/framework8-issues#333 Change-Id: I55f5d6a0cd690f2c0b5e757318a5f528a67ef34e --- .../com/vaadin/client/ui/VCheckBoxGroup.java | 13 +- .../vaadin/client/ui/VRadioButtonGroup.java | 125 +++++++++--------- .../RadioButtonGroupConnector.java | 23 ++-- .../widgets/FocusableFlowPanelComposite.java | 8 +- .../java/com/vaadin/ui/RadioButtonGroup.java | 40 +++++- .../AbstractListingFocusBlurTest.java | 68 ++++++++++ .../checkboxgroup/CheckBoxGroupFocusBlur.java | 25 ++-- .../nativeselect/NativeSelectFocusBlur.java | 26 ++-- .../RadioButtonGroupFocusBlur.java | 34 +++++ .../RadioButtonGroupFocusBlurTest.java | 86 ++++++++++++ 10 files changed, 327 insertions(+), 121 deletions(-) create mode 100644 uitest/src/main/java/com/vaadin/tests/components/AbstractListingFocusBlurTest.java create mode 100644 uitest/src/main/java/com/vaadin/tests/components/radiobuttongroup/RadioButtonGroupFocusBlur.java create mode 100644 uitest/src/test/java/com/vaadin/tests/components/radiobuttongroup/RadioButtonGroupFocusBlurTest.java diff --git a/client/src/main/java/com/vaadin/client/ui/VCheckBoxGroup.java b/client/src/main/java/com/vaadin/client/ui/VCheckBoxGroup.java index 6ba7ea75d5..d6b8c3ade9 100644 --- a/client/src/main/java/com/vaadin/client/ui/VCheckBoxGroup.java +++ b/client/src/main/java/com/vaadin/client/ui/VCheckBoxGroup.java @@ -42,8 +42,8 @@ import elemental.json.JsonObject; * @author Vaadin Ltd. * @since 8.0 */ -public class VCheckBoxGroup extends FocusableFlowPanelComposite implements - Field, ClickHandler, com.vaadin.client.Focusable, HasEnabled { +public class VCheckBoxGroup extends FocusableFlowPanelComposite + implements Field, ClickHandler, HasEnabled { public static final String CLASSNAME = "v-select-optiongroup"; public static final String CLASSNAME_OPTION = "v-select-option"; @@ -99,7 +99,7 @@ public class VCheckBoxGroup extends FocusableFlowPanelComposite implements } private void updateItem(VCheckBox widget, JsonObject item, - boolean requireInitializations) { + boolean requireInitialization) { String itemHtml = item .getString(ListingJsonConstants.JSONKEY_ITEM_VALUE); if (!isHtmlContentAllowed()) { @@ -117,7 +117,7 @@ public class VCheckBoxGroup extends FocusableFlowPanelComposite implements item.getBoolean(ListingJsonConstants.JSONKEY_ITEM_SELECTED)); setOptionEnabled(widget, item); - if (requireInitializations) { + if (requireInitialization) { widget.addStyleName(CLASSNAME_OPTION); widget.addClickHandler(this); getWidget().add(widget); @@ -168,11 +168,6 @@ public class VCheckBoxGroup extends FocusableFlowPanelComposite implements checkBox.setEnabled(enabled); } - @Override - public void focus() { - getWidget().focus(); - } - public boolean isHtmlContentAllowed() { return htmlContentAllowed; } diff --git a/client/src/main/java/com/vaadin/client/ui/VRadioButtonGroup.java b/client/src/main/java/com/vaadin/client/ui/VRadioButtonGroup.java index fd4d7cf98a..fe5afea620 100644 --- a/client/src/main/java/com/vaadin/client/ui/VRadioButtonGroup.java +++ b/client/src/main/java/com/vaadin/client/ui/VRadioButtonGroup.java @@ -18,7 +18,6 @@ package com.vaadin.client.ui; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -27,17 +26,14 @@ import com.google.gwt.aria.client.Roles; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.FocusWidget; -import com.google.gwt.user.client.ui.Focusable; import com.google.gwt.user.client.ui.HasEnabled; -import com.google.gwt.user.client.ui.Panel; import com.google.gwt.user.client.ui.RadioButton; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.BrowserInfo; import com.vaadin.client.WidgetUtil; +import com.vaadin.client.widgets.FocusableFlowPanelComposite; import com.vaadin.shared.Registration; import com.vaadin.shared.data.DataCommunicatorConstants; import com.vaadin.shared.ui.ListingJsonConstants; @@ -50,8 +46,8 @@ import elemental.json.JsonObject; * @author Vaadin Ltd. * @since 8.0 */ -public class VRadioButtonGroup extends Composite implements Field, ClickHandler, - com.vaadin.client.Focusable, HasEnabled { +public class VRadioButtonGroup extends FocusableFlowPanelComposite + implements Field, ClickHandler, HasEnabled { public static final String CLASSNAME = "v-select-optiongroup"; public static final String CLASSNAME_OPTION = "v-select-option"; @@ -64,14 +60,6 @@ public class VRadioButtonGroup extends Composite implements Field, ClickHandler, */ public ApplicationConnection client; - /** - * Widget holding the different options (e.g. ListBox or Panel for radio - * buttons) (optional, fallbacks to container Panel) - *

- * For internal use only. May be removed or replaced in the future. - */ - public Panel optionsContainer; - private boolean htmlContentAllowed = false; private boolean enabled; @@ -81,9 +69,7 @@ public class VRadioButtonGroup extends Composite implements Field, ClickHandler, public VRadioButtonGroup() { groupId = DOM.createUniqueId(); - optionsContainer = new FlowPanel(); - initWidget(optionsContainer); - optionsContainer.setStyleName(CLASSNAME); + getWidget().setStyleName(CLASSNAME); optionsToItems = new HashMap<>(); keyToOptions = new HashMap<>(); selectionChangeListeners = new ArrayList<>(); @@ -93,47 +79,68 @@ public class VRadioButtonGroup extends Composite implements Field, ClickHandler, * Build all the options */ public void buildOptions(List items) { - /* - * In order to retain focus, we need to update values rather than - * recreate panel from scratch (#10451). However, the panel will be - * rebuilt (losing focus) if number of elements or their order is - * changed. - */ - Roles.getRadiogroupRole().set(getElement()); - optionsContainer.clear(); - optionsToItems.clear(); - keyToOptions.clear(); - for (JsonObject item : items) { - String itemHtml = item - .getString(ListingJsonConstants.JSONKEY_ITEM_VALUE); - if (!isHtmlContentAllowed()) { - itemHtml = WidgetUtil.escapeHTML(itemHtml); + int i = 0; + int widgetsToRemove = getWidget().getWidgetCount() - items.size(); + if (widgetsToRemove < 0) { + widgetsToRemove = 0; + } + List remove = new ArrayList<>(widgetsToRemove); + for (Widget widget : getWidget()) { + if (i < items.size()) { + updateItem((RadioButton) widget, items.get(i), false); + i++; + } else { + remove.add(widget); } - RadioButton radioButton = new RadioButton(groupId); + } + remove.stream().forEach(this::remove); + while (i < items.size()) { + updateItem(new RadioButton(groupId), items.get(i), true); + i++; + } + } - String iconUrl = item - .getString(ListingJsonConstants.JSONKEY_ITEM_ICON); - if (iconUrl != null && iconUrl.length() != 0) { - Icon icon = client.getIcon(iconUrl); - itemHtml = icon.getElement().getString() + itemHtml; - } - radioButton.setStyleName("v-radiobutton"); - radioButton.addStyleName(CLASSNAME_OPTION); - radioButton.addClickHandler(this); - radioButton.setHTML(itemHtml); - radioButton.setValue(item - .getBoolean(ListingJsonConstants.JSONKEY_ITEM_SELECTED)); - boolean optionEnabled = !item - .getBoolean(ListingJsonConstants.JSONKEY_ITEM_DISABLED); - boolean enabled = optionEnabled && !isReadonly() && isEnabled(); - radioButton.setEnabled(enabled); + private void remove(Widget widget) { + getWidget().remove(widget); + JsonObject item = optionsToItems.remove(widget); + if (item != null) { String key = item.getString(DataCommunicatorConstants.KEY); + keyToOptions.remove(key); + } + } + + private void updateItem(RadioButton button, JsonObject item, + boolean requireInitialization) { + String itemHtml = item + .getString(ListingJsonConstants.JSONKEY_ITEM_VALUE); + if (!isHtmlContentAllowed()) { + itemHtml = WidgetUtil.escapeHTML(itemHtml); + } + + String iconUrl = item.getString(ListingJsonConstants.JSONKEY_ITEM_ICON); + if (iconUrl != null && iconUrl.length() != 0) { + Icon icon = client.getIcon(iconUrl); + itemHtml = icon.getElement().getString() + itemHtml; + } - optionsContainer.add(radioButton); - optionsToItems.put(radioButton, item); - keyToOptions.put(key, radioButton); + button.setHTML(itemHtml); + button.setValue( + item.getBoolean(ListingJsonConstants.JSONKEY_ITEM_SELECTED)); + boolean optionEnabled = !item + .getBoolean(ListingJsonConstants.JSONKEY_ITEM_DISABLED); + boolean enabled = optionEnabled && !isReadonly() && isEnabled(); + button.setEnabled(enabled); + String key = item.getString(DataCommunicatorConstants.KEY); + + if (requireInitialization) { + getWidget().add(button); + button.setStyleName("v-radiobutton"); + button.addStyleName(CLASSNAME_OPTION); + button.addClickHandler(this); } + optionsToItems.put(button, item); + keyToOptions.put(key, button); } @Override @@ -160,7 +167,7 @@ public class VRadioButtonGroup extends Composite implements Field, ClickHandler, } public void setTabIndex(int tabIndex) { - for (Widget anOptionsContainer : optionsContainer) { + for (Widget anOptionsContainer : getWidget()) { FocusWidget widget = (FocusWidget) anOptionsContainer; widget.setTabIndex(tabIndex); } @@ -180,14 +187,6 @@ public class VRadioButtonGroup extends Composite implements Field, ClickHandler, } } - @Override - public void focus() { - Iterator iterator = optionsContainer.iterator(); - if (iterator.hasNext()) { - ((Focusable) iterator.next()).setFocus(true); - } - } - public boolean isHtmlContentAllowed() { return htmlContentAllowed; } @@ -229,7 +228,7 @@ public class VRadioButtonGroup extends Composite implements Field, ClickHandler, public void selectItemKey(String selectedItemKey) { RadioButton radioButton = keyToOptions.get(selectedItemKey); - if(radioButton!=null) {//Items might not be loaded yet + if (radioButton != null) {// Items might not be loaded yet radioButton.setValue(true); } } diff --git a/client/src/main/java/com/vaadin/client/ui/optiongroup/RadioButtonGroupConnector.java b/client/src/main/java/com/vaadin/client/ui/optiongroup/RadioButtonGroupConnector.java index 135126a099..4f3cad3c4c 100644 --- a/client/src/main/java/com/vaadin/client/ui/optiongroup/RadioButtonGroupConnector.java +++ b/client/src/main/java/com/vaadin/client/ui/optiongroup/RadioButtonGroupConnector.java @@ -16,9 +16,12 @@ package com.vaadin.client.ui.optiongroup; +import java.util.ArrayList; +import java.util.List; + import com.vaadin.client.annotations.OnStateChange; import com.vaadin.client.communication.StateChangeEvent; -import com.vaadin.client.connectors.AbstractListingConnector; +import com.vaadin.client.connectors.AbstractFocusableListingConnector; import com.vaadin.client.data.DataSource; import com.vaadin.client.ui.VRadioButtonGroup; import com.vaadin.shared.Range; @@ -28,14 +31,12 @@ import com.vaadin.shared.data.selection.SelectionServerRpc; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.optiongroup.RadioButtonGroupState; import com.vaadin.ui.RadioButtonGroup; -import elemental.json.JsonObject; -import java.util.ArrayList; -import java.util.List; +import elemental.json.JsonObject; @Connect(RadioButtonGroup.class) -public class RadioButtonGroupConnector - extends AbstractListingConnector> { +public class RadioButtonGroupConnector extends + AbstractFocusableListingConnector> { private Registration selectionChangeRegistration; private Registration dataChangeRegistration; @@ -85,11 +86,6 @@ public class RadioButtonGroupConnector getWidget().selectItemKey(getState().selectedItemKey); } - @Override - public VRadioButtonGroup getWidget() { - return (VRadioButtonGroup) super.getWidget(); - } - @Override public RadioButtonGroupState getState() { return (RadioButtonGroupState) super.getState(); @@ -105,9 +101,8 @@ public class RadioButtonGroupConnector */ private void onDataChange(Range range) { assert range.getStart() == 0 && range.getEnd() == getDataSource() - .size() : "RadioButtonGroup only supports full updates, but " + - "got range " - + range; + .size() : "RadioButtonGroup only supports full updates, but " + + "got range " + range; final VRadioButtonGroup select = getWidget(); DataSource dataSource = getDataSource(); diff --git a/client/src/main/java/com/vaadin/client/widgets/FocusableFlowPanelComposite.java b/client/src/main/java/com/vaadin/client/widgets/FocusableFlowPanelComposite.java index 4a5d031c3f..7a24549664 100644 --- a/client/src/main/java/com/vaadin/client/widgets/FocusableFlowPanelComposite.java +++ b/client/src/main/java/com/vaadin/client/widgets/FocusableFlowPanelComposite.java @@ -20,6 +20,7 @@ import com.google.gwt.event.dom.client.FocusHandler; import com.google.gwt.event.dom.client.HasAllFocusHandlers; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.ui.Composite; +import com.vaadin.client.Focusable; /** * Focusable composite whose widget is {@link ChildFocusAwareFlowPanel} (flow @@ -29,7 +30,7 @@ import com.google.gwt.user.client.ui.Composite; * */ public abstract class FocusableFlowPanelComposite extends Composite - implements HasAllFocusHandlers { + implements HasAllFocusHandlers, Focusable { private final ChildFocusAwareFlowPanel panel; @@ -55,4 +56,9 @@ public abstract class FocusableFlowPanelComposite extends Composite public HandlerRegistration addBlurHandler(BlurHandler handler) { return panel.addBlurHandler(handler); } + + @Override + public void focus() { + getWidget().focus(); + } } diff --git a/server/src/main/java/com/vaadin/ui/RadioButtonGroup.java b/server/src/main/java/com/vaadin/ui/RadioButtonGroup.java index a1474c91d4..6f1191e8d4 100644 --- a/server/src/main/java/com/vaadin/ui/RadioButtonGroup.java +++ b/server/src/main/java/com/vaadin/ui/RadioButtonGroup.java @@ -20,11 +20,19 @@ import java.util.Collection; import java.util.Objects; import com.vaadin.data.Listing; +import com.vaadin.event.FieldEvents.BlurEvent; +import com.vaadin.event.FieldEvents.BlurListener; +import com.vaadin.event.FieldEvents.BlurNotifier; +import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcDecorator; +import com.vaadin.event.FieldEvents.FocusEvent; +import com.vaadin.event.FieldEvents.FocusListener; +import com.vaadin.event.FieldEvents.FocusNotifier; import com.vaadin.server.Resource; import com.vaadin.server.ResourceReference; import com.vaadin.server.SerializablePredicate; import com.vaadin.server.data.DataGenerator; import com.vaadin.server.data.DataSource; +import com.vaadin.shared.Registration; import com.vaadin.shared.ui.ListingJsonConstants; import com.vaadin.shared.ui.optiongroup.RadioButtonGroupState; @@ -39,7 +47,8 @@ import elemental.json.JsonObject; * @author Vaadin Ltd * @since 8.0 */ -public class RadioButtonGroup extends AbstractSingleSelect { +public class RadioButtonGroup extends AbstractSingleSelect + implements FocusNotifier, BlurNotifier { private IconGenerator itemIconGenerator = item -> null; @@ -93,6 +102,7 @@ public class RadioButtonGroup extends AbstractSingleSelect { * @see Listing#setDataSource(DataSource) */ public RadioButtonGroup() { + registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent)); setSelectionModel(new SimpleSingleSelection()); addDataGenerator(new DataGenerator() { @@ -245,4 +255,32 @@ public class RadioButtonGroup extends AbstractSingleSelect { Objects.requireNonNull(itemEnabledProvider); this.itemEnabledProvider = itemEnabledProvider; } + + @Override + public Registration addFocusListener(FocusListener listener) { + addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, + FocusListener.focusMethod); + return () -> removeListener(FocusEvent.EVENT_ID, FocusEvent.class, + listener); + } + + @Override + @Deprecated + public void removeFocusListener(FocusListener listener) { + removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener); + } + + @Override + public Registration addBlurListener(BlurListener listener) { + addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, + BlurListener.blurMethod); + return () -> removeListener(BlurEvent.EVENT_ID, BlurEvent.class, + listener); + } + + @Override + @Deprecated + public void removeBlurListener(BlurListener listener) { + removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener); + } } diff --git a/uitest/src/main/java/com/vaadin/tests/components/AbstractListingFocusBlurTest.java b/uitest/src/main/java/com/vaadin/tests/components/AbstractListingFocusBlurTest.java new file mode 100644 index 0000000000..0b94b8fecb --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/AbstractListingFocusBlurTest.java @@ -0,0 +1,68 @@ +/* + * 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; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import com.googlecode.gentyref.GenericTypeReflector; +import com.vaadin.event.FieldEvents.BlurNotifier; +import com.vaadin.event.FieldEvents.FocusNotifier; +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.data.selection.SelectionModel; +import com.vaadin.ui.AbstractListing; + +/** + * @author Vaadin Ltd + * + */ +public abstract class AbstractListingFocusBlurTest & FocusNotifier & BlurNotifier, S extends SelectionModel> + extends AbstractTestUIWithLog { + + @Override + @SuppressWarnings("unchecked") + protected void setup(VaadinRequest request) { + Type valueType = GenericTypeReflector.getTypeParameter(getClass(), + AbstractListingFocusBlurTest.class.getTypeParameters()[0]); + if (valueType instanceof ParameterizedType) { + valueType = ((ParameterizedType) valueType).getRawType(); + } + if (valueType instanceof Class) { + Class clazz = (Class) valueType; + try { + AbstractListing select = (AbstractListing) clazz + .newInstance(); + select.setItems( + IntStream.range(1, 10).mapToObj(Integer::valueOf) + .collect(Collectors.toList())); + + addComponent(select); + ((FocusNotifier) select) + .addFocusListener(event -> log("Focus Event")); + ((BlurNotifier) select) + .addBlurListener(event -> log("Blur Event")); + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } else { + throw new RuntimeException( + "Unexpected component type " + valueType.getTypeName()); + } + } + +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/checkboxgroup/CheckBoxGroupFocusBlur.java b/uitest/src/main/java/com/vaadin/tests/components/checkboxgroup/CheckBoxGroupFocusBlur.java index 2b43a8134e..92efed09e7 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/checkboxgroup/CheckBoxGroupFocusBlur.java +++ b/uitest/src/main/java/com/vaadin/tests/components/checkboxgroup/CheckBoxGroupFocusBlur.java @@ -15,27 +15,20 @@ */ package com.vaadin.tests.components.checkboxgroup; -import java.util.stream.IntStream; - -import com.vaadin.server.VaadinRequest; -import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.shared.data.selection.SelectionModel.Multi; +import com.vaadin.tests.components.AbstractListingFocusBlurTest; import com.vaadin.ui.CheckBoxGroup; /** + * This class only provides a component type. The initialization code is inside + * the AbstractListingFocusBlurTest class. + * + * @see AbstractListingFocusBlurTest + * * @author Vaadin Ltd * */ -public class CheckBoxGroupFocusBlur extends AbstractTestUIWithLog { - - @Override - protected void setup(VaadinRequest request) { - CheckBoxGroup group = new CheckBoxGroup<>(); - group.setItems(IntStream.range(1, 10).mapToObj(Integer::valueOf) - .toArray(Integer[]::new)); - addComponent(group); - - group.addFocusListener(event -> log("Focus Event")); - group.addBlurListener(event -> log("Blur Event")); - } +public class CheckBoxGroupFocusBlur extends + AbstractListingFocusBlurTest, Multi> { } diff --git a/uitest/src/main/java/com/vaadin/tests/components/nativeselect/NativeSelectFocusBlur.java b/uitest/src/main/java/com/vaadin/tests/components/nativeselect/NativeSelectFocusBlur.java index b665e52fcb..b2664f892b 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/nativeselect/NativeSelectFocusBlur.java +++ b/uitest/src/main/java/com/vaadin/tests/components/nativeselect/NativeSelectFocusBlur.java @@ -15,28 +15,20 @@ */ package com.vaadin.tests.components.nativeselect; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import com.vaadin.server.VaadinRequest; -import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.tests.components.AbstractListingFocusBlurTest; +import com.vaadin.ui.AbstractSingleSelect; import com.vaadin.ui.NativeSelect; /** + * This class only provides a component type. The initialization code is inside + * the AbstractListingFocusBlurTest class. + * + * @see AbstractListingFocusBlurTest + * * @author Vaadin Ltd * */ -public class NativeSelectFocusBlur extends AbstractTestUIWithLog { - - @Override - protected void setup(VaadinRequest request) { - NativeSelect select = new NativeSelect<>(); - select.setItems(IntStream.range(1, 10).mapToObj(Integer::valueOf) - .collect(Collectors.toList())); - - addComponent(select); - select.addFocusListener(event -> log("Focus Event")); - select.addBlurListener(event -> log("Blur Event")); - } +public class NativeSelectFocusBlur extends + AbstractListingFocusBlurTest, AbstractSingleSelect.AbstractSingleSelection> { } diff --git a/uitest/src/main/java/com/vaadin/tests/components/radiobuttongroup/RadioButtonGroupFocusBlur.java b/uitest/src/main/java/com/vaadin/tests/components/radiobuttongroup/RadioButtonGroupFocusBlur.java new file mode 100644 index 0000000000..02238e5ef4 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/radiobuttongroup/RadioButtonGroupFocusBlur.java @@ -0,0 +1,34 @@ +/* + * 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.radiobuttongroup; + +import com.vaadin.tests.components.AbstractListingFocusBlurTest; +import com.vaadin.ui.AbstractSingleSelect; +import com.vaadin.ui.RadioButtonGroup; + +/** + * This class only provides a component type. The initialization code is inside + * the AbstractListingFocusBlurTest class. + * + * @see AbstractListingFocusBlurTest + * + * @author Vaadin Ltd + * + */ +public class RadioButtonGroupFocusBlur extends + AbstractListingFocusBlurTest, AbstractSingleSelect.AbstractSingleSelection> { + +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/radiobuttongroup/RadioButtonGroupFocusBlurTest.java b/uitest/src/test/java/com/vaadin/tests/components/radiobuttongroup/RadioButtonGroupFocusBlurTest.java new file mode 100644 index 0000000000..77fde84f54 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/radiobuttongroup/RadioButtonGroupFocusBlurTest.java @@ -0,0 +1,86 @@ +/* + * 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.radiobuttongroup; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; + +import com.vaadin.testbench.customelements.RadioButtonGroupElement; +import com.vaadin.testbench.elements.LabelElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * @author Vaadin Ltd + * + */ +public class RadioButtonGroupFocusBlurTest extends MultiBrowserTest { + + @Test + public void focusBlurEvents() { + openTestURL(); + + List radioButtons = $(RadioButtonGroupElement.class).first() + .findElements(By.tagName("input")); + radioButtons.get(0).click(); + + // Focus event is fired + Assert.assertTrue(logContainsText("1. Focus Event")); + + radioButtons.get(1).click(); + // click on the second radio button doesn't fire anything + Assert.assertFalse(logContainsText("2.")); + + // click in the middle between the first and the second (inside group). + WebElement first = radioButtons.get(0); + int middle = (first.getLocation().y + first.getSize().height + + radioButtons.get(1).getLocation().y) / 2; + new Actions(getDriver()).moveByOffset(first.getLocation().x, middle) + .click().build().perform(); + // no new events + Assert.assertFalse(logContainsText("2.")); + + // click to label of a radio button + $(RadioButtonGroupElement.class).first() + .findElements(By.tagName("label")).get(2).click(); + // no new events + Assert.assertFalse(logContainsText("2.")); + + // click on log label => blur + $(LabelElement.class).first().click(); + // blur event is fired + Assert.assertTrue(logContainsText("2. Blur Event")); + + radioButtons.get(3).click(); + // Focus event is fired + Assert.assertTrue(logContainsText("3. Focus Event")); + + // move keyboard focus to the next radio button + radioButtons.get(3).sendKeys(Keys.ARROW_DOWN); + // no new events + Assert.assertFalse(logContainsText("4.")); + + // select the next radio button + radioButtons.get(4).sendKeys(Keys.TAB); + // focus has gone away + waitUntil(driver -> logContainsText("4. Blur Event"), 5); + } +} -- 2.39.5