]> source.dussan.org Git - vaadin-framework.git/commitdiff
Implement focus/blur events for RadioButtonGroup.
authorDenis Anisimov <denis@vaadin.com>
Sun, 30 Oct 2016 16:11:21 +0000 (18:11 +0200)
committerVaadin Code Review <review@vaadin.com>
Mon, 31 Oct 2016 14:45:13 +0000 (14:45 +0000)
Fixes vaadin/framework8-issues#333

Change-Id: I55f5d6a0cd690f2c0b5e757318a5f528a67ef34e

client/src/main/java/com/vaadin/client/ui/VCheckBoxGroup.java
client/src/main/java/com/vaadin/client/ui/VRadioButtonGroup.java
client/src/main/java/com/vaadin/client/ui/optiongroup/RadioButtonGroupConnector.java
client/src/main/java/com/vaadin/client/widgets/FocusableFlowPanelComposite.java
server/src/main/java/com/vaadin/ui/RadioButtonGroup.java
uitest/src/main/java/com/vaadin/tests/components/AbstractListingFocusBlurTest.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/components/checkboxgroup/CheckBoxGroupFocusBlur.java
uitest/src/main/java/com/vaadin/tests/components/nativeselect/NativeSelectFocusBlur.java
uitest/src/main/java/com/vaadin/tests/components/radiobuttongroup/RadioButtonGroupFocusBlur.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/radiobuttongroup/RadioButtonGroupFocusBlurTest.java [new file with mode: 0644]

index 6ba7ea75d50c9be05e5aa518c5da674632368c41..d6b8c3ade91c10a4cdaf06a0d6a6252cca27c741 100644 (file)
@@ -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;
     }
index fd4d7cf98a5593f1201cc8f9deee0944254a45e0..fe5afea6208dadb8686ee382f220019f336e0771 100644 (file)
@@ -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)
-     * <p>
-     * 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<JsonObject> 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<Widget> 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<Widget> 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);
         }
     }
index 135126a099b9180339e3626d40d39d189e5fc2ac..4f3cad3c4ce1ddbd8b43a4276b07df650833f22e 100644 (file)
 
 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<SelectionModel.Single<?>> {
+public class RadioButtonGroupConnector extends
+        AbstractFocusableListingConnector<VRadioButtonGroup, SelectionModel.Single<?>> {
 
     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<JsonObject> dataSource = getDataSource();
index 4a5d031c3f62b69eaeb5e6d4ebe42e623d07e16e..7a245496646b78c1b453c03d53c63c6b422ceabd 100644 (file)
@@ -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();
+    }
 }
index a1474c91d40f69a90654b84cc563e58ec40c0509..6f1191e8d433d9688c65971b4a0980ded526c055 100644 (file)
@@ -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<T> extends AbstractSingleSelect<T> {
+public class RadioButtonGroup<T> extends AbstractSingleSelect<T>
+        implements FocusNotifier, BlurNotifier {
 
     private IconGenerator<T> itemIconGenerator = item -> null;
 
@@ -93,6 +102,7 @@ public class RadioButtonGroup<T> extends AbstractSingleSelect<T> {
      * @see Listing#setDataSource(DataSource)
      */
     public RadioButtonGroup() {
+        registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));
         setSelectionModel(new SimpleSingleSelection());
 
         addDataGenerator(new DataGenerator<T>() {
@@ -245,4 +255,32 @@ public class RadioButtonGroup<T> extends AbstractSingleSelect<T> {
         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 (file)
index 0000000..0b94b8f
--- /dev/null
@@ -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<T extends AbstractListing<Integer, S> & FocusNotifier & BlurNotifier, S extends SelectionModel<Integer>>
+        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<Integer, ?> select = (AbstractListing<Integer, ?>) 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());
+        }
+    }
+
+}
index 2b43a8134e119039d7c768be4fcd6738663ab625..92efed09e722f24f1f0f07465aef94f44b56f648 100644 (file)
  */
 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<Integer> 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<CheckBoxGroup<Integer>, Multi<Integer>> {
 
 }
index b665e52fcb74652fe6c9a73036b8ded183c6510f..b2664f892bb27942da425fd35d31f947d9da3f65 100644 (file)
  */
 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<Integer> 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<NativeSelect<Integer>, AbstractSingleSelect<Integer>.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 (file)
index 0000000..02238e5
--- /dev/null
@@ -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<RadioButtonGroup<Integer>, AbstractSingleSelect<Integer>.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 (file)
index 0000000..77fde84
--- /dev/null
@@ -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<WebElement> 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);
+    }
+}