]> source.dussan.org Git - vaadin-framework.git/commitdiff
Add item description to RadioButtonGroup and CheckBoxGroup (#9841)
authorTeemu Suo-Anttila <tsuoanttila@users.noreply.github.com>
Wed, 23 Aug 2017 11:02:20 +0000 (14:02 +0300)
committerAleksi Hietanen <aleksi@vaadin.com>
Wed, 23 Aug 2017 11:02:20 +0000 (14:02 +0300)
12 files changed:
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/CheckBoxGroupConnector.java
client/src/main/java/com/vaadin/client/ui/optiongroup/RadioButtonGroupConnector.java
server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java
server/src/main/java/com/vaadin/ui/CheckBoxGroup.java
server/src/main/java/com/vaadin/ui/RadioButtonGroup.java
shared/src/main/java/com/vaadin/shared/ui/ListingJsonConstants.java
uitest/src/main/java/com/vaadin/tests/components/checkbox/CheckBoxGroupTestUI.java
uitest/src/main/java/com/vaadin/tests/components/radiobutton/RadioButtonGroupTestUI.java
uitest/src/test/java/com/vaadin/tests/components/checkboxgroup/CheckBoxGroupTest.java
uitest/src/test/java/com/vaadin/tests/components/radiobuttongroup/RadioButtonGroupTest.java

index ee1d83ee2c20ebc8eb0e35608f4ccdcca0435372..8d38331af1a4a046ec43250652b26e86093ff7aa 100644 (file)
@@ -23,6 +23,7 @@ import java.util.Map;
 import java.util.function.BiConsumer;
 
 import com.google.gwt.aria.client.Roles;
+import com.google.gwt.dom.client.Element;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.user.client.ui.FocusWidget;
@@ -94,6 +95,21 @@ public class VCheckBoxGroup extends FocusableFlowPanelComposite
         }
     }
 
+    /**
+     * Returns the JsonObject used to populate the CheckBox widget that contains
+     * given Element.
+     *
+     * @since
+     * @param element
+     *            the element to search for
+     * @return the related JsonObject; {@code null} if not found
+     */
+    public JsonObject getItem(Element element) {
+        return optionsToItems.entrySet().stream()
+                .filter(e -> e.getKey().getElement().isOrHasChild(element))
+                .map(e -> e.getValue()).findFirst().orElse(null);
+    }
+
     private void remove(Widget widget) {
         getWidget().remove(widget);
         optionsToItems.remove(widget);
index 138600535120681cfcd0a2b25ab2ed3d4f98b36e..1561cb9b8ad65cdd9c101c216eb993bdd7d20fd1 100644 (file)
@@ -20,9 +20,11 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.function.Consumer;
 
 import com.google.gwt.aria.client.Roles;
+import com.google.gwt.dom.client.Element;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.user.client.DOM;
@@ -101,6 +103,28 @@ public class VRadioButtonGroup extends FocusableFlowPanelComposite
         }
     }
 
+    /**
+     * Returns the JsonObject used to populate the RadioButton widget that
+     * contains given Element.
+     * 
+     * @since
+     * @param element
+     *            the element to search for
+     * @return the related JsonObject; {@code null} if not found
+     */
+    public JsonObject getItem(Element element) {
+        // The HTML populated in updateItem does not match RadioButton directly,
+        // which is why tryGetItem is also attempted on the parent element
+        return tryGetItem(element)
+                .orElse(tryGetItem(element.getParentElement()).orElse(null));
+    }
+
+    private Optional<JsonObject> tryGetItem(Element element) {
+        return optionsToItems.entrySet().stream()
+                .filter(e -> e.getKey().getElement().equals(element))
+                .map(e -> e.getValue()).findFirst();
+    }
+
     private void remove(Widget widget) {
         getWidget().remove(widget);
         JsonObject item = optionsToItems.remove(widget);
index 1936aac79dd3df9020ff46756c40341052d328de..18d4019611825a5c6b5b8158a9baf63f8fe68a72 100644 (file)
@@ -21,6 +21,8 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 
+import com.google.gwt.dom.client.Element;
+import com.vaadin.client.TooltipInfo;
 import com.vaadin.client.communication.StateChangeEvent;
 import com.vaadin.client.connectors.AbstractFocusableListingConnector;
 import com.vaadin.client.data.DataSource;
@@ -28,6 +30,7 @@ import com.vaadin.client.ui.HasRequiredIndicator;
 import com.vaadin.client.ui.VCheckBoxGroup;
 import com.vaadin.shared.data.selection.MultiSelectServerRpc;
 import com.vaadin.shared.ui.Connect;
+import com.vaadin.shared.ui.ListingJsonConstants;
 import com.vaadin.shared.ui.optiongroup.CheckBoxGroupState;
 import com.vaadin.ui.CheckBoxGroup;
 
@@ -98,4 +101,20 @@ public class CheckBoxGroupConnector
     public boolean isRequiredIndicatorVisible() {
         return getState().required && !isReadOnly();
     }
+
+    @Override
+    public TooltipInfo getTooltipInfo(Element element) {
+        JsonObject item = getWidget().getItem(element);
+        if (item != null
+                && item.hasKey(ListingJsonConstants.JSONKEY_ITEM_DESCRIPTION)) {
+            return new TooltipInfo(item
+                    .getString(ListingJsonConstants.JSONKEY_ITEM_DESCRIPTION));
+        }
+        return super.getTooltipInfo(element);
+    }
+
+    @Override
+    public boolean hasTooltip() {
+        return true;
+    }
 }
