]> source.dussan.org Git - vaadin-framework.git/commitdiff
Create a CheckBoxGroup that replaces the multi select case of OptionGroup
authorelmot <elmot@vaadin.com>
Mon, 12 Sep 2016 14:58:38 +0000 (17:58 +0300)
committerVaadin Code Review <review@vaadin.com>
Tue, 13 Sep 2016 18:06:18 +0000 (18:06 +0000)
Change-Id: I250c60741bc65443b66498a8d0b17541edb77bf1

client/src/main/java/com/vaadin/client/ui/VCheckBoxGroup.java [new file with mode: 0644]
client/src/main/java/com/vaadin/client/ui/checkbox/CheckBoxConnector.java
client/src/main/java/com/vaadin/client/ui/optiongroup/CheckBoxGroupConnector.java [new file with mode: 0644]
server/src/main/java/com/vaadin/ui/CheckBoxGroup.java [new file with mode: 0644]
server/src/test/java/com/vaadin/ui/CheckBoxGroupBoVTest.java [new file with mode: 0644]
shared/src/main/java/com/vaadin/shared/ui/optiongroup/CheckBoxGroupConstants.java [new file with mode: 0644]
shared/src/main/java/com/vaadin/shared/ui/optiongroup/CheckBoxGroupState.java [new file with mode: 0644]
uitest-common/src/main/java/com/vaadin/testbench/customelements/CheckBoxGroupElement.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/components/checkbox/CheckBoxGroupTestUI.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/checkboxgroup/CheckBoxGroupTest.java [new file with mode: 0644]

