diff --git a/client/src/main/java/com/vaadin/client/ui/VListSelect.java b/client/src/main/java/com/vaadin/client/ui/VListSelect.java new file mode 100644 index 00000000000..185c593b839 --- /dev/null +++ b/client/src/main/java/com/vaadin/client/ui/VListSelect.java @@ -0,0 +1,263 @@ +/* + * 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 java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.function.BiConsumer; + +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.HasEnabled; +import com.google.gwt.user.client.ui.ListBox; +import com.vaadin.client.FastStringSet; +import com.vaadin.client.Focusable; +import com.vaadin.client.connectors.AbstractMultiSelectConnector.MultiSelectWidget; +import com.vaadin.shared.Registration; + +import elemental.json.JsonObject; + +/** + * A simple list select for selecting multiple items. + * + * @author Vaadin Ltd + */ +public class VListSelect extends Composite implements ClickHandler, Field, + Focusable, HasEnabled, MultiSelectWidget { + + private List, Set>> selectionChangeListeners = new ArrayList<>(); + + /** Container for select. Kept for DOM backwards compatibility. */ + protected final FlowPanel container; + /** The select component. */ + protected final ListBox select; + + private boolean enabled; + private boolean readOnly; + private FastStringSet selectedItemKeys = FastStringSet.create(); + + /** + * Constructs a simple ListSelect widget in multiselect mode. + */ + public VListSelect() { + container = new FlowPanel(); + initWidget(container); + + select = new ListBox(); + select.setMultipleSelect(true); + select.addClickHandler(this); + + container.add(select); + + updateEnabledState(); + } + + /** + * Sets the number of visible items for the list select. + * + * @param rows + * the number of items to show + * @see ListBox#setVisibleItemCount(int) + */ + public void setRows(int rows) { + if (select.getVisibleItemCount() != rows) { + select.setVisibleItemCount(rows); + } + } + + /** + * Returns the number of visible items for the list select. + * + * @return the number of items to show + * @see ListBox#setVisibleItemCount(int) + */ + public int getRows() { + return select.getVisibleItemCount(); + } + + @Override + public Registration addSelectionChangeListener( + BiConsumer, Set> listener) { + Objects.nonNull(listener); + selectionChangeListeners.add(listener); + return (Registration) () -> selectionChangeListeners.remove(listener); + } + + @Override + public void setStyleName(String style) { + super.setStyleName(style); + updateStyleNames(); + } + + @Override + public void setStylePrimaryName(String style) { + super.setStylePrimaryName(style); + updateStyleNames(); + } + + /** Update the style names for container & select. */ + protected void updateStyleNames() { + container.setStyleName(getStylePrimaryName()); + select.setStyleName(getStylePrimaryName() + "-select"); + } + + @Override + public void setItems(List items) { + selectedItemKeys = FastStringSet.create(); + for (int i = 0; i < items.size(); i++) { + final JsonObject item = items.get(i); + // reuse existing option if possible + final String key = MultiSelectWidget.getKey(item); + if (i < select.getItemCount()) { + select.setItemText(i, MultiSelectWidget.getCaption(item)); + select.setValue(i, key); + } else { + select.addItem(MultiSelectWidget.getCaption(item), key); + } + final boolean selected = MultiSelectWidget.isSelected(item); + select.setItemSelected(i, selected); + if (selected) { + selectedItemKeys.add(key); + } + } + + // remove extra + for (int i = select.getItemCount() - 1; i >= items.size(); i--) { + select.removeItem(i); + } + } + + /** + * Gets the currently selected item values. + * + * @return the currently selected item keys + */ + protected FastStringSet getSelectedItems() { + final FastStringSet selectedItemKeys = FastStringSet.create(); + for (int i = 0; i < select.getItemCount(); i++) { + if (select.isItemSelected(i)) { + selectedItemKeys.add(select.getValue(i)); + } + } + return selectedItemKeys; + } + + @Override + public void onClick(ClickEvent event) { + if (event.getSource() == select) { + // selection can change by adding and at the same time removing + // previous keys, or by just adding (e.g. when modifier keys are + // pressed) + final Set newSelectedItemKeys = new HashSet<>(); + final Set removedItemKeys = new HashSet<>(); + for (int i = 0; i < select.getItemCount(); i++) { + String key = select.getValue(i); + boolean selected = select.isItemSelected(i); + boolean wasSelected = selectedItemKeys.contains(key); + if (selected && !wasSelected) { + newSelectedItemKeys.add(key); + selectedItemKeys.add(key); + } else if (!selected && wasSelected) { + removedItemKeys.add(key); + selectedItemKeys.remove(key); + } + } + selectionChangeListeners.forEach( + l -> l.accept(newSelectedItemKeys, removedItemKeys)); + } + } + + @Override + public void setHeight(String height) { + select.setHeight(height); + super.setHeight(height); + } + + @Override + public void setWidth(String width) { + select.setWidth(width); + super.setWidth(width); + } + + /** + * Sets the tab index. + * + * @param tabIndex + * the tab index to set + */ + public void setTabIndex(int tabIndex) { + select.setTabIndex(tabIndex); + } + + /** + * Gets the tab index. + * + * @return the tab index + */ + public int getTabIndex() { + return select.getTabIndex(); + } + + /** + * Sets this select as read only, meaning selection cannot be changed. + * + * @param readOnly + * {@code true} for read only, {@code false} for not read only + */ + public void setReadOnly(boolean readOnly) { + if (this.readOnly != readOnly) { + this.readOnly = readOnly; + updateEnabledState(); + } + } + + /** + * Returns {@code true} if this select is in read only mode, {@code false} + * if not. + * + * @return {@code true} for read only, {@code false} for not read only + */ + public boolean isReadOnly() { + return readOnly; + } + + @Override + public void setEnabled(boolean enabled) { + if (this.enabled != enabled) { + this.enabled = enabled; + updateEnabledState(); + } + } + + @Override + public boolean isEnabled() { + return enabled; + } + + private void updateEnabledState() { + select.setEnabled(isEnabled() && !isReadOnly()); + } + + @Override + public void focus() { + select.setFocus(true); + } +} diff --git a/client/src/main/java/com/vaadin/client/ui/listselect/ListSelectConnector.java b/client/src/main/java/com/vaadin/client/ui/listselect/ListSelectConnector.java new file mode 100644 index 00000000000..3e20b115e0e --- /dev/null +++ b/client/src/main/java/com/vaadin/client/ui/listselect/ListSelectConnector.java @@ -0,0 +1,54 @@ +/* + * 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.listselect; + +import com.vaadin.client.annotations.OnStateChange; +import com.vaadin.client.connectors.AbstractMultiSelectConnector; +import com.vaadin.client.ui.VListSelect; +import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.listselect.ListSelectState; +import com.vaadin.ui.ListSelect; + +/** + * Client side connector for {@link ListSelect} component. + * + * @author Vaadin Ltd + * + */ +@Connect(ListSelect.class) +public class ListSelectConnector extends AbstractMultiSelectConnector { + + @Override + public VListSelect getWidget() { + return (VListSelect) super.getWidget(); + } + + @Override + public MultiSelectWidget getMultiSelectWidget() { + return getWidget(); + } + + @Override + public ListSelectState getState() { + return (ListSelectState) super.getState(); + } + + @OnStateChange("readOnly") + void updateReadOnly() { + getWidget().setReadOnly(isReadOnly()); + } + +} diff --git a/server/src/main/java/com/vaadin/ui/ListSelect.java b/server/src/main/java/com/vaadin/ui/ListSelect.java new file mode 100644 index 00000000000..7bbcbb2e8bb --- /dev/null +++ b/server/src/main/java/com/vaadin/ui/ListSelect.java @@ -0,0 +1,124 @@ +/* + * 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 java.util.Collection; + +import com.vaadin.server.data.DataSource; +import com.vaadin.shared.ui.listselect.ListSelectState; + +/** + * This is a simple list select without, for instance, support for new items, + * lazyloading, and other advanced features. + * + * @author Vaadin Ltd + * + * @param + * item type + */ +public class ListSelect extends AbstractMultiSelect { + + /** Default number of rows visible for select. */ + // protected to allow javadoc linking + protected static final int DEFAULT_ROWS = 10; + + /** + * Constructs a new ListSelect. + */ + public ListSelect() { + setRows(DEFAULT_ROWS); + } + + /** + * Constructs a new ListSelect with the given caption. + * + * @param caption + * the caption to set, can be {@code null} + */ + public ListSelect(String caption) { + this(); + setCaption(caption); + } + + /** + * Constructs a new ListSelect with caption and data source for options. + * + * @param caption + * the caption to set, can be {@code null} + * @param dataSource + * the data source, not {@code null} + */ + public ListSelect(String caption, DataSource dataSource) { + this(caption); + setDataSource(dataSource); + } + + /** + * Constructs a new ListSelect with caption and the given options. + * + * @param caption + * the caption to set, can be {@code null} + * @param options + * the options, cannot be {@code null} + */ + public ListSelect(String caption, Collection options) { + this(caption, DataSource.create(options)); + } + + /** + * Returns the number of rows in the select. + *

+ * Default value is {@link #DEFAULT_ROWS} + * + * @return the number of rows visible + */ + public int getRows() { + return getState(false).rows; + } + + /** + * Sets the number of rows in the select. If the number of rows is set to 0, + * the actual number of displayed rows is determined implicitly by the + * select. + *

+ * If a height if set (using {@link #setHeight(String)} or + * {@link #setHeight(float, int)}) it overrides the number of rows. Leave + * the height undefined to use this method. + *

+ * Default value is {@link #DEFAULT_ROWS} + * + * @param rows + * the number of rows to set. + */ + public void setRows(int rows) { + if (rows < 0) { + rows = 0; + } + if (getState(false).rows != rows) { + getState().rows = rows; + } + } + + @Override + protected ListSelectState getState() { + return (ListSelectState) super.getState(); + } + + @Override + protected ListSelectState getState(boolean markAsDirty) { + return (ListSelectState) super.getState(markAsDirty); + } +} diff --git a/server/src/test/java/com/vaadin/tests/server/component/listselect/ListSelectDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/listselect/ListSelectDeclarativeTest.java new file mode 100644 index 00000000000..a3079bcdc30 --- /dev/null +++ b/server/src/test/java/com/vaadin/tests/server/component/listselect/ListSelectDeclarativeTest.java @@ -0,0 +1,73 @@ +/* + * 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.server.component.listselect; + +import java.util.Arrays; + +import org.junit.Test; + +import com.vaadin.tests.design.DeclarativeTestBase; +import com.vaadin.ui.ListSelect; + +public class ListSelectDeclarativeTest + extends DeclarativeTestBase> { + + private ListSelect getWithOptionsExpected() { + ListSelect ls = new ListSelect<>(null, + Arrays.asList("Male", "Female")); + ls.setRows(9); // 10 is default + return ls; + } + + private String getWithOptionsDesign() { + return "\n" + + " \n" + + " \n" + + "\n" + ""; + } + + @Test + public void testReadWithOptions() { + testRead(getWithOptionsDesign(), getWithOptionsExpected()); + } + + @Test + public void testWriteWithOptions() { + testWrite(stripOptionTags(getWithOptionsDesign()), + getWithOptionsExpected()); + } + + private ListSelect getBasicExpected() { + ListSelect ls = new ListSelect<>(); + ls.setCaption("Hello"); + return ls; + } + + private String getBasicDesign() { + return ""; + } + + @Test + public void testReadBasic() { + testRead(getBasicDesign(), getBasicExpected()); + } + + @Test + public void testWriteBasic() { + testWrite(getBasicDesign(), getBasicExpected()); + } + +} diff --git a/server/src/test/java/com/vaadin/ui/AbstractMultiSelectTest.java b/server/src/test/java/com/vaadin/ui/AbstractMultiSelectTest.java index 3505e4ec38a..d10cee91571 100644 --- a/server/src/test/java/com/vaadin/ui/AbstractMultiSelectTest.java +++ b/server/src/test/java/com/vaadin/ui/AbstractMultiSelectTest.java @@ -41,7 +41,8 @@ public class AbstractMultiSelectTest { @Parameters(name = "{0}") public static Iterable> multiSelects() { - return Arrays.asList(new CheckBoxGroup<>(), new TwinColSelect<>()); + return Arrays.asList(new CheckBoxGroup<>(), new TwinColSelect<>(), + new ListSelect<>()); } @Parameter @@ -90,6 +91,11 @@ public void stableSelectionOrder() { selectionModel.deselectItems("2", "1", "4", "5"); assertSelectionOrder(selectionModel, "3", "7", "8"); + + selectionModel.updateSelection( + new LinkedHashSet<>(Arrays.asList("5", "2")), + new LinkedHashSet<>(Arrays.asList("3", "8"))); + assertSelectionOrder(selectionModel, "7", "5", "2"); } @Test diff --git a/shared/src/main/java/com/vaadin/shared/ui/listselect/ListSelectState.java b/shared/src/main/java/com/vaadin/shared/ui/listselect/ListSelectState.java new file mode 100644 index 00000000000..8999f3c7399 --- /dev/null +++ b/shared/src/main/java/com/vaadin/shared/ui/listselect/ListSelectState.java @@ -0,0 +1,33 @@ +/* + * 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.listselect; + +import com.vaadin.shared.annotations.DelegateToWidget; +import com.vaadin.shared.ui.TabIndexState; + +/** + * Shared state for ListSelect component. + * + * @author Vaadin Ltd + * + */ +public class ListSelectState extends TabIndexState { + { + primaryStyleName = "v-select"; + } + @DelegateToWidget + public int rows; +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectAddRemoveItems.java b/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectAddRemoveItems.java index dc98df4b923..cd2e8ad1f47 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectAddRemoveItems.java +++ b/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectAddRemoveItems.java @@ -15,20 +15,27 @@ */ package com.vaadin.tests.components.listselect; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + import com.vaadin.server.VaadinRequest; +import com.vaadin.server.data.ListDataSource; import com.vaadin.tests.components.AbstractTestUIWithLog; import com.vaadin.ui.Button; -import com.vaadin.ui.Button.ClickEvent; -import com.vaadin.v7.data.util.IndexedContainer; -import com.vaadin.v7.ui.ListSelect; +import com.vaadin.ui.ListSelect; +// FIXME this test should be updated once the datasource supports CRUD operations #77 public class ListSelectAddRemoveItems extends AbstractTestUIWithLog { - private IndexedContainer container = new IndexedContainer(); + private ListDataSource dataSource = new ListDataSource<>( + Collections.emptyList()); + private ListSelect listSelect; @Override protected void setup(VaadinRequest request) { - ListSelect listSelect = new ListSelect("ListSelect", container); + listSelect = new ListSelect<>("ListSelect", dataSource); listSelect.setWidth("100px"); listSelect.setRows(10); @@ -36,87 +43,87 @@ protected void setup(VaadinRequest request) { logContainer(); addComponent(listSelect); - addComponent(new Button("Reset", new Button.ClickListener() { - @Override - public void buttonClick(ClickEvent event) { - resetContainer(); - log.clear(); - logContainer(); - } + addComponent(new Button("Reset", event -> { + resetContainer(); + log.clear(); + logContainer(); })); - addComponent(new Button("Add first", new Button.ClickListener() { - @Override - public void buttonClick(ClickEvent event) { - container.addItemAt(0, "first"); - logContainer(); - } + addComponent(new Button("Add first", event -> { + List list = dataSource.apply(null) + .collect(Collectors.toList()); + list.add(0, "first"); + dataSource = new ListDataSource<>(list); + listSelect.setDataSource(dataSource); + logContainer(); })); - addComponent(new Button("Add middle", new Button.ClickListener() { - @Override - public void buttonClick(ClickEvent event) { - container.addItemAt(container.size() / 2, "middle"); - logContainer(); - } + addComponent(new Button("Add middle", event -> { + List list = dataSource.apply(null) + .collect(Collectors.toList()); + list.add(list.size() / 2, "middle"); + dataSource = new ListDataSource<>(list); + listSelect.setDataSource(dataSource); + logContainer(); })); - addComponent(new Button("Add last", new Button.ClickListener() { - @Override - public void buttonClick(ClickEvent event) { - container.addItem("last"); - logContainer(); - } + addComponent(new Button("Add last", event -> { + List list = dataSource.apply(null) + .collect(Collectors.toList()); + list.add("last"); + dataSource = new ListDataSource<>(list); + listSelect.setDataSource(dataSource); + logContainer(); })); - addComponent(new Button("Swap", new Button.ClickListener() { - @Override - public void buttonClick(ClickEvent event) { - Object lastItem = container.lastItemId(); - Object firstItem = container.firstItemId(); - if (lastItem != firstItem) { - container.removeItem(lastItem); - container.removeItem(firstItem); - - container.addItemAt(0, lastItem); - container.addItem(firstItem); - } + addComponent(new Button("Swap", event -> { + List list = dataSource.apply(null) + .collect(Collectors.toList()); + Collections.swap(list, 0, list.size() - 1); + dataSource = new ListDataSource<>(list); + listSelect.setDataSource(dataSource); - logContainer(); - } + logContainer(); })); - addComponent(new Button("Remove first", new Button.ClickListener() { - @Override - public void buttonClick(ClickEvent event) { - container.removeItem(container.firstItemId()); - logContainer(); - } + addComponent(new Button("Remove first", event -> { + List list = dataSource.apply(null) + .collect(Collectors.toList()); + list.remove(0); + + dataSource = new ListDataSource<>(list); + listSelect.setDataSource(dataSource); + + logContainer(); })); - addComponent(new Button("Remove middle", new Button.ClickListener() { - @Override - public void buttonClick(ClickEvent event) { - container.removeItem( - container.getIdByIndex(container.size() / 2)); - logContainer(); - } + addComponent(new Button("Remove middle", event -> { + List list = dataSource.apply(null) + .collect(Collectors.toList()); + list.remove(list.size() / 2); + dataSource = new ListDataSource<>(list); + listSelect.setDataSource(dataSource); + logContainer(); })); - addComponent(new Button("Remove last", new Button.ClickListener() { - @Override - public void buttonClick(ClickEvent event) { - container.removeItem(container.lastItemId()); - logContainer(); - } + addComponent(new Button("Remove last", event -> { + List list = dataSource.apply(null) + .collect(Collectors.toList()); + list.remove(list.size() - 1); + + dataSource = new ListDataSource<>(list); + listSelect.setDataSource(dataSource); + + logContainer(); })); } private void logContainer() { StringBuilder b = new StringBuilder(); - for (int i = 0; i < container.size(); i++) { - Object id = container.getIdByIndex(i); + List list = dataSource.apply(null).collect(Collectors.toList()); + for (int i = 0; i < list.size(); i++) { + Object id = list.get(i); if (i != 0) { b.append(", "); } @@ -127,10 +134,8 @@ private void logContainer() { } public void resetContainer() { - container.removeAllItems(); - for (String value : new String[] { "a", "b", "c" }) { - container.addItem(value); - } + dataSource = new ListDataSource<>(Arrays.asList("a", "b", "c")); + listSelect.setDataSource(dataSource); } @Override diff --git a/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectAllowNewItem.java b/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectAllowNewItem.java deleted file mode 100644 index ee56f2c5a5d..00000000000 --- a/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectAllowNewItem.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.vaadin.tests.components.listselect; - -import java.util.Arrays; - -import com.vaadin.tests.components.TestBase; -import com.vaadin.v7.ui.ListSelect; - -public class ListSelectAllowNewItem extends TestBase { - - @Override - protected void setup() { - ListSelect select = new ListSelect("Select", - Arrays.asList("Option 1", "Option 2")); - select.setImmediate(true); - select.setNewItemsAllowed(true); - addComponent(select); - } - - @Override - protected String getDescription() { - return "ListSelect with allowNewItems turned on"; - } - - @Override - protected Integer getTicketNumber() { - return 10537; - } - -} diff --git a/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectJump.java b/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectJump.java index e719b4f13ea..ae0b25c09c9 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectJump.java +++ b/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectJump.java @@ -8,8 +8,7 @@ import com.vaadin.tests.components.AbstractTestUI; import com.vaadin.ui.Button; import com.vaadin.ui.Label; -import com.vaadin.v7.ui.AbstractSelect; -import com.vaadin.v7.ui.ListSelect; +import com.vaadin.ui.ListSelect; public class ListSelectJump extends AbstractTestUI { @@ -26,12 +25,8 @@ public void setup(VaadinRequest request) { for (int i = 1; i <= 25; i++) { list.add("Option #" + i); } - ListSelect listSelect = new ListSelect(null, list); - listSelect.setNullSelectionAllowed(false); - listSelect.setMultiSelect(true); - listSelect.setImmediate(false); + ListSelect listSelect = new ListSelect<>(null, list); listSelect.setRows(5); - listSelect.setItemCaptionMode(AbstractSelect.ItemCaptionMode.ID); listSelect.setId("listselect"); addComponent(listSelect); Button button = new Button("Press Me"); diff --git a/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectPushSelectionChanges.java b/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectPushSelectionChanges.java deleted file mode 100644 index 4b80b878778..00000000000 --- a/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectPushSelectionChanges.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2000-2016 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.tests.components.listselect; - -import java.util.Arrays; -import java.util.Collection; - -import com.vaadin.server.VaadinRequest; -import com.vaadin.tests.components.AbstractTestUI; -import com.vaadin.ui.Button; -import com.vaadin.ui.Button.ClickEvent; -import com.vaadin.v7.data.Property.ValueChangeEvent; -import com.vaadin.v7.data.Property.ValueChangeListener; -import com.vaadin.v7.ui.ListSelect; -import com.vaadin.v7.ui.OptionGroup; - -public class ListSelectPushSelectionChanges extends AbstractTestUI { - - @Override - protected void setup(VaadinRequest request) { - Collection options = Arrays.asList("a", "b", "c"); - - final ListSelect listSelect = new ListSelect("ListSelect", options); - listSelect.setNullSelectionAllowed(true); - - final OptionGroup optionGroup = new OptionGroup("OptionGroup", options); - - // Option group changes propagate to the list select - listSelect.setPropertyDataSource(optionGroup); - - final OptionGroup modeSelect = new OptionGroup("Mode", - Arrays.asList("Single", "Multi")); - modeSelect.setValue("Single"); - modeSelect.addValueChangeListener(new ValueChangeListener() { - @Override - public void valueChange(ValueChangeEvent event) { - optionGroup.setValue(null); - - boolean multiSelect = "Multi".equals(modeSelect.getValue()); - - listSelect.setMultiSelect(multiSelect); - optionGroup.setMultiSelect(multiSelect); - } - }); - - Button selectNullButton = new Button("Select null", - new Button.ClickListener() { - @Override - public void buttonClick(ClickEvent event) { - listSelect.setValue(null); - listSelect.markAsDirty(); - } - }); - - addComponents(modeSelect, listSelect, optionGroup, selectNullButton); - } -} diff --git a/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectTestUI.java b/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectTestUI.java new file mode 100644 index 00000000000..57196d55648 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectTestUI.java @@ -0,0 +1,35 @@ +package com.vaadin.tests.components.listselect; + +import java.util.LinkedHashMap; + +import com.vaadin.tests.components.abstractlisting.AbstractMultiSelectTestUI; +import com.vaadin.ui.ListSelect; + +public class ListSelectTestUI + extends AbstractMultiSelectTestUI> { + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + protected Class> getTestClass() { + return (Class) ListSelect.class; + } + + @Override + protected void createActions() { + super.createActions(); + createRows(); + }; + + private void createRows() { + LinkedHashMap options = new LinkedHashMap<>(); + options.put("0", 0); + options.put("1", 1); + options.put("2", 2); + options.put("5", 5); + options.put("10 (default)", 10); + options.put("50", 50); + + createSelectAction("Rows", CATEGORY_STATE, options, "10 (default)", + (c, value, data) -> c.setRows(value), null); + } +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/listselect/ListSelectAddRemoveItemsTest.java b/uitest/src/test/java/com/vaadin/tests/components/listselect/ListSelectAddRemoveItemsTest.java index ed1cbbea716..ee655c40aff 100644 --- a/uitest/src/test/java/com/vaadin/tests/components/listselect/ListSelectAddRemoveItemsTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/listselect/ListSelectAddRemoveItemsTest.java @@ -28,28 +28,28 @@ public class ListSelectAddRemoveItemsTest extends SingleBrowserTest { @Test public void testAddAndRemove() { openTestURL(); - assertOptions("", "a", "b", "c"); + assertOptions("a", "b", "c"); click("Add first"); - assertOptions("", "first", "a", "b", "c"); + assertOptions("first", "a", "b", "c"); click("Swap"); - assertOptions("", "c", "a", "b", "first"); + assertOptions("c", "a", "b", "first"); click("Remove first"); - assertOptions("", "a", "b", "first"); + assertOptions("a", "b", "first"); click("Add middle"); - assertOptions("", "a", "middle", "b", "first"); + assertOptions("a", "middle", "b", "first"); click("Add last"); - assertOptions("", "a", "middle", "b", "first", "last"); + assertOptions("a", "middle", "b", "first", "last"); click("Remove middle"); - assertOptions("", "a", "middle", "first", "last"); + assertOptions("a", "middle", "first", "last"); click("Reset"); - assertOptions("", "a", "b", "c"); + assertOptions("a", "b", "c"); } private void assertOptions(String... options) { diff --git a/uitest/src/test/java/com/vaadin/tests/components/listselect/ListSelectPushSelectionChangesTest.java b/uitest/src/test/java/com/vaadin/tests/components/listselect/ListSelectPushSelectionChangesTest.java deleted file mode 100644 index 44cc4b86b8b..00000000000 --- a/uitest/src/test/java/com/vaadin/tests/components/listselect/ListSelectPushSelectionChangesTest.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2000-2016 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.tests.components.listselect; - -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; -import org.openqa.selenium.By; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.ui.Select; - -import com.vaadin.testbench.elements.ButtonElement; -import com.vaadin.testbench.elements.ListSelectElement; -import com.vaadin.testbench.elements.OptionGroupElement; -import com.vaadin.tests.tb3.SingleBrowserTestPhantomJS2; - -public class ListSelectPushSelectionChangesTest - extends SingleBrowserTestPhantomJS2 { - @Test - public void testMultiSelectBehavior() { - openTestURL(); - - Assert.assertEquals( - "Should have null item + 3 options in single selection mode", 4, - getListSelect().getOptions().size()); - - $(OptionGroupElement.class).caption("Mode").first() - .selectByText("Multi"); - - Assert.assertEquals( - "Should have 3 options but no null item in multi selection mode", - 3, getListSelect().getOptions().size()); - - selectOptionGroup("a"); - Assert.assertEquals("a", getSelectValue()); - - selectOptionGroup("b"); - Assert.assertEquals("a,b", getSelectValue()); - - selectOptionGroup("a"); - Assert.assertEquals( - "Clicking selected item should deselct in multi selection mode", - "b", getSelectValue()); - - selectNull(); - Assert.assertEquals("", getSelectValue()); - } - - @Test - public void testSingleSelectBehavior() { - openTestURL(); - - selectOptionGroup("a"); - Assert.assertEquals("a", getSelectValue()); - - selectOptionGroup("b"); - Assert.assertEquals("b", getSelectValue()); - - selectOptionGroup("b"); - Assert.assertEquals( - "Selecting the selected item again should not deselect in single selection mode", - "b", getSelectValue()); - - selectNull(); - Assert.assertEquals("", getSelectValue()); - Assert.assertEquals( - "Not even the single select item should be selected after setValue(null)", - 0, getSelectCount()); - - selectOptionGroup("c"); - Assert.assertEquals("c", getSelectValue()); - - getListSelect().selectByText(""); - Assert.assertEquals("", getSelectValue()); - Assert.assertEquals( - "Null select item should remain selected if clicked by the user", - 1, getSelectCount()); - - selectNull(); - Assert.assertEquals("", getSelectValue()); - Assert.assertEquals( - "Null select item should remain selected even after a repaint", - 1, getSelectCount()); - } - - private ListSelectElement getListSelect() { - return $(ListSelectElement.class).first(); - } - - private int getSelectCount() { - return getSelectedOptions().size(); - } - - private void selectNull() { - $(ButtonElement.class).first().click(); - } - - private String getSelectValue() { - List selectedOptions = getSelectedOptions(); - - StringBuilder value = new StringBuilder(); - for (int i = 0; i < selectedOptions.size(); i++) { - if (i != 0) { - value.append(','); - } - value.append(selectedOptions.get(i).getText()); - } - return value.toString(); - } - - private List getSelectedOptions() { - ListSelectElement listSelect = getListSelect(); - Select select = new Select( - listSelect.findElement(By.tagName("select"))); - return select.getAllSelectedOptions(); - } - - private void selectOptionGroup(String value) { - $(OptionGroupElement.class).caption("OptionGroup").first() - .selectByText(value); - } -} diff --git a/uitest/src/test/java/com/vaadin/tests/components/listselect/ListSelectTest.java b/uitest/src/test/java/com/vaadin/tests/components/listselect/ListSelectTest.java new file mode 100644 index 00000000000..44ae8537903 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/listselect/ListSelectTest.java @@ -0,0 +1,266 @@ +package com.vaadin.tests.components.listselect; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.Select; + +import com.vaadin.testbench.elements.ListSelectElement; +import com.vaadin.tests.tb3.SingleBrowserTestPhantomJS2; + +public class ListSelectTest extends SingleBrowserTestPhantomJS2 { + @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); + } + + @Test + public void clickToSelect() { + selectMenuPath("Component", "Listeners", "Selection listener"); + + selectItem("Item 4"); + Assert.assertEquals("1. Selected: [Item 4]", getLogRow(0)); + + selectItem("Item 2"); + Assert.assertEquals("3. Selected: [Item 2]", getLogRow(0)); + + addItemsToSelection("Item 4"); + Assert.assertEquals("4. Selected: [Item 2, Item 4]", getLogRow(0)); + + addItemsToSelection("Item 10", "Item 0", "Item 9"); // will cause 3 + // events + + Assert.assertEquals( + "7. Selected: [Item 2, Item 4, Item 10, Item 0, Item 9]", + getLogRow(0)); + + removeItemsFromSelection("Item 0", "Item 2", "Item 9"); // will cause 3 + // events + Assert.assertEquals("10. Selected: [Item 4, Item 10]", getLogRow(0)); + } + + @Test + public void disabled_clickToSelect() { + selectMenuPath("Component", "State", "Enabled"); + + List select = getListSelect() + .findElements(By.tagName("select")); + Assert.assertEquals(1, select.size()); + Assert.assertNotNull(select.get(0).getAttribute("disabled")); + + selectMenuPath("Component", "Listeners", "Selection listener"); + + String lastLogRow = getLogRow(0); + + selectItem("Item 4"); + Assert.assertEquals(lastLogRow, getLogRow(0)); + assertNothingSelected(); + + addItemsToSelection("Item 2"); + Assert.assertEquals(lastLogRow, getLogRow(0)); + assertNothingSelected(); + + removeItemsFromSelection("Item 4"); + Assert.assertEquals(lastLogRow, getLogRow(0)); + assertNothingSelected(); + } + + @Test + public void readOnly_clickToSelect() { + selectMenuPath("Component", "Listeners", "Selection listener"); + selectMenuPath("Component", "State", "Readonly"); + + List select = getListSelect() + .findElements(By.tagName("select")); + Assert.assertEquals(1, select.size()); + Assert.assertNotNull(select.get(0).getAttribute("disabled")); + + String lastLogRow = getLogRow(0); + + selectItem("Item 4"); + Assert.assertEquals(lastLogRow, getLogRow(0)); + assertNothingSelected(); + + addItemsToSelection("Item 2"); + Assert.assertEquals(lastLogRow, getLogRow(0)); + assertNothingSelected(); + + removeItemsFromSelection("Item 4"); + Assert.assertEquals(lastLogRow, getLogRow(0)); + assertNothingSelected(); + } + + @Test + public void clickToSelect_reenable() { + selectMenuPath("Component", "State", "Enabled"); + selectMenuPath("Component", "Listeners", "Selection listener"); + + selectItem("Item 4"); + assertNothingSelected(); + + selectMenuPath("Component", "State", "Enabled"); + + selectItem("Item 5"); + Assert.assertEquals("3. Selected: [Item 5]", getLogRow(0)); + + selectItem("Item 1"); + Assert.assertEquals("5. Selected: [Item 1]", getLogRow(0)); + + addItemsToSelection("Item 2"); + Assert.assertEquals("6. Selected: [Item 1, Item 2]", getLogRow(0)); + + removeItemsFromSelection("Item 1"); + Assert.assertEquals("7. Selected: [Item 2]", getLogRow(0)); + } + + @Test + public void clickToSelect_notReadOnly() { + selectMenuPath("Component", "State", "Readonly"); + selectMenuPath("Component", "Listeners", "Selection listener"); + + selectItem("Item 4"); + assertNothingSelected(); + + selectMenuPath("Component", "State", "Readonly"); + + selectItem("Item 5"); + Assert.assertEquals("3. Selected: [Item 5]", getLogRow(0)); + + selectItem("Item 1"); + Assert.assertEquals("5. Selected: [Item 1]", getLogRow(0)); + + addItemsToSelection("Item 2"); + Assert.assertEquals("6. Selected: [Item 1, Item 2]", getLogRow(0)); + + removeItemsFromSelection("Item 1"); + Assert.assertEquals("7. Selected: [Item 2]", getLogRow(0)); + } + + @Test + public void itemCaptionProvider() { + selectMenuPath("Component", "Item Generator", + "Use Item Caption Generator"); + assertItems(20, " Caption"); + } + + @Test + public void selectProgramatically() { + selectMenuPath("Component", "Listeners", "Selection listener"); + + selectMenuPath("Component", "Selection", "Toggle Item 5"); + Assert.assertEquals("2. Selected: [Item 5]", getLogRow(0)); + assertSelected("Item 5"); + + selectMenuPath("Component", "Selection", "Toggle Item 1"); + // Selection order (most recently selected is last) + Assert.assertEquals("4. Selected: [Item 5, Item 1]", getLogRow(0)); + // DOM order + assertSelected("Item 1", "Item 5"); + + selectMenuPath("Component", "Selection", "Toggle Item 5"); + Assert.assertEquals("6. Selected: [Item 1]", getLogRow(0)); + assertSelected("Item 1"); + } + + private List getSelectedValues() { + Select select = new Select( + getListSelect().findElement(By.tagName("select"))); + return select.getAllSelectedOptions().stream().map(e -> e.getText()) + .collect(Collectors.toList()); + } + + private void assertSelected(String... expectedSelection) { + Assert.assertEquals(Arrays.asList(expectedSelection), + getSelectedValues()); + } + + @Override + protected Class getUIClass() { + return ListSelectTestUI.class; + } + + protected ListSelectElement getListSelect() { + return $(ListSelectElement.class).first(); + } + + protected void selectItem(String text) { + // phantomjs1 seems to be adding to selection when clicked items, thus + // need to deselect all clicking, which makes this test kind of + // nothing... + Select select = new Select( + getListSelect().findElement(By.tagName("select"))); + select.deselectAll(); + + Optional first = select.getOptions().stream() + .filter(element -> text.equals(element.getText())).findFirst(); + if (first.isPresent()) { + first.get().click(); + } else { + Assert.fail("No element present with text " + text); + } + } + + protected void addItemsToSelection(String... items) { + // acts as multi selection, no need to press modifier key + Stream.of(items).forEach(text -> getListSelect().selectByText(text)); + } + + protected void removeItemsFromSelection(String... items) { + Stream.of(items).forEach(text -> getListSelect().deselectByText(text)); + } + + protected void assertItems(int count) { + assertItems(count, ""); + } + + private void assertNothingSelected() { + Assert.assertEquals(0, getSelectedValues().size()); + } + + protected void assertItems(int count, String suffix) { + int i = 0; + for (String text : getListSelect().getOptions()) { + assertEquals("Item " + i + suffix, text); + i++; + } + assertEquals("Number of items", count, i); + } + + protected void assertItemSuffices(int count) { + int i = 0; + for (String text : getListSelect().getOptions()) { + assertTrue(text.endsWith("Item " + i)); + i++; + } + assertEquals("Number of items", count, i); + } + +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/twincolselect/TwinColSelectTest.java b/uitest/src/test/java/com/vaadin/tests/components/twincolselect/TwinColSelectTest.java index 42c8128d10e..e5f44a405b4 100644 --- a/uitest/src/test/java/com/vaadin/tests/components/twincolselect/TwinColSelectTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/twincolselect/TwinColSelectTest.java @@ -99,10 +99,18 @@ public void clickToSelect() { public void disabled_clickToSelect() { selectMenuPath("Component", "State", "Enabled"); - Assert.assertTrue(getTwinColSelect().findElements(By.tagName("input")) - .stream() + List selects = getTwinColSelect() + .findElements(By.tagName("select")); + Assert.assertEquals(2, selects.size()); + Assert.assertTrue(selects.stream() .allMatch(element -> element.getAttribute("disabled") != null)); + List buttons = getTwinColSelect() + .findElements(By.className("v-button")); + Assert.assertEquals(2, buttons.size()); + buttons.forEach(button -> Assert.assertEquals("v-button v-disabled", + button.getAttribute("className"))); + selectMenuPath("Component", "Listeners", "Selection listener"); String lastLogRow = getLogRow(0); @@ -126,11 +134,25 @@ public void clickToSelect_reenable() { selectMenuPath("Component", "State", "Enabled"); selectMenuPath("Component", "Listeners", "Selection listener"); + List selects = getTwinColSelect() + .findElements(By.tagName("select")); + Assert.assertEquals(2, selects.size()); + Assert.assertTrue(selects.stream() + .allMatch(element -> element.getAttribute("disabled") != null)); + + List buttons = getTwinColSelect() + .findElements(By.className("v-button")); + Assert.assertEquals(2, buttons.size()); + buttons.forEach(button -> Assert.assertEquals("v-button v-disabled", + button.getAttribute("className"))); + selectItems("Item 4"); assertNothingSelected(); selectMenuPath("Component", "State", "Enabled"); + assertElementNotPresent(By.className("v-disabled")); + selectItems("Item 5"); Assert.assertEquals("3. Selected: [Item 5]", getLogRow(0)); assertSelected("Item 5");