index de92728712fe4c95fa8bfd7c6919ee36a091a6d5..882a91aa26a62f70032de7b37c463422f8d474d5 100644 (file)
@@ -19,6 +19,8 @@ package com.vaadin.client.ui.optiongroup;
 import java.util.ArrayList;
 import java.util.List;
 
+import com.google.gwt.dom.client.Element;
+import com.vaadin.client.TooltipInfo;
 import com.vaadin.client.annotations.OnStateChange;
 import com.vaadin.client.communication.StateChangeEvent;
 import com.vaadin.client.connectors.AbstractSingleSelectConnector;
@@ -28,6 +30,7 @@ import com.vaadin.shared.Range;
 import com.vaadin.shared.Registration;
 import com.vaadin.shared.data.selection.SelectionServerRpc;
 import com.vaadin.shared.ui.Connect;
+import com.vaadin.shared.ui.ListingJsonConstants;
 import com.vaadin.shared.ui.optiongroup.RadioButtonGroupState;
 import com.vaadin.ui.RadioButtonGroup;
 
@@ -120,4 +123,20 @@ public class RadioButtonGroupConnector
         select.buildOptions(options);
         updateSelectedItem();
     }
+
+    @Override
+    public TooltipInfo getTooltipInfo(Element element) {
+        JsonObject item = getWidget().getItem(element);
+        if (item != null
+                && item.hasKey(ListingJsonConstants.JSONKEY_ITEM_DESCRIPTION)) {
+            return new TooltipInfo(item
+                    .getString(ListingJsonConstants.JSONKEY_ITEM_DESCRIPTION));
+        }
+        return super.getTooltipInfo(element);
+    }
+
+    @Override
+    public boolean hasTooltip() {
+        return true;
+    }
 }
index a0c8fd2225a5edf8ca0ab5b2f601df6cef1a6386..93b0b35403469e1f32e7b86ef05c4c925a23cd2a 100644 (file)
@@ -89,7 +89,7 @@ public abstract class AbstractMultiSelect<T> extends AbstractListing<T>
 
     }
 