diff --git a/client/src/main/java/com/vaadin/client/ui/VCheckBoxGroup.java b/client/src/main/java/com/vaadin/client/ui/VCheckBoxGroup.java
new file mode 100644 (file)
index 0000000..3b6c739
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * 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.google.gwt.aria.client.Roles;
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+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.Widget;
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.WidgetUtil;
+import com.vaadin.shared.Registration;
+import com.vaadin.shared.ui.optiongroup.CheckBoxGroupConstants;
+import elemental.json.JsonObject;
+
+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;
+
+import static com.vaadin.shared.ui.optiongroup.CheckBoxGroupConstants.JSONKEY_ITEM_DISABLED;
+
+/**
+ * The client-side widget for the {@code CheckBoxGroup} component.
+ *
+ * @author Vaadin Ltd.
+ * @since 8.0
+ */
+public class VCheckBoxGroup extends Composite
+        implements Field, ClickHandler, ChangeHandler,
+        com.vaadin.client.Focusable, HasEnabled {
+
+    public static final String CLASSNAME = "v-select-optiongroup";
+    public static final String CLASSNAME_OPTION = "v-select-option";
+
+    private final Map<VCheckBox, JsonObject> optionsToItems;
+
+    /**
+     * For internal use only. May be removed or replaced in the future.
+     */
+    public ApplicationConnection client;
+    /**
+     * For internal use only. May be removed or replaced in the future.
+     */
+    public JsonObject selected; //TODO replace with SelectionModel
+    /**
+     * 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;
+    private boolean readonly;
+    private List<Consumer<JsonObject>> selectionChangeListeners;
+
+    public VCheckBoxGroup() {
+        optionsContainer = new FlowPanel();
+        initWidget(this.optionsContainer);
+        optionsContainer.setStyleName(CLASSNAME);
+        optionsToItems = new HashMap<>();
+        selectionChangeListeners = new ArrayList<>();
+    }
+
+    /*
+     * 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();
+        for (JsonObject item : items) {
+            String itemHtml =
+                    item.getString(CheckBoxGroupConstants.JSONKEY_ITEM_VALUE);
+            if (!isHtmlContentAllowed()) {
+                itemHtml = WidgetUtil.escapeHTML(itemHtml);
+            }
+            VCheckBox checkBox = new VCheckBox();
+
+            String iconUrl =
+                    item.getString(CheckBoxGroupConstants.JSONKEY_ITEM_ICON);
+            if (iconUrl != null && iconUrl.length() != 0) {
+                checkBox.icon = client.getIcon(iconUrl);
+            }
+
+            checkBox.addStyleName(CLASSNAME_OPTION);
+            checkBox.addClickHandler(this);
+            checkBox.setHTML(itemHtml);
+            checkBox.setValue(true);//TODO selection model here
+            boolean optionEnabled = !item.getBoolean(JSONKEY_ITEM_DISABLED);
+            boolean enabled = optionEnabled && !isReadonly() && isEnabled();
+            checkBox.setEnabled(enabled);
+
+            optionsContainer.add(checkBox);
+            optionsToItems.put(checkBox, item);
+        }
+    }
+
+    @Override
+    public void onClick(ClickEvent event) {
+        if (event.getSource() instanceof VCheckBox) {
+            VCheckBox source = (VCheckBox) event.getSource();
+            if (!source.isEnabled()) {
+                // Click events on the text are received even though the
+                // checkbox is disabled
+                return;
+            }
+
+            final boolean selected = source.getValue();
+            JsonObject item = optionsToItems.get(source);  //TODO SelectionModel
+            if (selected) {
+                this.selected = item;
+            } else {
+                this.selected = null;
+            }
+        }
+    }
+
+    public void setTabIndex(int tabIndex) {
+        for (Widget anOptionsContainer : optionsContainer) {
+            FocusWidget widget = (FocusWidget) anOptionsContainer;
+            widget.setTabIndex(tabIndex);
+        }
+    }
+
+    protected void updateEnabledState() {
+        boolean optionGroupEnabled = isEnabled() && !isReadonly();
+        // sets options enabled according to the widget's enabled,
+        // readonly and each options own enabled
+        for (Map.Entry<VCheckBox, JsonObject> entry : optionsToItems
+                .entrySet()) {
+            VCheckBox checkBox = entry.getKey();
+            JsonObject value = entry.getValue();
+            Boolean isOptionEnabled = !value.getBoolean(
+                    CheckBoxGroupConstants.JSONKEY_ITEM_DISABLED);
+            checkBox.setEnabled(optionGroupEnabled && isOptionEnabled);
+        }
+    }
+
+    @Override
+    public void focus() {
+        Iterator<Widget> iterator = optionsContainer.iterator();
+        if (iterator.hasNext()) {
+            ((Focusable) iterator.next()).setFocus(true);
+        }
+    }
+
+    public boolean isHtmlContentAllowed() {
+        return htmlContentAllowed;
+    }
+
+    public void setHtmlContentAllowed(boolean htmlContentAllowed) {
+        this.htmlContentAllowed = htmlContentAllowed;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public boolean isReadonly() {
+        return readonly;
+    }
+
+    @Override
+    public void onChange(ChangeEvent event) {
+        //TODO selectionModel
+    }
+
+    public void setReadonly(boolean readonly) {
+        if (this.readonly != readonly) {
+            this.readonly = readonly;
+            updateEnabledState();
+        }
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        if (this.enabled != enabled) {
+            this.enabled = enabled;
+            updateEnabledState();
+        }
+    }
+
+    public Registration addNotifyHandler(
+            Consumer<JsonObject> selectionChanged) {
+        selectionChangeListeners.add(selectionChanged);
+        return (Registration) () -> selectionChangeListeners
+                .remove(selectionChanged);
+    }
+}
index 598280cf5c0652b3a11c51b33db22dbc297a9aab..3c36ffd4fe85698fa41652d4cb13eede4ef37208 100644 (file)
@@ -36,6 +36,12 @@ import com.vaadin.shared.ui.checkbox.CheckBoxServerRpc;
 import com.vaadin.shared.ui.checkbox.CheckBoxState;
 import com.vaadin.ui.CheckBox;
 
+/**
+ * The client-side connector for the {@code CheckBoxGroup} component.
+ *
+ * @author Vaadin Ltd.
+ * @since 8.0
+ */
 @Connect(CheckBox.class)
 public class CheckBoxConnector extends AbstractFieldConnector
         implements ClickHandler {
diff --git a/client/src/main/java/com/vaadin/client/ui/optiongroup/CheckBoxGroupConnector.java b/client/src/main/java/com/vaadin/client/ui/optiongroup/CheckBoxGroupConnector.java
new file mode 100644 (file)
index 0000000..dbd5313
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * 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.optiongroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.vaadin.client.communication.StateChangeEvent;
+import com.vaadin.client.connectors.AbstractListingConnector;
+import com.vaadin.client.data.DataSource;
+import com.vaadin.client.ui.VCheckBoxGroup;
+import com.vaadin.shared.Registration;
+import com.vaadin.shared.data.selection.SelectionModel;
+import com.vaadin.shared.ui.Connect;
+import com.vaadin.shared.ui.optiongroup.CheckBoxGroupState;
+import com.vaadin.ui.CheckBoxGroup;
+
+import elemental.json.JsonObject;
+
+@Connect(CheckBoxGroup.class)
+public class CheckBoxGroupConnector
+        extends AbstractListingConnector<SelectionModel.Multi<JsonObject>> {
+
+    private Registration selectionChangeRegistration;
+
+    @Override
+    protected void init() {
+        super.init();
+        selectionChangeRegistration =
+                getWidget().addNotifyHandler(this::selectionChanged);
+    }
+
+    private void selectionChanged(JsonObject newSelection) {
+        getSelectionModel().select(newSelection);
+    }
+
+    @Override
+    public void onStateChanged(StateChangeEvent stateChangeEvent) {
+        super.onStateChanged(stateChangeEvent);
+        getWidget().setTabIndex(getState().tabIndex);
+        getWidget().setReadonly(isReadOnly());
+        getWidget().client = getConnection();
+    }
+
+    @Override
+    public void setDataSource(DataSource<JsonObject> dataSource) {
+        dataSource.addDataChangeHandler(range -> updateOptionGroup());
+        super.setDataSource(dataSource);
+    }
+
+    private void updateOptionGroup() {
+        List<JsonObject> items = new ArrayList<>(getDataSource().size());
+        for (int i = 0; i < getDataSource().size(); ++i) {
+            JsonObject item = getDataSource().getRow(i);
+            items.add(item);
+        }
+        getWidget().buildOptions(items);
+    }
+
+    @Override
+    public VCheckBoxGroup getWidget() {
+        return (VCheckBoxGroup) super.getWidget();
+    }
+
+    @Override
+    public CheckBoxGroupState getState() {
+        return (CheckBoxGroupState) super.getState();
+    }
+}
diff --git a/server/src/main/java/com/vaadin/ui/CheckBoxGroup.java b/server/src/main/java/com/vaadin/ui/CheckBoxGroup.java
new file mode 100644 (file)
index 0000000..2ad72c5
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * 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.ui;
+
+import com.vaadin.data.Listing;
+import com.vaadin.server.Resource;
+import com.vaadin.server.ResourceReference;
+import com.vaadin.server.data.DataGenerator;
+import com.vaadin.server.data.DataSource;
+import com.vaadin.shared.data.selection.SelectionModel;
+import com.vaadin.shared.ui.optiongroup.CheckBoxGroupConstants;
+import com.vaadin.shared.ui.optiongroup.CheckBoxGroupState;
+import elemental.json.JsonObject;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * A group of Checkboxes. Individual checkboxes are made from items given to
+ * supplied by {@code Datasource}. Checkboxes my have captions and icons.
+ *
+ * @param <T>
+ *         item type
+ * @author Vaadin Ltd
+ * @since 8.0
+ */
+public class CheckBoxGroup<T> extends AbstractListing<T, SelectionModel<T>> {
+
+    private Function<T, Resource> itemIconProvider = item -> null;
+
+    private Function<T, String> itemCaptionProvider = String::valueOf;
+
+    private Predicate<T> itemEnabledProvider = item -> true;
+
+    /**
+     * Constructs a new CheckBoxGroup with caption.
+     *
+     * @param caption
+     *         caption text
+     * @see Listing#setDataSource(DataSource)
+     */
+    public CheckBoxGroup(String caption) {
+        this();
+        setCaption(caption);
+    }
+
+    /**
+     * Constructs a new CheckBoxGroup with caption and DataSource.
+     *
+     * @param caption
+     *         the caption text
+     * @param dataSource
+     *         the data source, not null
+     * @see Listing#setDataSource(DataSource)
+     */
+    public CheckBoxGroup(String caption, DataSource<T> dataSource) {
+        this(caption);
+        setDataSource(dataSource);
+    }
+
+    /**
+     * Constructs a new CheckBoxGroup with caption and DataSource containing
+     * given items.
+     *
+     * @param caption
+     *         the caption text
+     * @param items
+     *         the data items to use, not null
+     * @see Listing#setDataSource(DataSource)
+     */
+    public CheckBoxGroup(String caption, Collection<T> items) {
+        this(caption, DataSource.create(items));
+    }
+
+    /**
+     * Constructs a new CheckBoxGroup.
+     *
+     * @see Listing#setDataSource(DataSource)
+     */
+    public CheckBoxGroup() {
+        //TODO Fix when MultiSelection is ready
+        //            SingleSelection<T> model = new SingleSelection<>(this);
+        //            setSelectionModel(model);
+        //            model.addSelectionListener(event -> beforeClientResponse(false));
+        setSelectionModel(new SelectionModel.Multi<T>() {
+            @Override
+            public void select(T item) {
+
+            }
+
+            @Override
+            public Set<T> getSelectedItems() {
+                return Collections.emptySet();
+            }
+
+            @Override
+            public void deselect(T item) {
+
+            }
+
+            @Override
+            public void deselectAll() {
+
+            }
+
+            @Override
+            public boolean isSelected(T item) {
+                return false;
+            }
+        });
+        addDataGenerator(new DataGenerator<T>() {
+            @Override
+            public void generateData(T data, JsonObject jsonObject) {
+                jsonObject.put(CheckBoxGroupConstants.JSONKEY_ITEM_VALUE,
+                               itemCaptionProvider.apply(data));
+                Resource icon = itemIconProvider.apply(data);
+                if (icon != null) {
+                    String iconUrl = ResourceReference
+                            .create(icon, CheckBoxGroup.this, null).getURL();
+                    jsonObject.put(CheckBoxGroupConstants.JSONKEY_ITEM_ICON,
+                                   iconUrl);
+                }
+                if (!itemEnabledProvider.test(data)) {
+                    jsonObject.put(CheckBoxGroupConstants.JSONKEY_ITEM_DISABLED,
+                                   true);
+                }
+            }
+
+            @Override
+            public void destroyData(T data) {
+            }
+        });
+
+    }
+
+    /**
+     * Sets whether html is allowed in the item captions. If set to true, the
+     * captions are passed to the browser as html and the developer is
+     * responsible for ensuring no harmful html is used. If set to false, the
+     * content is passed to the browser as plain text.
+     *
+     * @param htmlContentAllowed
+     *         true if the captions are used as html, false if used as plain
+     *         text
+     */
+    public void setHtmlContentAllowed(boolean htmlContentAllowed) {
+        getState().htmlContentAllowed = htmlContentAllowed;
+    }
+
+    /**
+     * Checks whether captions are interpreted as html or plain text.
+     *
+     * @return true if the captions are used as html, false if used as plain
+     * text
+     * @see #setHtmlContentAllowed(boolean)
+     */
+    public boolean isHtmlContentAllowed() {
+        return getState(false).htmlContentAllowed;
+    }
+
+    @Override
+    protected CheckBoxGroupState getState() {
+        return (CheckBoxGroupState) super.getState();
+    }
+
+    @Override
+    protected CheckBoxGroupState getState(boolean markAsDirty) {
+        return (CheckBoxGroupState) super.getState(markAsDirty);
+    }
+
+    /**
+     * Returns the item icons provider.
+     *
+     * @return the icons provider for items
+     * @see #setItemIconProvider
+     */
+    public Function<T, Resource> getItemIconProvider() {
+        return itemIconProvider;
+    }
+
+    /**
+     * Sets the item icon provider for this checkbox group. The icon provider
+     * is queried for each item to optionally display an icon next to
+     * the item caption. If the provider returns null for an item, no icon is
+     * displayed. The default provider always returns null (no icons).
+     *
+     * @param itemIconProvider
+     *         icons provider, not null
+     */
+    public void setItemIconProvider(Function<T, Resource> itemIconProvider) {
+        Objects.nonNull(itemIconProvider);
+        this.itemIconProvider = itemIconProvider;
+    }
+
+    /**
+     * Returns the item caption provider.
+     *
+     * @return the captions provider
+     * @see #setItemCaptionProvider
+     */
+    public Function<T, String> getItemCaptionProvider() {
+        return itemCaptionProvider;
+    }
+
+    /**
+     * Sets the item caption provider for this checkbox group. The caption
+     * provider is queried for each item to optionally display an item
+     * textual representation. The default provider returns
+     * {@code String.valueOf(item)}.
+     *
+     * @param itemCaptionProvider
+     *         the item caption provider, not null
+     */
+    public void setItemCaptionProvider(
+            Function<T, String> itemCaptionProvider) {
+        Objects.nonNull(itemCaptionProvider);
+        this.itemCaptionProvider = itemCaptionProvider;
+    }
+
+    /**
+     * Returns the item enabled predicate.
+     *
+     * @return the item enabled predicate
+     * @see #setItemEnabledProvider
+     */
+    public Predicate<T> getItemEnabledProvider() {
+        return itemEnabledProvider;
+    }
+
+    /**
+     * Sets the item enabled predicate for this checkbox group. The predicate
+     * is applied to each item to determine whether the item should be
+     * enabled  (true) or disabled (false). Disabled items are displayed as
+     * grayed out and the user cannot select them. The default predicate
+     * always returns true (all the items are enabled).
+     *
+     * @param itemEnabledProvider
+     *         the item enable predicate, not null
+     */
+    public void setItemEnabledProvider(Predicate<T> itemEnabledProvider) {
+        Objects.nonNull(itemEnabledProvider);
+        this.itemEnabledProvider = itemEnabledProvider;
+    }
+}
diff --git a/server/src/test/java/com/vaadin/ui/CheckBoxGroupBoVTest.java b/server/src/test/java/com/vaadin/ui/CheckBoxGroupBoVTest.java
new file mode 100644 (file)
index 0000000..e859475
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2000-2014 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.ui;
+
+import java.util.EnumSet;
+
+/**
+ * Option group test from Book of Vaadin
+ *
+ * @author Vaadin Ltd
+ * @since 8.0
+ */
+public class CheckBoxGroupBoVTest
+{
+    public enum Status {
+        STATE_A,
+        STATE_B,
+        STATE_C,
+        STATE_D;
+
+        public String getCaption() {
+            return "** " + toString();
+        }
+    }
+
+
+    public void createOptionGroup() {
+        CheckBoxGroup<Status> s = new CheckBoxGroup<>();
+        s.setItems(EnumSet.allOf(Status.class));
+        s.setItemCaptionProvider(Status::getCaption);
+    }
+
+}
diff --git a/shared/src/main/java/com/vaadin/shared/ui/optiongroup/CheckBoxGroupConstants.java b/shared/src/main/java/com/vaadin/shared/ui/optiongroup/CheckBoxGroupConstants.java
new file mode 100644 (file)
index 0000000..2747dd4
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.shared.ui.optiongroup;
+
+import java.io.Serializable;
+
+public class CheckBoxGroupConstants implements Serializable {
+    public static final String JSONKEY_ITEM_DISABLED = "d";
+
+    public static final String JSONKEY_ITEM_ICON = "i";
+
+    public static final String JSONKEY_ITEM_VALUE = "v";
+
+    public static final String JSONKEY_ITEM_KEY = "k";
+}
diff --git a/shared/src/main/java/com/vaadin/shared/ui/optiongroup/CheckBoxGroupState.java b/shared/src/main/java/com/vaadin/shared/ui/optiongroup/CheckBoxGroupState.java
new file mode 100644 (file)
index 0000000..2decfdd
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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.shared.ui.optiongroup;
+
+import com.vaadin.shared.annotations.DelegateToWidget;
+import com.vaadin.shared.AbstractFieldState;
+
+/**
+ * Shared state for the CheckBoxGroup component.
+ *
+ * @author Vaadin Ltd.
+ * @since 8.0
+ */
+public class CheckBoxGroupState extends AbstractFieldState {
+
+    {
+        primaryStyleName = "v-select-optiongroup";
+    }
+
+    @DelegateToWidget
+    public boolean htmlContentAllowed = false;
+}
diff --git a/uitest-common/src/main/java/com/vaadin/testbench/customelements/CheckBoxGroupElement.java b/uitest-common/src/main/java/com/vaadin/testbench/customelements/CheckBoxGroupElement.java
new file mode 100644 (file)
index 0000000..b070ece
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2000-2014 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.testbench.customelements;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openqa.selenium.WebElement;
+
+import com.vaadin.testbench.By;
+import com.vaadin.testbench.elements.AbstractSelectElement;
+import com.vaadin.testbench.elementsbase.ServerClass;
+
+/**
+ * TestBench element supporting CheckBoxGroup
+ *
+ * @author Vaadin Ltd
+ */
+
+@ServerClass("com.vaadin.ui.CheckBoxGroup")
+public class CheckBoxGroupElement extends AbstractSelectElement {
+    private static org.openqa.selenium.By byButtonSpan =
+            By.className("v-select-option");
+    private static org.openqa.selenium.By byLabel = By.tagName("label");
+    private static org.openqa.selenium.By byInput = By.tagName("input");
+
+    public List<String> getOptions() {
+        List<String> optionTexts = new ArrayList<String>();
+        List<WebElement> options = findElements(byButtonSpan);
+        for (WebElement option : options) {
+            optionTexts.add(option.findElement(byLabel).getText());
+        }
+        return optionTexts;
+    }
+
+    public void selectByText(String text) throws ReadOnlyException {
+        if (isReadOnly()) {
+            throw new ReadOnlyException();
+        }
+        List<WebElement> options = findElements(byButtonSpan);
+        for (WebElement option : options) {
+            if (text.equals(option.findElement(byLabel).getText())) {
+                option.findElement(byInput).click();
+            }
+        }
+    }
+
+    /**
+     * Return list of the selected options in the checkbox group
+     *
+     * @return list of the selected options in the checkbox group
+     */
+    public List<String> getSelection() {
+        List<String> values = new ArrayList<>();
+        List<WebElement> options = findElements(byButtonSpan);
+        for (WebElement option : options) {
+            WebElement checkedItem;
+            checkedItem = option.findElement(By.tagName("input"));
+            String checked = checkedItem.getAttribute("checked");
+            if (checked != null &&
+                    checkedItem.getAttribute("checked").equals("true")) {
+                values.add(option.findElement(By.tagName("label")).getText());
+            }
+        }
+        return values;
+    }
+
+    /**
+     * Select option in the checkbox group with the specified value
+     *
+     * @param chars
+     *         value of the option in the checkbox group which will be selected
+     */
+    public void selectOption(CharSequence chars) throws ReadOnlyException {
+        selectByText((String) chars);
+    }
+
+    @Override
+    public void clear() {
+        throw new UnsupportedOperationException(
+                "Clear operation is not supported for CheckBoxGroup." +
+                        " This operation has no effect on the element.");
+    }
+}
+
diff --git a/uitest/src/main/java/com/vaadin/tests/components/checkbox/CheckBoxGroupTestUI.java b/uitest/src/main/java/com/vaadin/tests/components/checkbox/CheckBoxGroupTestUI.java
new file mode 100644 (file)
index 0000000..343faf3
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2000-2014 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.checkbox;
+
+import com.vaadin.tests.components.abstractlisting.AbstractListingTestUI;
+import com.vaadin.ui.CheckBoxGroup;
+
+/**
+ * Test UI for CheckBoxGroup component
+ *
+ * @author Vaadin Ltd
+ */
+public class CheckBoxGroupTestUI
+        extends AbstractListingTestUI<CheckBoxGroup<Object>> {
+    @Override
+    protected Class<CheckBoxGroup<Object>> getTestClass() {
+        return (Class) CheckBoxGroup.class;
+    }
+}
diff --git a/uitest/src/test/java/com/vaadin/tests/components/checkboxgroup/CheckBoxGroupTest.java b/uitest/src/test/java/com/vaadin/tests/components/checkboxgroup/CheckBoxGroupTest.java
new file mode 100644 (file)
index 0000000..9a3c2a6
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2000-2014 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.checkboxgroup;
+
+import com.vaadin.testbench.customelements.CheckBoxGroupElement;
+import com.vaadin.tests.components.checkbox.CheckBoxGroupTestUI;
+import com.vaadin.tests.tb3.MultiBrowserTest;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test for CheckBoxGroup
+ *
+ * @author Vaadin Ltd
+ * @since 8.0
+ */
+public class CheckBoxGroupTest extends MultiBrowserTest {
+
+    @Before
+    public void setUp() throws Exception {
+        openTestURL();
+    }
+
+    @Test
+    public void initialLoad_containsCorrectItems() {
+        assertItems(20);
+    }
+
+    @Test
+    public void initialItems_reduceItemCount_containsCorrectItems() {
+        selectMenuPath("Component", "Data source", "Items", "5");
+        assertItems(5);
+    }
+
+    @Test
+    public void initialItems_increaseItemCount_containsCorrectItems() {
+        selectMenuPath("Component", "Data source", "Items", "100");
+        assertItems(100);
+    }
+
+    @Override
+    protected Class<?> getUIClass() {
+        return CheckBoxGroupTestUI.class;
+    }
+
+    protected CheckBoxGroupElement getSelect() {
+        return $(CheckBoxGroupElement.class).first();
+    }
+
+    protected void assertItems(int count) {
+        int i = 0;
+        for (String text : getSelect().getOptions()) {
+            assertEquals("Item " + i, text);
+            i++;
+        }
+        assertEquals("Number of items", count, i);
+    }
+}