-    private class MultiSelectDataGenerator implements DataGenerator<T> {
+    private final class MultiSelectDataGenerator implements DataGenerator<T> {
         @Override
         public void generateData(T data, JsonObject jsonObject) {
             String caption = getItemCaptionGenerator().apply(data);
index b6b6d69074365fc229e3149c6dc8b1ed0e6cb3d9..c257a1a0d0d03b93c0e6790c64cf888dc92722c1 100644 (file)
@@ -17,6 +17,7 @@
 package com.vaadin.ui;
 
 import java.util.Collection;
+import java.util.Objects;
 import java.util.Set;
 
 import org.jsoup.nodes.Element;
@@ -32,7 +33,9 @@ import com.vaadin.event.FieldEvents.FocusListener;
 import com.vaadin.event.FieldEvents.FocusNotifier;
 import com.vaadin.server.SerializablePredicate;
 import com.vaadin.shared.Registration;
+import com.vaadin.shared.ui.ListingJsonConstants;
 import com.vaadin.shared.ui.optiongroup.CheckBoxGroupState;
+import com.vaadin.ui.components.grid.DescriptionGenerator;
 import com.vaadin.ui.declarative.DesignContext;
 import com.vaadin.ui.declarative.DesignFormatter;
 
@@ -48,6 +51,8 @@ import com.vaadin.ui.declarative.DesignFormatter;
 public class CheckBoxGroup<T> extends AbstractMultiSelect<T>
         implements FocusNotifier, BlurNotifier, HasDataProvider<T> {
 
+    private DescriptionGenerator<T> descriptionGenerator = item -> null;
+
     /**
      * Constructs a new CheckBoxGroup with caption.
      *
@@ -92,6 +97,13 @@ public class CheckBoxGroup<T> extends AbstractMultiSelect<T>
      */
     public CheckBoxGroup() {
         registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));
+        addDataGenerator((item, jsonObject) -> {
+            String description = getItemDescriptionGenerator().apply(item);
+            if (description != null) {
+                jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_DESCRIPTION,
+                        description);
+            }
+        });
     }
 
     /**
@@ -162,6 +174,38 @@ public class CheckBoxGroup<T> extends AbstractMultiSelect<T>
                 BlurListener.blurMethod);
     }
 
+    /**
+     * Sets the description generator that is used for generating descriptions
+     * for items. Description is shown as a tooltip when hovering on
+     * corresponding element. If the generator returns {@code null}, no tooltip
+     * is shown.
+     *
+     *
+     * @param descriptionGenerator
+     *            the item description generator to set, not {@code null}
+     * 
+     * @since
+     */
+    public void setItemDescriptionGenerator(
+            DescriptionGenerator<T> descriptionGenerator) {
+        Objects.requireNonNull(descriptionGenerator);
+        if (this.descriptionGenerator != descriptionGenerator) {
+            this.descriptionGenerator = descriptionGenerator;
+            getDataProvider().refreshAll();
+        }
+    }
+
+    /**
+     * Gets the item description generator.
+     *
+     * @return the item description generator
+     * 
+     * @since
+     */
+    public DescriptionGenerator<T> getItemDescriptionGenerator() {
+        return descriptionGenerator;
+    }
+
     @Override
     protected void readItems(Element design, DesignContext context) {
         setItemEnabledProvider(new DeclarativeItemEnabledProvider<>());
index 35490c49d1c4f765f6cadb1ffc9d0d3a4b186a98..c6e4a66c6599ff474347b82a7d3c1e1d76190114 100644 (file)
@@ -38,6 +38,7 @@ import com.vaadin.server.SerializablePredicate;
 import com.vaadin.shared.Registration;
 import com.vaadin.shared.ui.ListingJsonConstants;
 import com.vaadin.shared.ui.optiongroup.RadioButtonGroupState;
+import com.vaadin.ui.components.grid.DescriptionGenerator;
 import com.vaadin.ui.declarative.DesignContext;
 import com.vaadin.ui.declarative.DesignFormatter;
 
@@ -56,6 +57,7 @@ public class RadioButtonGroup<T> extends AbstractSingleSelect<T>
         implements FocusNotifier, BlurNotifier, HasDataProvider<T> {
 
     private SerializablePredicate<T> itemEnabledProvider = item -> true;
+    private DescriptionGenerator<T> descriptionGenerator = item -> null;
 
     /**
      * Constructs a new RadioButtonGroup with caption.
@@ -112,6 +114,12 @@ public class RadioButtonGroup<T> extends AbstractSingleSelect<T>
                 } else {
                     jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_VALUE, "");
                 }
+                String description = getItemDescriptionGenerator().apply(data);
+                if (description != null) {
+                    jsonObject.put(
+                            ListingJsonConstants.JSONKEY_ITEM_DESCRIPTION,
+                            description);
+                }
                 Resource icon = getItemIconGenerator().apply(data);
                 if (icon != null) {
                     String iconUrl = ResourceReference
@@ -219,6 +227,37 @@ public class RadioButtonGroup<T> extends AbstractSingleSelect<T>
         this.itemEnabledProvider = itemEnabledProvider;
     }
 
+    /**
+     * Sets the description generator that is used for generating descriptions
+     * for items. Description is shown as a tooltip when hovering on
+     * corresponding element. If the generator returns {@code null}, no tooltip
+     * is shown.
+     *
+     * @param descriptionGenerator
+     *            the item description generator to set, not {@code null}
+     * 
+     * @since
+     */
+    public void setItemDescriptionGenerator(
+            DescriptionGenerator<T> descriptionGenerator) {
+        Objects.requireNonNull(descriptionGenerator);
+        if (this.descriptionGenerator != descriptionGenerator) {
+            this.descriptionGenerator = descriptionGenerator;
+            getDataProvider().refreshAll();
+        }
+    }
+
+    /**
+     * Gets the item description generator.
+     *
+     * @return the item description generator
+     * 
+     * @since
+     */
+    public DescriptionGenerator<T> getItemDescriptionGenerator() {
+        return descriptionGenerator;
+    }
+
     @Override
     public Registration addFocusListener(FocusListener listener) {
         return addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener,
index 2ffd08ad7d6d825a9b921608cb3c068a3db780b3..acdbcd097fcf92b1b00dfd768c36de4311f77db6 100644 (file)
@@ -32,4 +32,9 @@ public class ListingJsonConstants implements Serializable {
     public static final String JSONKEY_ITEM_VALUE = "v";
 
     public static final String JSONKEY_ITEM_SELECTED = "s";
+
+    /**
+     * @since
+     */
+    public static final String JSONKEY_ITEM_DESCRIPTION = "dsc";
 }
index 88006fd68ac6096e4f0c194221b4f961c5c428e8..71c3a6147f5aac0d760a628e041965253c78dfde 100644 (file)
  */
 package com.vaadin.tests.components.checkbox;
 
+import java.util.LinkedHashMap;
+
 import com.vaadin.icons.VaadinIcons;
 import com.vaadin.tests.components.abstractlisting.AbstractMultiSelectTestUI;
 import com.vaadin.ui.CheckBoxGroup;
 import com.vaadin.ui.IconGenerator;
+import com.vaadin.ui.components.grid.DescriptionGenerator;
 
 /**
  * Test UI for CheckBoxGroup component
@@ -49,6 +52,7 @@ public class CheckBoxGroupTestUI
     protected void createActions() {
         super.createActions();
         createItemIconGenerator();
+        createItemDescriptionGeneratorMenu();
     }
 
     private void createItemIconGenerator() {
@@ -56,6 +60,21 @@ public class CheckBoxGroupTestUI
                 this::useItemIconProvider);
     }
 
+    private void createItemDescriptionGeneratorMenu() {
+        LinkedHashMap<String, DescriptionGenerator<Object>> options = new LinkedHashMap<>();
+        options.put("Null Description Generator", item -> null);
+        options.put("Default Description Generator", item -> item.toString());
+        options.put("Custom Description Generator",
+                item -> item.toString() + " Description");
+
+        createSelectAction("Item Description Generator",
+                "Item Description Generator", options, "None",
+                (checkBoxGroup, generator, data) -> {
+                    checkBoxGroup.setItemDescriptionGenerator(generator);
+                    checkBoxGroup.getDataProvider().refreshAll();
+                }, true);
+    }
+
     private void useItemIconProvider(CheckBoxGroup<Object> group,
             boolean activate, Object data) {
         if (activate) {
index 20314fd3475d66684c0e5d9a4fb9e089688d5a7a..d9161a6a46e7224900eeadb65ea024b82a34cc55 100644 (file)
@@ -22,6 +22,7 @@ import com.vaadin.icons.VaadinIcons;
 import com.vaadin.tests.components.abstractlisting.AbstractListingTestUI;
 import com.vaadin.ui.ItemCaptionGenerator;
 import com.vaadin.ui.RadioButtonGroup;
+import com.vaadin.ui.components.grid.DescriptionGenerator;
 
 /**
  * Test UI for RadioButtonGroup component
@@ -46,6 +47,7 @@ public class RadioButtonGroupTestUI
         createSelectionMenu();
         createItemIconGeneratorMenu();
         createItemCaptionGeneratorMenu();
+        createItemDescriptionGeneratorMenu();
     }
 
     protected void createSelectionMenu() {
@@ -92,6 +94,21 @@ public class RadioButtonGroupTestUI
                 }, true);
     }
 
+    private void createItemDescriptionGeneratorMenu() {
+        LinkedHashMap<String, DescriptionGenerator<Object>> options = new LinkedHashMap<>();
+        options.put("Null Description Generator", item -> null);
+        options.put("Default Description Generator", item -> item.toString());
+        options.put("Custom Description Generator",
+                item -> item.toString() + " Description");
+
+        createSelectAction("Item Description Generator",
+                "Item Description Generator", options, "None",
+                (radioButtonGroup, generator, data) -> {
+                    radioButtonGroup.setItemDescriptionGenerator(generator);
+                    radioButtonGroup.getDataProvider().refreshAll();
+                }, true);
+    }
+
     private void toggleSelection(String item) {
         if (getComponent().isSelected(item)) {
             getComponent().setValue(null);
index 5d7c37c75496b820d91352cc7b4842d95805b1eb..98d9cc03e558940e504375e630cb815e8168a303 100644 (file)
@@ -33,6 +33,7 @@ import org.openqa.selenium.By;
 import org.openqa.selenium.WebElement;
 
 import com.vaadin.icons.VaadinIcons;
+import com.vaadin.testbench.TestBenchElement;
 import com.vaadin.testbench.elements.CheckBoxGroupElement;
 import com.vaadin.tests.components.checkbox.CheckBoxGroupTestUI;
 import com.vaadin.tests.tb3.MultiBrowserTest;
@@ -195,6 +196,28 @@ public class CheckBoxGroupTest extends MultiBrowserTest {
         assertSelected("Item 1");
     }
 
+    @Test
+    public void testItemDescriptionGenerators() {
+        TestBenchElement label;
+
+        selectMenuPath("Component", "Item Description Generator",
+                "Item Description Generator", "Default Description Generator");
+
+        label = (TestBenchElement) findElements(By.tagName("label")).get(5);
+        label.showTooltip();
+        Assert.assertEquals("Tooltip should contain the same text as caption",
+                label.getText(), getTooltipElement().getText());
+
+        selectMenuPath("Component", "Item Description Generator",
+                "Item Description Generator", "Custom Description Generator");
+
+        label = (TestBenchElement) findElements(By.tagName("label")).get(5);
+        label.showTooltip();
+        Assert.assertEquals("Tooltip should contain caption + ' Description'",
+                label.getText() + " Description",
+                getTooltipElement().getText());
+    }
+
     private void assertSelected(String... expectedSelection) {
         Assert.assertEquals(Arrays.asList(expectedSelection),
                 getSelect().getValue());
index 2c53903d29c0bcaa41d01d8938e38cd6126c0eab..6c0b60be78ec7729c8f3096ed5e7c3d9db732547 100644 (file)
@@ -27,6 +27,7 @@ import org.openqa.selenium.WebElement;
 
 import com.vaadin.icons.VaadinIcons;
 import com.vaadin.testbench.By;
+import com.vaadin.testbench.TestBenchElement;
 import com.vaadin.testbench.elements.RadioButtonGroupElement;
 import com.vaadin.tests.components.radiobutton.RadioButtonGroupTestUI;
 import com.vaadin.tests.tb3.MultiBrowserTest;
@@ -189,6 +190,28 @@ public class RadioButtonGroupTest extends MultiBrowserTest {
         assertSelected("Item 5");
     }
 
+    @Test
+    public void testItemDescriptionGenerators() {
+        TestBenchElement label;
+
+        selectMenuPath("Component", "Item Description Generator",
+                "Item Description Generator", "Default Description Generator");
+
+        label = (TestBenchElement) findElements(By.tagName("label")).get(5);
+        label.showTooltip();
+        Assert.assertEquals("Tooltip should contain the same text as caption",
+                label.getText(), getTooltipElement().getText());
+
+        selectMenuPath("Component", "Item Description Generator",
+                "Item Description Generator", "Custom Description Generator");
+
+        label = (TestBenchElement) findElements(By.tagName("label")).get(5);
+        label.showTooltip();
+        Assert.assertEquals("Tooltip should contain caption + ' Description'",
+                label.getText() + " Description",
+                getTooltipElement().getText());
+    }
+
     private void assertSelected(String expectedSelection) {
         Assert.assertEquals(expectedSelection, getSelect().getValue());
     }