From f2d8f812efa067b4baa7e27c0ea76f7596b291e6 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Pekka=20Hyv=C3=B6nen?= Date: Fri, 11 Nov 2016 09:41:43 +0200 Subject: [PATCH] Add MultiSelect support for Grid Still missing following things coming in next patches: - select all checkbox - firing an event when data provider is changed in grid - read only selection models for grid Part 1 for vaadin/framework8-issues#232 Change-Id: Ib2c7c81a838f43cb7c521a56d50139c91961f54a --- .../grid/MultiSelectionModelConnector.java | 101 ++++ .../grid/SingleSelectionModelConnector.java | 15 +- .../widget/grid/events/SelectAllEvent.java | 2 +- .../widget/grid/selection/SelectionEvent.java | 9 - .../grid/selection}/SelectionModel.java | 16 +- .../java/com/vaadin/client/widgets/Grid.java | 82 +-- .../java/com/vaadin/data/SelectionModel.java | 27 +- .../event/selection/MultiSelectionEvent.java | 33 +- server/src/main/java/com/vaadin/ui/Grid.java | 75 ++- .../grid/MultiSelectionModelImpl.java | 277 +++++++++ ...del.java => SingleSelectionModelImpl.java} | 34 +- .../data/GridAsMultiSelectInBinder.java | 270 +++++++++ .../data/GridAsSingleSelectInBinder.java | 14 +- .../grid/GridMultiSelectionModelTest.java | 543 ++++++++++++++++++ .../grid/GridSingleSelectionModelTest.java | 113 +++- .../selection/GridMultiSelectServerRpc.java | 40 +- .../grid/GridCustomSelectionModel.java | 65 +++ .../grid/GridDefaultSelectionMode.java | 23 +- .../grid/GridDisabledMultiselect.java | 14 +- .../grid/GridDragSelectionWhileScrolled.java | 75 +++ .../grid/GridMultiSelectionOnInit.java} | 32 +- .../grid/GridMultiSelectionScrollBar.java | 16 +- .../components/grid/basics/DataObject.java | 4 +- .../components/grid/basics/GridBasics.java | 56 +- .../grid/MySelectionModelConnector.java | 36 +- .../grid/GridCustomSelectionModel.java | 37 -- .../grid/GridDragSelectionWhileScrolled.java | 51 -- .../grid/GridMultiSelectionOnInit.java | 46 -- .../components/grid/GridSelectAllCell.java | 19 - .../grid/GridCustomSelectionModelTest.java | 6 +- .../grid/GridDefaultSelectionModeTest.java | 2 +- .../grid/GridDisabledMultiselectTest.java | 14 +- .../GridDragSelectionWhileScrolledTest.java | 6 +- .../grid/GridMultiSelectionOnInitTest.java | 36 +- .../grid/GridMultiSelectionScrollBarTest.java | 2 +- .../components/grid/GridSelectAllTest.java | 108 ++++ .../components/grid}/GridSelectionTest.java | 216 ++----- .../grid/basics/GridBasicSelectionTest.java | 24 +- .../grid/basics/GridBasicsTest.java | 20 +- .../grid/GridSelectAllCellTest.java | 37 -- 40 files changed, 1954 insertions(+), 642 deletions(-) create mode 100644 client/src/main/java/com/vaadin/client/connectors/grid/MultiSelectionModelConnector.java rename client/src/main/java/com/vaadin/client/{data => widget/grid/selection}/SelectionModel.java (84%) create mode 100644 server/src/main/java/com/vaadin/ui/components/grid/MultiSelectionModelImpl.java rename server/src/main/java/com/vaadin/ui/components/grid/{SingleSelectionModel.java => SingleSelectionModelImpl.java} (89%) create mode 100644 server/src/test/java/com/vaadin/data/GridAsMultiSelectInBinder.java create mode 100644 server/src/test/java/com/vaadin/tests/components/grid/GridMultiSelectionModelTest.java rename uitest/src/test/java/com/vaadin/v7/tests/components/grid/SelectDuringInitTest.java => shared/src/main/java/com/vaadin/shared/data/selection/GridMultiSelectServerRpc.java (50%) create mode 100644 uitest/src/main/java/com/vaadin/tests/components/grid/GridCustomSelectionModel.java rename uitest/src/main/java/com/vaadin/{v7 => }/tests/components/grid/GridDefaultSelectionMode.java (79%) rename uitest/src/main/java/com/vaadin/{v7 => }/tests/components/grid/GridDisabledMultiselect.java (67%) create mode 100644 uitest/src/main/java/com/vaadin/tests/components/grid/GridDragSelectionWhileScrolled.java rename uitest/src/main/java/com/vaadin/{v7/tests/components/grid/SelectDuringInit.java => tests/components/grid/GridMultiSelectionOnInit.java} (50%) rename uitest/src/main/java/com/vaadin/{v7 => }/tests/components/grid/GridMultiSelectionScrollBar.java (66%) rename uitest/src/main/java/com/vaadin/tests/widgetset/client/{v7 => }/grid/MySelectionModelConnector.java (58%) delete mode 100644 uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridCustomSelectionModel.java delete mode 100644 uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridDragSelectionWhileScrolled.java delete mode 100644 uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridMultiSelectionOnInit.java delete mode 100644 uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridSelectAllCell.java rename uitest/src/test/java/com/vaadin/{v7 => }/tests/components/grid/GridCustomSelectionModelTest.java (94%) rename uitest/src/test/java/com/vaadin/{v7 => }/tests/components/grid/GridDefaultSelectionModeTest.java (98%) rename uitest/src/test/java/com/vaadin/{v7 => }/tests/components/grid/GridDisabledMultiselectTest.java (80%) rename uitest/src/test/java/com/vaadin/{v7 => }/tests/components/grid/GridDragSelectionWhileScrolledTest.java (94%) rename uitest/src/test/java/com/vaadin/{v7 => }/tests/components/grid/GridMultiSelectionOnInitTest.java (61%) rename uitest/src/test/java/com/vaadin/{v7 => }/tests/components/grid/GridMultiSelectionScrollBarTest.java (96%) create mode 100644 uitest/src/test/java/com/vaadin/tests/components/grid/GridSelectAllTest.java rename uitest/src/test/java/com/vaadin/{v7/tests/components/grid/basicfeatures/server => tests/components/grid}/GridSelectionTest.java (54%) delete mode 100644 uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridSelectAllCellTest.java diff --git a/client/src/main/java/com/vaadin/client/connectors/grid/MultiSelectionModelConnector.java b/client/src/main/java/com/vaadin/client/connectors/grid/MultiSelectionModelConnector.java new file mode 100644 index 0000000000..7b421be15b --- /dev/null +++ b/client/src/main/java/com/vaadin/client/connectors/grid/MultiSelectionModelConnector.java @@ -0,0 +1,101 @@ +/* + * 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.connectors.grid; + +import com.vaadin.client.ServerConnector; +import com.vaadin.client.extensions.AbstractExtensionConnector; +import com.vaadin.client.renderers.Renderer; +import com.vaadin.client.widget.grid.selection.MultiSelectionRenderer; +import com.vaadin.client.widget.grid.selection.SelectionModel; +import com.vaadin.client.widget.grid.selection.SelectionModelWithSelectionColumn; +import com.vaadin.client.widgets.Grid; +import com.vaadin.shared.data.DataCommunicatorConstants; +import com.vaadin.shared.data.selection.GridMultiSelectServerRpc; +import com.vaadin.shared.ui.Connect; + +import elemental.json.JsonObject; + +/** + * Connector for server side multiselection model implementation. + * + * @author Vaadin Ltd + * + * @since 8.0 + * + */ +@Connect(com.vaadin.ui.components.grid.MultiSelectionModelImpl.class) +public class MultiSelectionModelConnector extends AbstractExtensionConnector { + + /** + * Client side multiselection model implementation. + */ + protected class MultiSelectionModel implements SelectionModel, + SelectionModelWithSelectionColumn { + + @Override + public Renderer getRenderer() { + // this method is only called once when the selection model is set + // to grid + return new MultiSelectionRenderer<>(getGrid()); + } + + @Override + public void select(JsonObject item) { + getRpcProxy(GridMultiSelectServerRpc.class) + .select(item.getString(DataCommunicatorConstants.KEY)); + } + + @Override + public void deselect(JsonObject item) { + // handled by diffstate + getRpcProxy(GridMultiSelectServerRpc.class) + .deselect(item.getString(DataCommunicatorConstants.KEY)); + } + + @Override + public void deselectAll() { + // TODO Will be added in a separate patch + throw new UnsupportedOperationException( + "Deselect all not supported."); + } + + @Override + public boolean isSelected(JsonObject item) { + return SelectionModel.isItemSelected(item); + } + + } + + @Override + protected void extend(ServerConnector target) { + getGrid().setSelectionModel(new MultiSelectionModel()); + } + + @Override + public GridConnector getParent() { + return (GridConnector) super.getParent(); + } + + /** + * Shorthand for fetching the grid this selection model is bound to. + * + * @return the grid + */ + protected Grid getGrid() { + return getParent().getWidget(); + } + +} diff --git a/client/src/main/java/com/vaadin/client/connectors/grid/SingleSelectionModelConnector.java b/client/src/main/java/com/vaadin/client/connectors/grid/SingleSelectionModelConnector.java index 0a30dfa070..33a6d9d9b6 100644 --- a/client/src/main/java/com/vaadin/client/connectors/grid/SingleSelectionModelConnector.java +++ b/client/src/main/java/com/vaadin/client/connectors/grid/SingleSelectionModelConnector.java @@ -15,11 +15,9 @@ */ package com.vaadin.client.connectors.grid; -import java.util.Set; - import com.vaadin.client.ServerConnector; -import com.vaadin.client.data.SelectionModel; import com.vaadin.client.extensions.AbstractExtensionConnector; +import com.vaadin.client.widget.grid.selection.SelectionModel; import com.vaadin.shared.data.DataCommunicatorConstants; import com.vaadin.shared.data.selection.SelectionServerRpc; import com.vaadin.shared.ui.Connect; @@ -33,7 +31,7 @@ import elemental.json.JsonObject; * * @since 8.0 */ -@Connect(com.vaadin.ui.components.grid.SingleSelectionModel.class) +@Connect(com.vaadin.ui.components.grid.SingleSelectionModelImpl.class) public class SingleSelectionModelConnector extends AbstractExtensionConnector { @Override @@ -54,14 +52,13 @@ public class SingleSelectionModelConnector extends AbstractExtensionConnector { } @Override - public Set getSelectedItems() { - throw new UnsupportedOperationException( - "Selected item not known on the client side"); + public boolean isSelected(JsonObject item) { + return SelectionModel.isItemSelected(item); } @Override - public boolean isSelected(JsonObject item) { - return SelectionModel.isItemSelected(item); + public void deselectAll() { + getRpcProxy(SelectionServerRpc.class).select(null); } }); diff --git a/client/src/main/java/com/vaadin/client/widget/grid/events/SelectAllEvent.java b/client/src/main/java/com/vaadin/client/widget/grid/events/SelectAllEvent.java index 2ac2f51e4b..25ce090d6e 100644 --- a/client/src/main/java/com/vaadin/client/widget/grid/events/SelectAllEvent.java +++ b/client/src/main/java/com/vaadin/client/widget/grid/events/SelectAllEvent.java @@ -16,7 +16,7 @@ package com.vaadin.client.widget.grid.events; import com.google.gwt.event.shared.GwtEvent; -import com.vaadin.client.data.SelectionModel; +import com.vaadin.client.widget.grid.selection.SelectionModel; /** * A select all event, fired by the Grid when it needs all rows in data source diff --git a/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionEvent.java b/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionEvent.java index 8219b8998c..707330ad34 100644 --- a/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionEvent.java +++ b/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionEvent.java @@ -136,15 +136,6 @@ public class SelectionEvent extends GwtEvent { return Collections.unmodifiableCollection(removed); } - /** - * Gets currently selected rows. - * - * @return a non-null collection containing all currently selected rows. - */ - public Collection getSelected() { - return grid.getSelectedRows(); - } - /** * Gets a type identifier for this event. * diff --git a/client/src/main/java/com/vaadin/client/data/SelectionModel.java b/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModel.java similarity index 84% rename from client/src/main/java/com/vaadin/client/data/SelectionModel.java rename to client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModel.java index 4079140128..27e099ad46 100644 --- a/client/src/main/java/com/vaadin/client/data/SelectionModel.java +++ b/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModel.java @@ -13,9 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.client.data; - -import java.util.Set; +package com.vaadin.client.widget.grid.selection; import com.vaadin.shared.data.DataCommunicatorConstants; @@ -51,14 +49,6 @@ public interface SelectionModel { */ void deselect(T item); - /** - * Returns a set of the currently selected items. It is safe to invoke other - * {@code SelectionModel} methods while iterating over the set. - * - * @return the items in the current selection, not null - */ - Set getSelectedItems(); - /** * Returns whether the given item is currently selected. * @@ -71,9 +61,7 @@ public interface SelectionModel { /** * Deselects all currently selected items. */ - default void deselectAll() { - getSelectedItems().forEach(this::deselect); - } + void deselectAll(); /** * Gets the selected state from a given grid row json object. This is a diff --git a/client/src/main/java/com/vaadin/client/widgets/Grid.java b/client/src/main/java/com/vaadin/client/widgets/Grid.java index 0cad901ec2..97775aa482 100644 --- a/client/src/main/java/com/vaadin/client/widgets/Grid.java +++ b/client/src/main/java/com/vaadin/client/widgets/Grid.java @@ -83,7 +83,6 @@ import com.vaadin.client.WidgetUtil; import com.vaadin.client.data.DataChangeHandler; import com.vaadin.client.data.DataSource; import com.vaadin.client.data.DataSource.RowHandle; -import com.vaadin.client.data.SelectionModel; import com.vaadin.client.renderers.ComplexRenderer; import com.vaadin.client.renderers.Renderer; import com.vaadin.client.renderers.WidgetRenderer; @@ -160,6 +159,7 @@ import com.vaadin.client.widget.grid.selection.HasSelectionHandlers; import com.vaadin.client.widget.grid.selection.MultiSelectionRenderer; import com.vaadin.client.widget.grid.selection.SelectionEvent; import com.vaadin.client.widget.grid.selection.SelectionHandler; +import com.vaadin.client.widget.grid.selection.SelectionModel; import com.vaadin.client.widget.grid.selection.SelectionModelWithSelectionColumn; import com.vaadin.client.widget.grid.sort.Sort; import com.vaadin.client.widget.grid.sort.SortEvent; @@ -2483,8 +2483,8 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, setStyleName(rowWithFocusStyle, rowFocusStyleName, true); } } else if (rowWithFocusStyle == row.getElement() - || (containerWithFocus != escalator.getBody() - && rowWithFocusStyle != null)) { + || containerWithFocus != escalator.getBody() + && rowWithFocusStyle != null) { // Remove focus style. setStyleName(rowWithFocusStyle, rowFocusStyleName, false); rowWithFocusStyle = null; @@ -2781,9 +2781,9 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, * a range of added rows */ public void rowsAddedToBody(Range added) { - boolean bodyHasFocus = (containerWithFocus == escalator.getBody()); - boolean insertionIsAboveFocusedCell = (added - .getStart() <= rowWithFocus); + boolean bodyHasFocus = containerWithFocus == escalator.getBody(); + boolean insertionIsAboveFocusedCell = added + .getStart() <= rowWithFocus; if (bodyHasFocus && insertionIsAboveFocusedCell) { rowWithFocus += added.length(); rowWithFocus = Math.min(rowWithFocus, @@ -2853,17 +2853,11 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, @Override protected void setDefaultHeaderContent(HeaderCell selectionCell) { - /* - * TODO: Currently the select all check box is shown when multi - * selection is in use. This might result in malfunctions if no - * SelectAllHandlers are present. - * - * Later on this could be fixed so that it check such handlers - * exist. - */ final SelectionModel model = getSelectionModel(); + final boolean shouldSelectAllCheckBoxBeShown = getHandlerCount( + SelectAllEvent.getType()) > 0; - if (selectAllCheckBox == null) { + if (selectAllCheckBox == null && shouldSelectAllCheckBoxBeShown) { selectAllCheckBox = GWT.create(CheckBox.class); selectAllCheckBox.setStylePrimaryName( getStylePrimaryName() + SELECT_ALL_CHECKBOX_CLASSNAME); @@ -2919,7 +2913,8 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, } } }); - } else { + } else if (selectAllCheckBox != null + && !shouldSelectAllCheckBoxBeShown) { for (HeaderRow row : header.getRows()) { if (row.getCell(this) .getType() == GridStaticCellType.WIDGET) { @@ -3300,8 +3295,8 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, setColumnSizes(columnSizes); for (Column column : nonFixedColumns) { - final int expandRatio = (defaultExpandRatios ? 1 - : column.getExpandRatio()); + final int expandRatio = defaultExpandRatios ? 1 + : column.getExpandRatio(); final double maxWidth = getMaxWidth(column); final double newWidth = Math.min(maxWidth, column.getWidthActual()); @@ -3433,8 +3428,8 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, boolean hasAutoWidth = column.getWidth() < 0; if (hasAutoWidth && currentWidth < minWidth) { columnSizes.put(columnIndex, minWidth); - pixelsToRemoveFromOtherColumns += (minWidth - - currentWidth); + pixelsToRemoveFromOtherColumns += minWidth + - currentWidth; minWidthsCausedReflows = true; /* @@ -3853,7 +3848,7 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, .getFirstChildElement(); double height = WidgetUtil .getRequiredHeightBoundingClientRectDouble(firstHeaderCell) - - (WidgetUtil.measureVerticalBorder(getElement()) / 2); + - WidgetUtil.measureVerticalBorder(getElement()) / 2; openCloseButton.setHeight(height + "px"); } @@ -3929,7 +3924,7 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, toggle.setStyleName("hidden", column.isHidden()); } else if (columnToHidingToggleMap.containsKey(column)) { sidebar.menuBar - .removeItem((columnToHidingToggleMap.remove(column))); + .removeItem(columnToHidingToggleMap.remove(column)); } updateTogglesOrder(); } @@ -4333,8 +4328,7 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, final int colspan = header.getRow(eventCell.getRowIndex()) .getCell(eventCell.getColumn()).getColspan(); if (latestColumnDropIndex != draggedColumnIndex - && latestColumnDropIndex != (draggedColumnIndex - + colspan)) { + && latestColumnDropIndex != draggedColumnIndex + colspan) { List> columns = getColumns(); List> reordered = new ArrayList<>(); if (draggedColumnIndex < latestColumnDropIndex) { @@ -4523,7 +4517,7 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, } } - if (leftBound == (rightBound - 1)) { + if (leftBound == rightBound - 1) { return; } @@ -5404,7 +5398,7 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, setStyleName(rowElement, rowHasDataStyleName, hasData); } - boolean isEvenIndex = (row.getRow() % 2 == 0); + boolean isEvenIndex = row.getRow() % 2 == 0; setStyleName(rowElement, rowStripeStyleName, !isEvenIndex); rowReference.set(rowIndex, rowData, rowElement); @@ -5869,11 +5863,6 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, setSelectionModel(new SelectionModel() { - @Override - public Set getSelectedItems() { - return Collections.emptySet(); - } - @Override public void select(T item) { } @@ -5887,6 +5876,10 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, return false; } + @Override + public void deselectAll() { + } + }); escalator.getBody().setSpacerUpdater(gridSpacerUpdater); @@ -7657,32 +7650,6 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, getSelectionModel().deselectAll(); } - /** - * Gets last selected row from the current SelectionModel. - *

- * Only selection models implementing {@link SelectionModel.Single} are - * valid for this method; for anything else, use the - * {@link Grid#getSelectedRows()} method. - * - * @return a selected row reference, or null, if no row is selected - * @throws IllegalStateException - * if the current selection model is not an instance of - * {@link SelectionModel.Single} - */ - public T getSelectedRow() { - return getSelectionModel().getSelectedItems().stream().findFirst() - .orElse(null); - } - - /** - * Gets currently selected rows from the current selection model. - * - * @return a non-null collection containing all currently selected rows. - */ - public Collection getSelectedRows() { - return getSelectionModel().getSelectedItems(); - } - @Override public HandlerRegistration addSelectionHandler( final SelectionHandler handler) { @@ -8877,4 +8844,5 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, } return null; } + } diff --git a/server/src/main/java/com/vaadin/data/SelectionModel.java b/server/src/main/java/com/vaadin/data/SelectionModel.java index 98a068993a..cf4fb846b6 100644 --- a/server/src/main/java/com/vaadin/data/SelectionModel.java +++ b/server/src/main/java/com/vaadin/data/SelectionModel.java @@ -72,10 +72,15 @@ public interface SelectionModel extends Serializable { if (item != null) { select(item); } else { - deselectAll(); + getSelectedItem().ifPresent(this::deselect); } } + @Override + public default void deselectAll() { + setSelectedItem(null); + } + /** * Returns a singleton set of the currently selected item or an empty * set if no item is selected. @@ -145,6 +150,12 @@ public interface SelectionModel extends Serializable { Collections.emptySet()); } + @SuppressWarnings("unchecked") + @Override + public default void deselect(T item) { + deselectItems(item); + } + /** * Removes the given items from the set of currently selected items. *

@@ -183,6 +194,14 @@ public interface SelectionModel extends Serializable { default Optional getFirstSelectedItem() { return getSelectedItems().stream().findFirst(); } + + /** + * Deselects all currently selected items. + */ + @Override + public default void deselectAll() { + updateSelection(Collections.emptySet(), getSelectedItems()); + } } /** @@ -227,11 +246,9 @@ public interface SelectionModel extends Serializable { public void deselect(T item); /** - * Deselects all currently selected items. + * Deselects all currently selected items, if any. */ - public default void deselectAll() { - getSelectedItems().forEach(this::deselect); - } + public void deselectAll(); /** * Returns whether the given item is currently selected. diff --git a/server/src/main/java/com/vaadin/event/selection/MultiSelectionEvent.java b/server/src/main/java/com/vaadin/event/selection/MultiSelectionEvent.java index 5fb91bbcbe..7718b5a1a6 100644 --- a/server/src/main/java/com/vaadin/event/selection/MultiSelectionEvent.java +++ b/server/src/main/java/com/vaadin/event/selection/MultiSelectionEvent.java @@ -21,6 +21,8 @@ import java.util.Set; import com.vaadin.data.HasValue.ValueChangeEvent; import com.vaadin.ui.AbstractMultiSelect; +import com.vaadin.ui.Component; +import com.vaadin.ui.MultiSelect; /** * Event fired when the the selection changes in a @@ -55,13 +57,32 @@ public class MultiSelectionEvent extends ValueChangeEvent> this.oldSelection = oldSelection; } + /** + * Creates a new selection change event in a multiselect component. + * + * @param component + * the component + * @param source + * the multiselect source + * @param oldSelection + * the old set of selected items + * @param userOriginated + * {@code true} if this event originates from the client, + * {@code false} otherwise. + */ + public MultiSelectionEvent(Component component, MultiSelect source, + Set oldSelection, boolean userOriginated) { + super(component, source, userOriginated); + this.oldSelection = oldSelection; + } + /** * Gets the new selection. *

* The result is the current selection of the source * {@link AbstractMultiSelect} object. So it's always exactly the same as * {@link AbstractMultiSelect#getValue()} - * + * * @see #getValue() * * @return a set of items selected after the selection was changed @@ -83,4 +104,14 @@ public class MultiSelectionEvent extends ValueChangeEvent> public Optional getFirstSelected() { return getValue().stream().findFirst(); } + + /** + * The multiselect on which the Event initially occurred. + * + * @return the multiselect on which the Event initially occurred. + */ + @Override + public MultiSelect getSource() { + return (MultiSelect) super.getSource(); + } } diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java index e6830f3b95..5010387377 100644 --- a/server/src/main/java/com/vaadin/ui/Grid.java +++ b/server/src/main/java/com/vaadin/ui/Grid.java @@ -72,7 +72,7 @@ import com.vaadin.ui.components.grid.EditorImpl; import com.vaadin.ui.components.grid.Footer; import com.vaadin.ui.components.grid.Header; import com.vaadin.ui.components.grid.Header.Row; -import com.vaadin.ui.components.grid.SingleSelectionModel; +import com.vaadin.ui.components.grid.SingleSelectionModelImpl; import com.vaadin.ui.declarative.DesignContext; import com.vaadin.ui.renderers.AbstractRenderer; import com.vaadin.ui.renderers.Renderer; @@ -133,9 +133,12 @@ public class Grid extends AbstractListing /** * The server-side interface that controls Grid's selection state. * SelectionModel should extend {@link AbstractGridExtension}. + *

* * @param * the grid bean type + * @see SingleSelectionModel + * @see MultiSelectionModel */ public interface GridSelectionModel extends SelectionModel, Extension { @@ -151,6 +154,42 @@ public class Grid extends AbstractListing void remove(); } + /** + * Single selection model interface for Grid. + * + * @param + * the type of items in grid + */ + public interface SingleSelectionModel extends GridSelectionModel, + com.vaadin.data.SelectionModel.Single { + + /** + * Gets a wrapper to use this single selection model as a single select + * in {@link Binder}. + * + * @return the single select wrapper + */ + SingleSelect asSingleSelect(); + } + + /** + * Multiselection model interface for Grid. + * + * @param + * the type of items in grid + */ + public interface MultiSelectionModel extends GridSelectionModel, + com.vaadin.data.SelectionModel.Multi { + + /** + * Gets a wrapper to use this multiselection model as a multiselect in + * {@link Binder}. + * + * @return the multiselect wrapper + */ + MultiSelect asMultiSelect(); + } + /** * An event listener for column resize events in the Grid. */ @@ -2030,7 +2069,7 @@ public class Grid extends AbstractListing setDefaultHeaderRow(appendHeaderRow()); - selectionModel = new SingleSelectionModel<>(this); + selectionModel = new SingleSelectionModelImpl<>(this); detailsManager = new DetailsManager<>(); addExtension(detailsManager); @@ -2896,15 +2935,18 @@ public class Grid extends AbstractListing /** * Use this grid as a single select in {@link Binder}. *

- * Sets the grid to single select mode, if not yet so. + * Throws {@link IllegalStateException} if the grid is not using a + * {@link SingleSelectionModel}. * * @return the single select wrapper that can be used in binder + * @throws IllegalStateException + * if not using a single selection model */ public SingleSelect asSingleSelect() { GridSelectionModel model = getSelectionModel(); if (!(model instanceof SingleSelectionModel)) { - model = new SingleSelectionModel<>(this); - setSelectionModel(model); + throw new IllegalStateException( + "Grid is not in single select mode, it needs to be explicitly set to such with setSelectionModel(SingleSelectionModel) before being able to use single selection features."); } return ((SingleSelectionModel) model).asSingleSelect(); @@ -2914,15 +2956,34 @@ public class Grid extends AbstractListing return editor; } + /** + * User this grid as a multiselect in {@link Binder}. + *

+ * Throws {@link IllegalStateException} if the grid is not using a + * {@link MultiSelectionModel}. + * + * @return the multiselect wrapper that can be used in binder + * @throws IllegalStateException + * if not using a multiselection model + */ + public MultiSelect asMultiSelect() { + GridSelectionModel model = getSelectionModel(); + if (!(model instanceof MultiSelectionModel)) { + throw new IllegalStateException( + "Grid is not in multiselect mode, it needs to be explicitly set to such with setSelectionModel(MultiSelectionModel) before being able to use multiselection features."); + } + return ((MultiSelectionModel) model).asMultiSelect(); + } + /** * Sets the selection model for this listing. *

- * The default selection model is {@link SingleSelectionModel}. + * The default selection model is {@link SingleSelectionModelImpl}. * * @param model * the selection model to use, not {@code null} */ - protected void setSelectionModel(GridSelectionModel model) { + public void setSelectionModel(GridSelectionModel model) { Objects.requireNonNull(model, "selection model cannot be null"); selectionModel.remove(); selectionModel = model; diff --git a/server/src/main/java/com/vaadin/ui/components/grid/MultiSelectionModelImpl.java b/server/src/main/java/com/vaadin/ui/components/grid/MultiSelectionModelImpl.java new file mode 100644 index 0000000000..286edd5f2b --- /dev/null +++ b/server/src/main/java/com/vaadin/ui/components/grid/MultiSelectionModelImpl.java @@ -0,0 +1,277 @@ +/* + * 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.components.grid; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Objects; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import com.vaadin.event.selection.MultiSelectionEvent; +import com.vaadin.event.selection.MultiSelectionListener; +import com.vaadin.shared.Registration; +import com.vaadin.shared.data.DataCommunicatorConstants; +import com.vaadin.shared.data.selection.GridMultiSelectServerRpc; +import com.vaadin.ui.Grid; +import com.vaadin.ui.Grid.AbstractGridExtension; +import com.vaadin.ui.Grid.MultiSelectionModel; +import com.vaadin.ui.MultiSelect; +import com.vaadin.util.ReflectTools; + +import elemental.json.JsonObject; + +/** + * Multiselection model for grid. + *

+ * Shows a column of checkboxes as the first column of grid. Each checkbox + * triggers the selection for that row. + * + * @author Vaadin Ltd. + * @since 8.0 + * + * @param + * the type of the selected item in grid. + */ +public class MultiSelectionModelImpl extends AbstractGridExtension + implements MultiSelectionModel { + + private class GridMultiSelectServerRpcImpl + implements GridMultiSelectServerRpc { + + @Override + public void select(String key) { + MultiSelectionModelImpl.this.updateSelection( + new LinkedHashSet<>(Arrays.asList(getData(key))), + Collections.emptySet(), true); + } + + @Override + public void deselect(String key) { + MultiSelectionModelImpl.this.updateSelection(Collections.emptySet(), + new LinkedHashSet<>(Arrays.asList(getData(key))), true); + } + + @Override + public void selectAll() { + // TODO will be added in another patch + throw new UnsupportedOperationException("Select all not supported"); + } + + @Override + public void deselectAll() { + // TODO will be added in another patch + throw new UnsupportedOperationException( + "Deelect all not supported"); + } + } + + @Deprecated + private static final Method SELECTION_CHANGE_METHOD = ReflectTools + .findMethod(MultiSelectionListener.class, "accept", + MultiSelectionEvent.class); + + private final Grid grid; + + private Set selection = new LinkedHashSet<>(); + + /** + * Constructs a new multiselection model for the given grid. + * + * @param grid + * the grid to bind the selection model into + */ + public MultiSelectionModelImpl(Grid grid) { + this.grid = grid; + extend(grid); + + registerRpc(new GridMultiSelectServerRpcImpl()); + } + + @Override + public void remove() { + updateSelection(Collections.emptySet(), getSelectedItems(), false); + + super.remove(); + } + + /** + * Adds a selection listener that will be called when the selection is + * changed either by the user or programmatically. + * + * @param listener + * the value change listener, not {@code null} + * @return a registration for the listener + */ + public Registration addSelectionListener( + MultiSelectionListener listener) { + addListener(MultiSelectionEvent.class, listener, + SELECTION_CHANGE_METHOD); + return () -> removeListener(MultiSelectionEvent.class, listener); + } + + @Override + public void generateData(T item, JsonObject jsonObject) { + // in case of all items selected, don't write individual items as + // seleted + if (isSelected(item)) { + jsonObject.put(DataCommunicatorConstants.SELECTED, true); + } + } + + @Override + public Set getSelectedItems() { + return Collections.unmodifiableSet(new LinkedHashSet<>(selection)); + } + + @Override + public void updateSelection(Set addedItems, Set removedItems) { + updateSelection(addedItems, removedItems, false); + } + + /** + * Gets a wrapper for using this grid as a multiselect in a binder. + * + * @return a multiselect wrapper for grid + */ + @Override + public MultiSelect asMultiSelect() { + return new MultiSelect() { + + @Override + public void setValue(Set value) { + Objects.requireNonNull(value); + Set copy = value.stream().map(Objects::requireNonNull) + .collect(Collectors.toCollection(LinkedHashSet::new)); + + updateSelection(copy, new LinkedHashSet<>(getSelectedItems())); + } + + @Override + public Set getValue() { + return getSelectedItems(); + } + + @Override + public Registration addValueChangeListener( + com.vaadin.data.HasValue.ValueChangeListener> listener) { + return addSelectionListener(event -> listener.accept(event)); + } + + @Override + public void setRequiredIndicatorVisible( + boolean requiredIndicatorVisible) { + // TODO support required indicator for grid ? + throw new UnsupportedOperationException( + "Required indicator is not supported in grid."); + } + + @Override + public boolean isRequiredIndicatorVisible() { + // TODO support required indicator for grid ? + throw new UnsupportedOperationException( + "Required indicator is not supported in grid."); + } + + @Override + public void setReadOnly(boolean readOnly) { + // TODO support read only in grid ? + throw new UnsupportedOperationException( + "Read only mode is not supported for grid."); + } + + @Override + public boolean isReadOnly() { + // TODO support read only in grid ? + throw new UnsupportedOperationException( + "Read only mode is not supported for grid."); + } + + @Override + public void updateSelection(Set addedItems, + Set removedItems) { + MultiSelectionModelImpl.this.updateSelection(addedItems, + removedItems); + } + + @Override + public Set getSelectedItems() { + return MultiSelectionModelImpl.this.getSelectedItems(); + } + + @Override + public Registration addSelectionListener( + MultiSelectionListener listener) { + return MultiSelectionModelImpl.this + .addSelectionListener(listener); + } + }; + } + + /** + * Updates the selection by adding and removing the given items. + *

+ * All selection updates should go through this method, since it handles + * incorrect parameters, removing duplicates, notifying data communicator + * and and firing events. + * + * @param addedItems + * the items added to selection, not {@code} null + * @param removedItems + * the items removed from selection, not {@code} null + * @param userOriginated + * {@code true} if this was used originated, {@code false} if not + */ + protected void updateSelection(Set addedItems, Set removedItems, + boolean userOriginated) { + Objects.requireNonNull(addedItems); + Objects.requireNonNull(removedItems); + + // if there are duplicates, some item is both added & removed, just + // discard that and leave things as was before + addedItems.removeIf(item -> removedItems.remove(item)); + + if (selection.containsAll(addedItems) + && Collections.disjoint(selection, removedItems)) { + return; + } + + doUpdateSelection(set -> { + // order of add / remove does not matter since no duplicates + set.removeAll(removedItems); + set.addAll(addedItems); + removedItems.forEach(grid.getDataCommunicator()::refresh); + addedItems.forEach(grid.getDataCommunicator()::refresh); + }, userOriginated); + } + + private void doUpdateSelection(Consumer> handler, + boolean userOriginated) { + if (getParent() == null) { + throw new IllegalStateException( + "Trying to update selection for grid selection model that has been detached from the grid."); + } + + LinkedHashSet oldSelection = new LinkedHashSet<>(selection); + handler.accept(selection); + + fireEvent(new MultiSelectionEvent<>(grid, asMultiSelect(), oldSelection, + userOriginated)); + } +} diff --git a/server/src/main/java/com/vaadin/ui/components/grid/SingleSelectionModel.java b/server/src/main/java/com/vaadin/ui/components/grid/SingleSelectionModelImpl.java similarity index 89% rename from server/src/main/java/com/vaadin/ui/components/grid/SingleSelectionModel.java rename to server/src/main/java/com/vaadin/ui/components/grid/SingleSelectionModelImpl.java index d095504768..e68487f358 100644 --- a/server/src/main/java/com/vaadin/ui/components/grid/SingleSelectionModel.java +++ b/server/src/main/java/com/vaadin/ui/components/grid/SingleSelectionModelImpl.java @@ -23,7 +23,6 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; -import com.vaadin.data.SelectionModel; import com.vaadin.event.selection.SingleSelectionEvent; import com.vaadin.event.selection.SingleSelectionListener; import com.vaadin.shared.Registration; @@ -32,7 +31,7 @@ import com.vaadin.shared.data.selection.SelectionServerRpc; import com.vaadin.ui.Component; import com.vaadin.ui.Grid; import com.vaadin.ui.Grid.AbstractGridExtension; -import com.vaadin.ui.Grid.GridSelectionModel; +import com.vaadin.ui.Grid.SingleSelectionModel; import com.vaadin.ui.SingleSelect; import com.vaadin.util.ReflectTools; @@ -47,8 +46,8 @@ import elemental.json.JsonObject; * @param * the type of the selected item in grid. */ -public class SingleSelectionModel extends AbstractGridExtension - implements GridSelectionModel, SelectionModel.Single { +public class SingleSelectionModelImpl extends AbstractGridExtension + implements SingleSelectionModel { private static final Method SELECTION_CHANGE_METHOD = ReflectTools .findMethod(SingleSelectionListener.class, "accept", @@ -63,7 +62,7 @@ public class SingleSelectionModel extends AbstractGridExtension * @param grid * the grid to bind the selection model into */ - public SingleSelectionModel(Grid grid) { + public SingleSelectionModelImpl(Grid grid) { this.grid = grid; extend(grid); registerRpc(new SelectionServerRpc() { @@ -83,15 +82,14 @@ public class SingleSelectionModel extends AbstractGridExtension } /** - * Adds a selection change listener to this select. The listener is called - * when the value of this select is changed either by the user or - * programmatically. + * Adds a selection listener to this select. The listener is called when the + * value of this select is changed either by the user or programmatically. * * @param listener * the value change listener, not null * @return a registration for the listener */ - public Registration addSelectionChangeListener( + public Registration addSelectionListener( SingleSelectionListener listener) { return addListener(SingleSelectionEvent.class, listener, SELECTION_CHANGE_METHOD); @@ -148,6 +146,11 @@ public class SingleSelectionModel extends AbstractGridExtension * selection */ protected void doSetSelectedKey(String key) { + if (getParent() == null) { + throw new IllegalStateException( + "Trying to update selection for grid selection model that has been detached from the grid."); + } + if (selectedItem != null) { grid.getDataCommunicator().refresh(selectedItem); } @@ -233,8 +236,7 @@ public class SingleSelectionModel extends AbstractGridExtension // when selection model changes, firing an event for selection change // event fired before removing so that parent is still intact (in case // needed) - selectedItem = null; - fireEvent(new SingleSelectionEvent<>(grid, asSingleSelect(), false)); + setSelectedFromServer(null); super.remove(); } @@ -244,24 +246,26 @@ public class SingleSelectionModel extends AbstractGridExtension * * @return a single select wrapper for grid */ + @Override public SingleSelect asSingleSelect() { return new SingleSelect() { @Override public void setValue(T value) { - SingleSelectionModel.this.setSelectedFromServer(value); + SingleSelectionModelImpl.this.setSelectedFromServer(value); } @Override public T getValue() { - return SingleSelectionModel.this.getSelectedItem().orElse(null); + return SingleSelectionModelImpl.this.getSelectedItem() + .orElse(null); } @Override public Registration addValueChangeListener( com.vaadin.data.HasValue.ValueChangeListener listener) { - return SingleSelectionModel.this.addSelectionChangeListener( - event -> listener.accept(event)); + return SingleSelectionModelImpl.this + .addSelectionListener(event -> listener.accept(event)); } @Override diff --git a/server/src/test/java/com/vaadin/data/GridAsMultiSelectInBinder.java b/server/src/test/java/com/vaadin/data/GridAsMultiSelectInBinder.java new file mode 100644 index 0000000000..e31b2ab188 --- /dev/null +++ b/server/src/test/java/com/vaadin/data/GridAsMultiSelectInBinder.java @@ -0,0 +1,270 @@ +package com.vaadin.data; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.util.converter.Converter; +import com.vaadin.data.util.converter.ValueContext; +import com.vaadin.tests.data.bean.BeanWithEnums; +import com.vaadin.tests.data.bean.Sex; +import com.vaadin.tests.data.bean.TestEnum; +import com.vaadin.ui.Grid; +import com.vaadin.ui.MultiSelect; +import com.vaadin.ui.components.grid.MultiSelectionModelImpl; +import com.vaadin.ui.components.grid.SingleSelectionModelImpl; + +public class GridAsMultiSelectInBinder + extends BinderTestBase, BeanWithEnums> { + public class TestEnumSetToStringConverter + implements Converter, String> { + @Override + public Result convertToModel(Set value, + ValueContext context) { + return Result.ok(value.stream().map(TestEnum::name) + .collect(Collectors.joining(","))); + } + + @Override + public Set convertToPresentation(String value, + ValueContext context) { + return Stream.of(value.split(",")) + .filter(string -> !string.isEmpty()).map(TestEnum::valueOf) + .collect(Collectors.toSet()); + } + } + + private class CustomMultiSelectModel extends MultiSelectionModelImpl { + + public CustomMultiSelectModel(Grid grid) { + super(grid); + } + + @Override + public void updateSelection(Set addedItems, Set removedItems, + boolean userOriginated) { + super.updateSelection(addedItems, removedItems, userOriginated); + } + + } + + private Binder> converterBinder = new Binder<>(); + private Grid grid; + private MultiSelect select; + + @Before + public void setUp() { + binder = new Binder<>(); + item = new BeanWithEnums(); + grid = new Grid<>(); + grid.setItems(TestEnum.values()); + grid.setSelectionModel(new MultiSelectionModelImpl<>(grid)); + select = grid.asMultiSelect(); + + converterBinder.forField(select) + .withConverter(new TestEnumSetToStringConverter()) + .bind(AtomicReference::get, AtomicReference::set); + } + + @Test(expected = IllegalStateException.class) + public void boundGridInBinder_selectionModelChanged_throws() { + grid.setSelectionModel(new SingleSelectionModelImpl<>(grid)); + + select.select(TestEnum.ONE); + } + + @Test + public void beanBound_bindSelectByShortcut_selectionUpdated() { + item.setEnums(Collections.singleton(TestEnum.ONE)); + binder.setBean(item); + binder.bind(select, BeanWithEnums::getEnums, BeanWithEnums::setEnums); + + assertEquals(Collections.singleton(TestEnum.ONE), + select.getSelectedItems()); + } + + @Test + public void beanBound_bindSelect_selectionUpdated() { + item.setEnums(Collections.singleton(TestEnum.TWO)); + binder.setBean(item); + binder.forField(select).bind(BeanWithEnums::getEnums, + BeanWithEnums::setEnums); + + assertEquals(Collections.singleton(TestEnum.TWO), + select.getSelectedItems()); + } + + @Test + public void selectBound_bindBeanWithoutEnums_selectedItemNotPresent() { + bindEnum(); + + assertTrue(select.getSelectedItems().isEmpty()); + } + + @Test + public void selectBound_bindBean_selectionUpdated() { + item.setEnums(Collections.singleton(TestEnum.ONE)); + bindEnum(); + + assertEquals(Collections.singleton(TestEnum.ONE), + select.getSelectedItems()); + } + + @Test + public void bound_setSelection_beanValueUpdated() { + bindEnum(); + + select.select(TestEnum.TWO); + + assertEquals(Collections.singleton(TestEnum.TWO), item.getEnums()); + } + + @Test + public void bound_setSelection_beanValueIsACopy() { + bindEnum(); + + select.select(TestEnum.TWO); + + Set enums = item.getEnums(); + + binder.setBean(new BeanWithEnums()); + select.select(TestEnum.ONE); + + assertEquals(Collections.singleton(TestEnum.TWO), enums); + } + + @Test + public void bound_deselect_beanValueUpdatedToNull() { + item.setEnums(Collections.singleton(TestEnum.ONE)); + bindEnum(); + + select.deselect(TestEnum.ONE); + + assertTrue(item.getEnums().isEmpty()); + } + + @Test + public void unbound_changeSelection_beanValueNotUpdated() { + item.setEnums(Collections.singleton(TestEnum.ONE)); + bindEnum(); + binder.removeBean(); + + select.select(TestEnum.TWO); + + assertEquals(Collections.singleton(TestEnum.ONE), item.getEnums()); + } + + @Test + public void withConverter_load_selectUpdated() { + converterBinder.readBean(new AtomicReference<>("TWO")); + + assertEquals(Collections.singleton(TestEnum.TWO), + select.getSelectedItems()); + } + + @Test + public void withConverter_save_referenceUpdated() { + select.select(TestEnum.ONE); + select.select(TestEnum.TWO); + + AtomicReference reference = new AtomicReference<>(""); + converterBinder.writeBeanIfValid(reference); + + assertEquals("ONE,TWO", reference.get()); + } + + @Test + public void withValidator_validate_validatorUsed() { + binder.forField(select) + .withValidator(selection -> selection.size() % 2 == 1, + "Must select odd number of items") + .bind(BeanWithEnums::getEnums, BeanWithEnums::setEnums); + binder.setBean(item); + + assertFalse(binder.validate().isOk()); + + select.select(TestEnum.TWO); + + assertTrue(binder.validate().isOk()); + } + + @Test + public void addValueChangeListener_selectionUpdated_eventTriggeredForMultiSelect() { + Grid grid = new Grid<>(); + CustomMultiSelectModel model = new CustomMultiSelectModel(grid); + grid.setSelectionModel(model); + grid.setItems(Sex.values()); + MultiSelect select = grid.asMultiSelect(); + + List selected = new ArrayList<>(); + List userOriginated = new ArrayList<>(); + select.addValueChangeListener(event -> { + selected.addAll(event.getValue()); + userOriginated.add(event.isUserOriginated()); + assertSame(grid, event.getComponent()); + // cannot compare that the event source is the select since a new + // MultiSelect wrapper object has been created for the event + + assertEquals(select.getValue(), event.getValue()); + }); + + select.select(Sex.UNKNOWN); + + assertEquals(Arrays.asList(Sex.UNKNOWN), selected); + + model.updateSelection(new LinkedHashSet<>(Arrays.asList(Sex.MALE)), + Collections.emptySet(), true); // simulate client side selection + assertEquals(Arrays.asList(Sex.UNKNOWN, Sex.UNKNOWN, Sex.MALE), + selected); + selected.clear(); + + select.select(Sex.MALE); // NOOP + assertEquals(Arrays.asList(), selected); + selected.clear(); + + model.updateSelection(Collections.emptySet(), + new LinkedHashSet<>(Arrays.asList(Sex.UNKNOWN)), true); // client + // side + // deselect + assertEquals(Arrays.asList(Sex.MALE), selected); + selected.clear(); + + select.deselect(Sex.UNKNOWN); // NOOP + assertEquals(Arrays.asList(), selected); + selected.clear(); + + select.deselect(Sex.FEMALE, Sex.MALE); // partly NOOP + assertEquals(Arrays.asList(), selected); + + model.selectItems(Sex.FEMALE, Sex.MALE); + assertEquals(Arrays.asList(Sex.FEMALE, Sex.MALE), selected); + selected.clear(); + + model.updateSelection(new LinkedHashSet<>(Arrays.asList(Sex.FEMALE)), + Collections.emptySet(), true); // client side NOOP + assertEquals(Arrays.asList(), selected); + + assertEquals(Arrays.asList(false, true, true, false, false), + userOriginated); + } + + protected void bindEnum() { + binder.forField(select).bind(BeanWithEnums::getEnums, + BeanWithEnums::setEnums); + binder.setBean(item); + } +} diff --git a/server/src/test/java/com/vaadin/data/GridAsSingleSelectInBinder.java b/server/src/test/java/com/vaadin/data/GridAsSingleSelectInBinder.java index 7b34c78f25..cf649384e0 100644 --- a/server/src/test/java/com/vaadin/data/GridAsSingleSelectInBinder.java +++ b/server/src/test/java/com/vaadin/data/GridAsSingleSelectInBinder.java @@ -16,7 +16,8 @@ import com.vaadin.tests.data.bean.Person; import com.vaadin.tests.data.bean.Sex; import com.vaadin.ui.Grid; import com.vaadin.ui.SingleSelect; -import com.vaadin.ui.components.grid.SingleSelectionModel; +import com.vaadin.ui.components.grid.MultiSelectionModelImpl; +import com.vaadin.ui.components.grid.SingleSelectionModelImpl; public class GridAsSingleSelectInBinder extends BinderTestBase, Person> { @@ -29,7 +30,7 @@ public class GridAsSingleSelectInBinder } } - private class CustomSingleSelectModel extends SingleSelectionModel { + private class CustomSingleSelectModel extends SingleSelectionModelImpl { public CustomSingleSelectModel(Grid grid) { super(grid); @@ -52,6 +53,13 @@ public class GridAsSingleSelectInBinder select = grid.asSingleSelect(); } + @Test(expected = IllegalStateException.class) + public void boundGridInBinder_selectionModelChanged_throws() { + grid.setSelectionModel(new MultiSelectionModelImpl<>(grid)); + + select.setValue(Sex.MALE); + } + @Test public void personBound_bindSelectByShortcut_selectionUpdated() { item.setSex(Sex.FEMALE); @@ -117,8 +125,6 @@ public class GridAsSingleSelectInBinder @Test public void addValueChangeListener_selectionUpdated_eventTriggeredForSelect() { - binder = new Binder<>(); - item = new Person(); GridWithCustomSingleSelectionModel grid = new GridWithCustomSingleSelectionModel(); CustomSingleSelectModel model = new CustomSingleSelectModel(grid); grid.setSelectionModel(model); diff --git a/server/src/test/java/com/vaadin/tests/components/grid/GridMultiSelectionModelTest.java b/server/src/test/java/com/vaadin/tests/components/grid/GridMultiSelectionModelTest.java new file mode 100644 index 0000000000..d7a087be54 --- /dev/null +++ b/server/src/test/java/com/vaadin/tests/components/grid/GridMultiSelectionModelTest.java @@ -0,0 +1,543 @@ +package com.vaadin.tests.components.grid; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import org.easymock.Capture; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import com.vaadin.data.HasValue.ValueChangeEvent; +import com.vaadin.event.selection.SingleSelectionEvent; +import com.vaadin.event.selection.SingleSelectionListener; +import com.vaadin.server.data.provider.bov.Person; +import com.vaadin.shared.Registration; +import com.vaadin.ui.Grid; +import com.vaadin.ui.Grid.GridSelectionModel; +import com.vaadin.ui.components.grid.MultiSelectionModelImpl; +import com.vaadin.ui.components.grid.SingleSelectionModelImpl; + +import elemental.json.JsonObject; + +public class GridMultiSelectionModelTest { + + public static final Person PERSON_C = new Person("c", 3); + public static final Person PERSON_B = new Person("b", 2); + public static final Person PERSON_A = new Person("a", 1); + + private Grid grid; + private MultiSelectionModelImpl selectionModel; + private Capture> currentSelectionCapture; + private Capture> oldSelectionCapture; + private AtomicInteger events; + + public static class CustomMultiSelectionModel + extends MultiSelectionModelImpl { + public final Map generatedData = new LinkedHashMap<>(); + + public CustomMultiSelectionModel(Grid grid) { + super(grid); + } + + @Override + public void generateData(String item, JsonObject jsonObject) { + super.generateData(item, jsonObject); + // capture updated row + generatedData.put(item, isSelected(item)); + } + + } + + @Before + public void setUp() { + grid = new Grid<>(); + selectionModel = new MultiSelectionModelImpl<>(grid); + grid.setSelectionModel(selectionModel); + grid.setItems(PERSON_A, PERSON_B, PERSON_C); + + currentSelectionCapture = new Capture<>(); + oldSelectionCapture = new Capture<>(); + events = new AtomicInteger(); + + selectionModel.addSelectionListener(event -> { + currentSelectionCapture + .setValue(new ArrayList<>(event.getNewSelection())); + oldSelectionCapture + .setValue(new ArrayList<>(event.getOldSelection())); + events.incrementAndGet(); + }); + } + + @Test(expected = IllegalStateException.class) + public void selectionModelChanged_usingPreviousSelectionModel_throws() { + grid.setSelectionModel(new SingleSelectionModelImpl<>(grid)); + + selectionModel.select(PERSON_A); + } + + @Test + public void changingSelectionModel_firesSelectionEvent() { + Grid customGrid = new Grid<>(); + customGrid.setSelectionModel(new MultiSelectionModelImpl<>(customGrid)); + customGrid.setItems("Foo", "Bar", "Baz"); + + List selectionChanges = new ArrayList<>(); + Capture> oldSelectionCapture = new Capture<>(); + ((MultiSelectionModelImpl) customGrid.getSelectionModel()) + .addSelectionListener(e -> { + selectionChanges.addAll(e.getValue()); + oldSelectionCapture + .setValue(new ArrayList<>(e.getOldSelection())); + }); + + customGrid.getSelectionModel().select("Foo"); + assertEquals(Arrays.asList("Foo"), selectionChanges); + selectionChanges.clear(); + + customGrid.getSelectionModel().select("Bar"); + assertEquals("Foo", + customGrid.getSelectionModel().getFirstSelectedItem().get()); + assertEquals(Arrays.asList("Foo", "Bar"), selectionChanges); + selectionChanges.clear(); + + customGrid.setSelectionModel(new SingleSelectionModelImpl<>(customGrid)); + assertFalse(customGrid.getSelectionModel().getFirstSelectedItem() + .isPresent()); + assertEquals(Arrays.asList(), selectionChanges); + assertEquals(Arrays.asList("Foo", "Bar"), + oldSelectionCapture.getValue()); + } + + @Test + public void serverSideSelection_GridChangingSelectionModel_sendsUpdatedRowsToClient() { + Grid customGrid = new Grid<>(); + customGrid.setItems("Foo", "Bar", "Baz"); + + CustomMultiSelectionModel customModel = new CustomMultiSelectionModel( + customGrid); + customGrid.setSelectionModel(customModel); + customGrid.getDataCommunicator().beforeClientResponse(true); + + Assert.assertFalse("Item should have been updated as selected", + customModel.generatedData.get("Foo")); + Assert.assertFalse("Item should have been updated as NOT selected", + customModel.generatedData.get("Bar")); + Assert.assertFalse("Item should have been updated as NOT selected", + customModel.generatedData.get("Baz")); + + customModel.generatedData.clear(); + + customGrid.getSelectionModel().select("Foo"); + customGrid.getDataCommunicator().beforeClientResponse(false); + + Assert.assertTrue("Item should have been updated as selected", + customModel.generatedData.get("Foo")); + Assert.assertFalse("Item should have NOT been updated", + customModel.generatedData.containsKey("Bar")); + Assert.assertFalse("Item should have NOT been updated", + customModel.generatedData.containsKey("Baz")); + + customModel.generatedData.clear(); + + customModel.updateSelection(asSet("Bar"), asSet("Foo")); + customGrid.getDataCommunicator().beforeClientResponse(false); + + Assert.assertFalse("Item should have been updated as NOT selected", + customModel.generatedData.get("Foo")); + Assert.assertTrue("Item should have been updated as selected", + customModel.generatedData.get("Bar")); + Assert.assertFalse("Item should have NOT been updated", + customModel.generatedData.containsKey("Baz")); + + // switch to single to cause event + customModel.generatedData.clear(); + customGrid.setSelectionModel(new SingleSelectionModelImpl<>(customGrid)); + customGrid.getDataCommunicator().beforeClientResponse(false); + + // changing selection model should trigger row updates, but the old + // selection model is not triggered as it has been removed + Assert.assertTrue(customModel.generatedData.isEmpty()); // not triggered + } + + @Test + public void select_gridWithStrings() { + Grid gridWithStrings = new Grid<>(); + gridWithStrings + .setSelectionModel(new MultiSelectionModelImpl<>(gridWithStrings)); + gridWithStrings.setItems("Foo", "Bar", "Baz"); + + GridSelectionModel model = gridWithStrings.getSelectionModel(); + Assert.assertFalse(model.isSelected("Foo")); + + model.select("Foo"); + Assert.assertTrue(model.isSelected("Foo")); + Assert.assertEquals(Optional.of("Foo"), model.getFirstSelectedItem()); + + model.select("Bar"); + Assert.assertTrue(model.isSelected("Foo")); + Assert.assertTrue(model.isSelected("Bar")); + Assert.assertEquals(Arrays.asList("Foo", "Bar"), + new ArrayList<>(model.getSelectedItems())); + + model.deselect("Bar"); + Assert.assertFalse(model.isSelected("Bar")); + Assert.assertTrue(model.getFirstSelectedItem().isPresent()); + Assert.assertEquals(Arrays.asList("Foo"), + new ArrayList<>(model.getSelectedItems())); + } + + @Test + public void select() { + selectionModel.select(PERSON_B); + + assertEquals(PERSON_B, + selectionModel.getFirstSelectedItem().orElse(null)); + assertEquals(Optional.of(PERSON_B), + selectionModel.getFirstSelectedItem()); + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + + assertEquals(Arrays.asList(PERSON_B), + currentSelectionCapture.getValue()); + + selectionModel.select(PERSON_A); + assertEquals(PERSON_B, + selectionModel.getFirstSelectedItem().orElse(null)); + + assertTrue(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + + assertEquals(Arrays.asList(PERSON_B, PERSON_A), + currentSelectionCapture.getValue()); + assertEquals(2, events.get()); + } + + @Test + public void deselect() { + selectionModel.select(PERSON_B); + selectionModel.deselect(PERSON_B); + + assertFalse(selectionModel.getFirstSelectedItem().isPresent()); + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertFalse(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(2, events.get()); + } + + @Test + public void selectItems() { + selectionModel.selectItems(PERSON_C, PERSON_B); + + assertEquals(PERSON_C, + selectionModel.getFirstSelectedItem().orElse(null)); + assertEquals(Optional.of(PERSON_C), + selectionModel.getFirstSelectedItem()); + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertTrue(selectionModel.isSelected(PERSON_C)); + + assertEquals(Arrays.asList(PERSON_C, PERSON_B), + currentSelectionCapture.getValue()); + + selectionModel.selectItems(PERSON_A, PERSON_C); // partly NOOP + assertTrue(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertTrue(selectionModel.isSelected(PERSON_C)); + + assertEquals(Arrays.asList(PERSON_C, PERSON_B, PERSON_A), + currentSelectionCapture.getValue()); + assertEquals(2, events.get()); + } + + @Test + public void deselectItems() { + selectionModel.selectItems(PERSON_C, PERSON_A, PERSON_B); + + selectionModel.deselectItems(PERSON_A); + assertEquals(PERSON_C, + selectionModel.getFirstSelectedItem().orElse(null)); + assertEquals(Optional.of(PERSON_C), + selectionModel.getFirstSelectedItem()); + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertTrue(selectionModel.isSelected(PERSON_C)); + + assertEquals(Arrays.asList(PERSON_C, PERSON_B), + currentSelectionCapture.getValue()); + + selectionModel.deselectItems(PERSON_A, PERSON_B, PERSON_C); + assertNull(selectionModel.getFirstSelectedItem().orElse(null)); + assertEquals(Optional.empty(), selectionModel.getFirstSelectedItem()); + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertFalse(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(3, events.get()); + } + + @Test + public void selectionEvent_newSelection_oldSelection() { + selectionModel.selectItems(PERSON_C, PERSON_A, PERSON_B); + + assertEquals(Arrays.asList(PERSON_C, PERSON_A, PERSON_B), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(), oldSelectionCapture.getValue()); + + selectionModel.deselect(PERSON_A); + + assertEquals(Arrays.asList(PERSON_C, PERSON_B), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_C, PERSON_A, PERSON_B), + oldSelectionCapture.getValue()); + + selectionModel.deselectItems(PERSON_A, PERSON_B, PERSON_C); + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_C, PERSON_B), + oldSelectionCapture.getValue()); + + selectionModel.selectItems(PERSON_A); + assertEquals(Arrays.asList(PERSON_A), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(), oldSelectionCapture.getValue()); + + selectionModel.updateSelection( + new LinkedHashSet<>(Arrays.asList(PERSON_B, PERSON_C)), + new LinkedHashSet<>(Arrays.asList(PERSON_A))); + assertEquals(Arrays.asList(PERSON_B, PERSON_C), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_A), oldSelectionCapture.getValue()); + + selectionModel.deselectAll(); + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_B, PERSON_C), + oldSelectionCapture.getValue()); + + selectionModel.select(PERSON_C); + assertEquals(Arrays.asList(PERSON_C), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(), oldSelectionCapture.getValue()); + + selectionModel.deselect(PERSON_C); + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_C), oldSelectionCapture.getValue()); + } + + @Test + public void deselectAll() { + selectionModel.selectItems(PERSON_A, PERSON_C, PERSON_B); + + assertTrue(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertTrue(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(PERSON_A, PERSON_C, PERSON_B), + currentSelectionCapture.getValue()); + assertEquals(1, events.get()); + + selectionModel.deselectAll(); + assertFalse(selectionModel.isSelected(PERSON_A)); + assertFalse(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_A, PERSON_C, PERSON_B), + oldSelectionCapture.getValue()); + assertEquals(2, events.get()); + + selectionModel.select(PERSON_C); + assertFalse(selectionModel.isSelected(PERSON_A)); + assertFalse(selectionModel.isSelected(PERSON_B)); + assertTrue(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(PERSON_C), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(), oldSelectionCapture.getValue()); + assertEquals(3, events.get()); + + selectionModel.deselectAll(); + assertFalse(selectionModel.isSelected(PERSON_A)); + assertFalse(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_C), oldSelectionCapture.getValue()); + assertEquals(4, events.get()); + + selectionModel.deselectAll(); + assertEquals(4, events.get()); + } + + @Test + public void updateSelection() { + selectionModel.updateSelection(asSet(PERSON_A), Collections.emptySet()); + + assertTrue(selectionModel.isSelected(PERSON_A)); + assertFalse(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(PERSON_A), + currentSelectionCapture.getValue()); + assertEquals(1, events.get()); + + selectionModel.updateSelection(asSet(PERSON_B), asSet(PERSON_A)); + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(PERSON_B), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_A), oldSelectionCapture.getValue()); + assertEquals(2, events.get()); + + selectionModel.updateSelection(asSet(PERSON_B), asSet(PERSON_A)); // NOOP + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(PERSON_B), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_A), oldSelectionCapture.getValue()); + assertEquals(2, events.get()); + + selectionModel.updateSelection(asSet(PERSON_A, PERSON_C), + asSet(PERSON_A)); // partly NOOP + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertTrue(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(PERSON_B, PERSON_C), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_B), oldSelectionCapture.getValue()); + assertEquals(3, events.get()); + + selectionModel.updateSelection(asSet(PERSON_B, PERSON_A), + asSet(PERSON_B)); // partly NOOP + + assertTrue(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertTrue(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(PERSON_B, PERSON_C, PERSON_A), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_B, PERSON_C), + oldSelectionCapture.getValue()); + assertEquals(4, events.get()); + + selectionModel.updateSelection(asSet(), + asSet(PERSON_B, PERSON_A, PERSON_C)); + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertFalse(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_B, PERSON_C, PERSON_A), + oldSelectionCapture.getValue()); + assertEquals(5, events.get()); + } + + private Set asSet(@SuppressWarnings("unchecked") T... people) { + return new LinkedHashSet<>(Arrays.asList(people)); + } + + @Test + public void selectTwice() { + selectionModel.select(PERSON_C); + selectionModel.select(PERSON_C); + + assertEquals(PERSON_C, + selectionModel.getFirstSelectedItem().orElse(null)); + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertFalse(selectionModel.isSelected(PERSON_B)); + assertTrue(selectionModel.isSelected(PERSON_C)); + + assertEquals(Optional.of(PERSON_C), + selectionModel.getFirstSelectedItem()); + + assertEquals(Arrays.asList(PERSON_C), + currentSelectionCapture.getValue()); + assertEquals(1, events.get()); + } + + @Test + public void deselectTwice() { + selectionModel.select(PERSON_C); + assertEquals(Arrays.asList(PERSON_C), + currentSelectionCapture.getValue()); + assertEquals(1, events.get()); + + selectionModel.deselect(PERSON_C); + + assertFalse(selectionModel.getFirstSelectedItem().isPresent()); + assertFalse(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(2, events.get()); + + selectionModel.deselect(PERSON_C); + + assertFalse(selectionModel.getFirstSelectedItem().isPresent()); + assertFalse(selectionModel.isSelected(PERSON_A)); + assertFalse(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(2, events.get()); + } + + @SuppressWarnings({ "serial" }) + @Test + public void addValueChangeListener() { + AtomicReference> selectionListener = new AtomicReference<>(); + Registration registration = Mockito.mock(Registration.class); + Grid grid = new Grid<>(); + grid.setItems("foo", "bar"); + String value = "foo"; + SingleSelectionModelImpl select = new SingleSelectionModelImpl( + grid) { + @Override + public Registration addSelectionListener( + SingleSelectionListener listener) { + selectionListener.set(listener); + return registration; + } + + @Override + public Optional getSelectedItem() { + return Optional.of(value); + } + }; + + AtomicReference> event = new AtomicReference<>(); + Registration actualRegistration = select.addSelectionListener(evt -> { + Assert.assertNull(event.get()); + event.set(evt); + }); + Assert.assertSame(registration, actualRegistration); + + selectionListener.get().accept(new SingleSelectionEvent<>(grid, + select.asSingleSelect(), true)); + + Assert.assertEquals(grid, event.get().getComponent()); + Assert.assertEquals(value, event.get().getValue()); + Assert.assertTrue(event.get().isUserOriginated()); + } +} diff --git a/server/src/test/java/com/vaadin/tests/components/grid/GridSingleSelectionModelTest.java b/server/src/test/java/com/vaadin/tests/components/grid/GridSingleSelectionModelTest.java index 67b35c5208..17b409c1a6 100644 --- a/server/src/test/java/com/vaadin/tests/components/grid/GridSingleSelectionModelTest.java +++ b/server/src/test/java/com/vaadin/tests/components/grid/GridSingleSelectionModelTest.java @@ -6,7 +6,9 @@ import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; @@ -20,61 +22,123 @@ import com.vaadin.event.selection.SingleSelectionEvent; import com.vaadin.event.selection.SingleSelectionListener; import com.vaadin.server.data.provider.bov.Person; import com.vaadin.shared.Registration; -import com.vaadin.shared.data.DataCommunicatorClientRpc; import com.vaadin.ui.Grid; import com.vaadin.ui.Grid.GridSelectionModel; -import com.vaadin.ui.components.grid.SingleSelectionModel; +import com.vaadin.ui.components.grid.MultiSelectionModelImpl; +import com.vaadin.ui.components.grid.SingleSelectionModelImpl; + +import elemental.json.JsonObject; public class GridSingleSelectionModelTest { public static final Person PERSON_C = new Person("c", 3); public static final Person PERSON_B = new Person("b", 2); public static final Person PERSON_A = new Person("a", 1); - public static final String RPC_INTERFACE = DataCommunicatorClientRpc.class - .getName(); - private class CustomSelectionModelGrid extends Grid { - public void switchSelectionModel() { - // just switch selection model to cause event - setSelectionModel(new SingleSelectionModel(this)); + public static class CustomSingleSelectionModel + extends SingleSelectionModelImpl { + public final Map generatedData = new LinkedHashMap<>(); + + public CustomSingleSelectionModel(Grid grid) { + super(grid); } + + @Override + public void generateData(String item, JsonObject jsonObject) { + super.generateData(item, jsonObject); + // capture updated row + generatedData.put(item, isSelected(item)); + } + } private List selectionChanges; private Grid grid; - private SingleSelectionModel selectionModel; + private SingleSelectionModelImpl selectionModel; @Before public void setUp() { grid = new Grid<>(); grid.setItems(PERSON_A, PERSON_B, PERSON_C); - selectionModel = (SingleSelectionModel) grid + selectionModel = (SingleSelectionModelImpl) grid .getSelectionModel(); selectionChanges = new ArrayList<>(); - selectionModel.addSelectionChangeListener( - e -> selectionChanges.add(e.getValue())); + selectionModel + .addSelectionListener(e -> selectionChanges.add(e.getValue())); + } + + @Test(expected = IllegalStateException.class) + public void selectionModelChanged_usingPreviousSelectionModel_throws() { + grid.setSelectionModel(new MultiSelectionModelImpl<>(grid)); + + selectionModel.select(PERSON_A); } @Test - public void testGridChangingSelectionModel_firesSelectionChangeEvent() { - CustomSelectionModelGrid customGrid = new CustomSelectionModelGrid(); + public void gridChangingSelectionModel_firesSelectionChangeEvent() { + Grid customGrid = new Grid<>(); customGrid.setItems("Foo", "Bar", "Baz"); List selectionChanges = new ArrayList<>(); - ((SingleSelectionModel) customGrid.getSelectionModel()) - .addSelectionChangeListener( - e -> selectionChanges.add(e.getValue())); + ((SingleSelectionModelImpl) customGrid.getSelectionModel()) + .addSelectionListener(e -> selectionChanges.add(e.getValue())); customGrid.getSelectionModel().select("Foo"); assertEquals("Foo", customGrid.getSelectionModel().getFirstSelectedItem().get()); assertEquals(Arrays.asList("Foo"), selectionChanges); - customGrid.switchSelectionModel(); + customGrid + .setSelectionModel(new CustomSingleSelectionModel(customGrid)); assertEquals(Arrays.asList("Foo", null), selectionChanges); } + @Test + public void serverSideSelection_GridChangingSelectionModel_sendsUpdatedRowsToClient() { + Grid customGrid = new Grid<>(); + customGrid.setItems("Foo", "Bar", "Baz"); + + CustomSingleSelectionModel customModel = new CustomSingleSelectionModel( + customGrid); + customGrid.setSelectionModel(customModel); + + customGrid.getDataCommunicator().beforeClientResponse(true); + + Assert.assertFalse("Item should have been updated as selected", + customModel.generatedData.get("Foo")); + Assert.assertFalse("Item should have been updated as NOT selected", + customModel.generatedData.get("Bar")); + Assert.assertFalse("Item should have been updated as NOT selected", + customModel.generatedData.get("Baz")); + + customModel.generatedData.clear(); + + customGrid.getSelectionModel().select("Foo"); + customGrid.getDataCommunicator().beforeClientResponse(false); + + Assert.assertTrue("Item should have been updated as selected", + customModel.generatedData.get("Foo")); + Assert.assertFalse("Item should have NOT been updated", + customModel.generatedData.containsKey("Bar")); + Assert.assertFalse("Item should have NOT been updated", + customModel.generatedData.containsKey("Baz")); + + // switch to another selection model to cause event + customModel.generatedData.clear(); + customGrid.setSelectionModel(new SingleSelectionModelImpl<>(customGrid)); + customGrid.getDataCommunicator().beforeClientResponse(false); + + // since the selection model has been removed, it is no longer a data + // generator for the data communicator, would need to verify somehow + // that row is not marked as selected anymore ? (done in UI tests) + Assert.assertTrue(customModel.generatedData.isEmpty()); // at least + // removed + // selection + // model is not + // triggered + } + @Test public void testGridWithSingleSelection() { Grid gridWithStrings = new Grid<>(); @@ -210,10 +274,10 @@ public class GridSingleSelectionModelTest { Grid grid = new Grid<>(); grid.setItems("foo", "bar"); String value = "foo"; - SingleSelectionModel select = new SingleSelectionModel( + SingleSelectionModelImpl select = new SingleSelectionModelImpl( grid) { @Override - public Registration addSelectionChangeListener( + public Registration addSelectionListener( SingleSelectionListener listener) { selectionListener.set(listener); return registration; @@ -226,11 +290,10 @@ public class GridSingleSelectionModelTest { }; AtomicReference> event = new AtomicReference<>(); - Registration actualRegistration = select - .addSelectionChangeListener(evt -> { - Assert.assertNull(event.get()); - event.set(evt); - }); + Registration actualRegistration = select.addSelectionListener(evt -> { + Assert.assertNull(event.get()); + event.set(evt); + }); Assert.assertSame(registration, actualRegistration); selectionListener.get().accept(new SingleSelectionEvent<>(grid, diff --git a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/SelectDuringInitTest.java b/shared/src/main/java/com/vaadin/shared/data/selection/GridMultiSelectServerRpc.java similarity index 50% rename from uitest/src/test/java/com/vaadin/v7/tests/components/grid/SelectDuringInitTest.java rename to shared/src/main/java/com/vaadin/shared/data/selection/GridMultiSelectServerRpc.java index 052aae5a60..a250d4440a 100644 --- a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/SelectDuringInitTest.java +++ b/shared/src/main/java/com/vaadin/shared/data/selection/GridMultiSelectServerRpc.java @@ -13,25 +13,27 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.v7.tests.components.grid; +package com.vaadin.shared.data.selection; -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.testbench.parallel.TestCategory; -import com.vaadin.tests.tb3.SingleBrowserTest; -import com.vaadin.testbench.customelements.GridElement; - -@TestCategory("grid") -public class SelectDuringInitTest extends SingleBrowserTest { - - @Test - public void testSelectDuringInit() { - openTestURL(); - - GridElement grid = $(GridElement.class).first(); - - Assert.assertTrue(grid.getRow(1).isSelected()); - } +/** + * Transmits selection events for grid multiselection model to server side. + * + * @since 8.0 + * @author Vaadin Ltd. + * + */ +public interface GridMultiSelectServerRpc extends SelectionServerRpc { + /** + * All rows in grid have been selected with the select all checkbox in + * header. + */ + public void selectAll(); + /** + * All rows in grid have been deselected with the select all checkbox in + * header. + *

+ * This can happen only if the all the rows were previously selected. + */ + public void deselectAll(); } diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/GridCustomSelectionModel.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridCustomSelectionModel.java new file mode 100644 index 0000000000..220f059f52 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridCustomSelectionModel.java @@ -0,0 +1,65 @@ +/* + * 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.grid; + +import java.util.List; + +import com.vaadin.annotations.Widgetset; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.tests.components.grid.basics.DataObject; +import com.vaadin.tests.components.grid.basics.GridBasics; +import com.vaadin.tests.widgetset.TestingWidgetSet; +import com.vaadin.ui.Grid; +import com.vaadin.ui.components.grid.MultiSelectionModelImpl; + +@Widgetset(TestingWidgetSet.NAME) +public class GridCustomSelectionModel extends AbstractTestUI { + + public static class MySelectionModel + extends MultiSelectionModelImpl { + + public MySelectionModel(Grid grid) { + super(grid); + } + } + + private Grid grid; + private List data; + + @Override + protected void setup(VaadinRequest request) { + data = DataObject.generateObjects(); + + // Create grid + grid = new Grid() { + { + MySelectionModel model = new MySelectionModel(this); + setSelectionModel(model); + model.extend(this); + } + }; + grid.setItems(data); + grid.addColumn(dataObj -> "(" + dataObj.getRowNumber() + ", 0)") + .setCaption(GridBasics.COLUMN_CAPTIONS[0]); + grid.addColumn(dataObj -> "(" + dataObj.getRowNumber() + ", 1)") + .setCaption(GridBasics.COLUMN_CAPTIONS[1]); + grid.addColumn(dataObj -> "(" + dataObj.getRowNumber() + ", 2)") + .setCaption(GridBasics.COLUMN_CAPTIONS[2]); + addComponent(grid); + } + +} diff --git a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridDefaultSelectionMode.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridDefaultSelectionMode.java similarity index 79% rename from uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridDefaultSelectionMode.java rename to uitest/src/main/java/com/vaadin/tests/components/grid/GridDefaultSelectionMode.java index 43ddb184d8..625f94d426 100644 --- a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridDefaultSelectionMode.java +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridDefaultSelectionMode.java @@ -13,20 +13,19 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.v7.tests.components.grid; +package com.vaadin.tests.components.grid; import java.util.ArrayList; import com.vaadin.server.VaadinRequest; -import com.vaadin.tests.components.AbstractReindeerTestUI; +import com.vaadin.tests.components.AbstractTestUI; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.Grid; import com.vaadin.ui.VerticalLayout; -import com.vaadin.v7.data.util.BeanItemContainer; -import com.vaadin.v7.ui.Grid; -public class GridDefaultSelectionMode extends AbstractReindeerTestUI { +public class GridDefaultSelectionMode extends AbstractTestUI { @Override protected void setup(VaadinRequest request) { @@ -42,11 +41,11 @@ public class GridDefaultSelectionMode extends AbstractReindeerTestUI { items.add(person1); items.add(person2); - final BeanItemContainer container = new BeanItemContainer<>( - Person.class, items); - - final Grid grid = new Grid(); - grid.setContainerDataSource(container); + final Grid grid = new Grid<>(); + grid.setItems(items); + grid.addColumn(person -> person.getFirstName()) + .setCaption("First Name"); + grid.addColumn(person -> person.getLastName()).setCaption("Last Name"); VerticalLayout v = new VerticalLayout(); @@ -54,7 +53,7 @@ public class GridDefaultSelectionMode extends AbstractReindeerTestUI { @Override public void buttonClick(ClickEvent event) { - grid.select(null); + grid.getSelectionModel().deselectAll(); } })); @@ -62,7 +61,7 @@ public class GridDefaultSelectionMode extends AbstractReindeerTestUI { @Override public void buttonClick(ClickEvent event) { - grid.select(person1); + grid.getSelectionModel().select(person1); } })); v.addComponent(grid); diff --git a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridDisabledMultiselect.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridDisabledMultiselect.java similarity index 67% rename from uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridDisabledMultiselect.java rename to uitest/src/main/java/com/vaadin/tests/components/grid/GridDisabledMultiselect.java index c58de99db7..46ac8ec260 100644 --- a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridDisabledMultiselect.java +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridDisabledMultiselect.java @@ -1,24 +1,24 @@ -package com.vaadin.v7.tests.components.grid; +package com.vaadin.tests.components.grid; import com.vaadin.server.VaadinRequest; import com.vaadin.tests.components.AbstractReindeerTestUI; import com.vaadin.ui.Button; -import com.vaadin.v7.ui.Grid; +import com.vaadin.ui.Grid; +import com.vaadin.ui.components.grid.MultiSelectionModelImpl; public class GridDisabledMultiselect extends AbstractReindeerTestUI { @Override protected void setup(VaadinRequest request) { - final Grid grid = new Grid(); - grid.addColumn("foo", String.class); - grid.addRow("bar"); - grid.setSelectionMode(Grid.SelectionMode.SINGLE); + final Grid grid = new Grid<>(); + grid.addColumn(string -> string); + grid.setItems("bar"); addComponent(grid); addButton("Multi", new Button.ClickListener() { @Override public void buttonClick(Button.ClickEvent event) { - grid.setSelectionMode(Grid.SelectionMode.MULTI); + grid.setSelectionModel(new MultiSelectionModelImpl<>(grid)); } }); diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/GridDragSelectionWhileScrolled.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridDragSelectionWhileScrolled.java new file mode 100644 index 0000000000..2875e18119 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridDragSelectionWhileScrolled.java @@ -0,0 +1,75 @@ +/* + * 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.grid; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.tests.components.grid.basics.DataObject; +import com.vaadin.tests.components.grid.basics.GridBasics; +import com.vaadin.ui.Grid; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Layout; +import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.components.grid.MultiSelectionModelImpl; +import com.vaadin.ui.renderers.DateRenderer; +import com.vaadin.ui.renderers.HtmlRenderer; +import com.vaadin.ui.renderers.NumberRenderer; + +public class GridDragSelectionWhileScrolled extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + Layout layout = new VerticalLayout(); + + HorizontalLayout spacer = new HorizontalLayout(); + spacer.setHeight("1000px"); + layout.addComponent(spacer); + + Grid grid = new Grid<>(); + grid.setItems(DataObject.generateObjects()); + grid.addColumn(dataObj -> "(" + dataObj.getRowNumber() + ", 0)") + .setCaption(GridBasics.COLUMN_CAPTIONS[0]); + grid.addColumn(dataObj -> "(" + dataObj.getRowNumber() + ", 1)") + .setCaption(GridBasics.COLUMN_CAPTIONS[1]); + grid.addColumn(dataObj -> "(" + dataObj.getRowNumber() + ", 2)") + .setCaption(GridBasics.COLUMN_CAPTIONS[2]); + + grid.addColumn(DataObject::getRowNumber, new NumberRenderer()) + .setCaption(GridBasics.COLUMN_CAPTIONS[3]); + grid.addColumn(DataObject::getDate, new DateRenderer()) + .setCaption(GridBasics.COLUMN_CAPTIONS[4]); + grid.addColumn(DataObject::getHtmlString, new HtmlRenderer()) + .setCaption(GridBasics.COLUMN_CAPTIONS[5]); + grid.addColumn(DataObject::getBigRandom, new NumberRenderer()) + .setCaption(GridBasics.COLUMN_CAPTIONS[6]); + + grid.setSelectionModel(new MultiSelectionModelImpl<>(grid)); + + layout.addComponent(grid); + + addComponent(layout); + } + + @Override + protected Integer getTicketNumber() { + return 17895; + } + + @Override + protected String getTestDescription() { + return "Drag selecting rows in Grid malfunctions if page is scrolled"; + } +} diff --git a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/SelectDuringInit.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridMultiSelectionOnInit.java similarity index 50% rename from uitest/src/main/java/com/vaadin/v7/tests/components/grid/SelectDuringInit.java rename to uitest/src/main/java/com/vaadin/tests/components/grid/GridMultiSelectionOnInit.java index c32f52a627..2b1e78d323 100644 --- a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/SelectDuringInit.java +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridMultiSelectionOnInit.java @@ -13,28 +13,28 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.v7.tests.components.grid; +package com.vaadin.tests.components.grid; import com.vaadin.server.VaadinRequest; -import com.vaadin.tests.components.AbstractReindeerTestUI; -import com.vaadin.v7.ui.Grid; -import com.vaadin.v7.ui.Grid.SelectionMode; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Grid; +import com.vaadin.ui.components.grid.MultiSelectionModelImpl; -public class SelectDuringInit extends AbstractReindeerTestUI { +public class GridMultiSelectionOnInit extends AbstractTestUI { @Override protected void setup(VaadinRequest request) { - Grid grid = new Grid(); - grid.setSelectionMode(SelectionMode.MULTI); - - grid.addColumn("value"); - grid.addRow("row 1"); - grid.addRow("row 2"); - grid.addRow("row 3"); - - grid.select(Integer.valueOf(2)); - + final Grid grid = new Grid<>(); + grid.setItems("Foo 1", "Foo 2"); + grid.addColumn(item -> item); + grid.setSelectionModel(new MultiSelectionModelImpl<>(grid)); addComponent(grid); - } + addComponent(new Button("Select rows", + event -> grid.getSelectionModel().select("Foo 1"))); + if (request.getParameter("initialSelection") != null) { + grid.getSelectionModel().select("Foo 2"); + } + } } diff --git a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridMultiSelectionScrollBar.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridMultiSelectionScrollBar.java similarity index 66% rename from uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridMultiSelectionScrollBar.java rename to uitest/src/main/java/com/vaadin/tests/components/grid/GridMultiSelectionScrollBar.java index 3d89f32831..22fdf64e5c 100644 --- a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridMultiSelectionScrollBar.java +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridMultiSelectionScrollBar.java @@ -13,20 +13,20 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.v7.tests.components.grid; +package com.vaadin.tests.components.grid; import com.vaadin.server.VaadinRequest; -import com.vaadin.tests.components.AbstractReindeerTestUI; -import com.vaadin.v7.ui.Grid; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Grid; -public class GridMultiSelectionScrollBar extends AbstractReindeerTestUI { +public class GridMultiSelectionScrollBar extends AbstractTestUI { @Override protected void setup(VaadinRequest request) { - Grid grid = new Grid(); - grid.addColumn("X").setWidth(39.25d); - grid.addColumn("Hello"); - grid.addColumn("World"); + Grid grid = new Grid<>(); + grid.addColumn(string -> "X:" + string).setWidth(39.25d); + grid.addColumn(string -> "Hello:" + string); + grid.addColumn(string -> "World:" + string); grid.setFrozenColumnCount(1); addComponent(grid); } diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/basics/DataObject.java b/uitest/src/main/java/com/vaadin/tests/components/grid/basics/DataObject.java index ff27268991..54dc8d74b2 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/grid/basics/DataObject.java +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/basics/DataObject.java @@ -5,7 +5,7 @@ import java.util.Date; import java.util.List; import java.util.Random; -class DataObject { +public class DataObject { private static final int ROWS = 1000; @@ -69,7 +69,7 @@ class DataObject { return "DataObject[" + rowNumber + "]"; } - static List generateObjects() { + public static List generateObjects() { List data = new ArrayList<>(); { diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java b/uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java index 8917ce0241..4f36eb6207 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java @@ -9,6 +9,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Optional; import java.util.function.Consumer; import java.util.stream.Stream; @@ -16,6 +17,8 @@ import com.vaadin.annotations.Theme; import com.vaadin.annotations.Widgetset; import com.vaadin.data.Binder; import com.vaadin.data.util.converter.StringToIntegerConverter; +import com.vaadin.event.selection.MultiSelectionEvent; +import com.vaadin.event.selection.SingleSelectionEvent; import com.vaadin.server.VaadinRequest; import com.vaadin.shared.Registration; import com.vaadin.shared.ui.grid.HeightMode; @@ -36,7 +39,8 @@ import com.vaadin.ui.Panel; import com.vaadin.ui.StyleGenerator; import com.vaadin.ui.TextField; import com.vaadin.ui.VerticalLayout; -import com.vaadin.ui.components.grid.SingleSelectionModel; +import com.vaadin.ui.components.grid.MultiSelectionModelImpl; +import com.vaadin.ui.components.grid.SingleSelectionModelImpl; import com.vaadin.ui.renderers.DateRenderer; import com.vaadin.ui.renderers.HtmlRenderer; import com.vaadin.ui.renderers.NumberRenderer; @@ -149,6 +153,8 @@ public class GridBasics extends AbstractTestUIWithLog { private List data; private int watchingCount = 0; private PersistingDetailsGenerator persistingDetails; + private List> initialColumnOrder; + private Registration selectionListenerRegistration; public GridBasics() { generators.put("NULL", null); @@ -215,15 +221,33 @@ public class GridBasics extends AbstractTestUIWithLog { new ProgressBarRenderer()).setCaption(COLUMN_CAPTIONS[7]) .setEditorComponent(smallRandom); - ((SingleSelectionModel) grid.getSelectionModel()) - .addSelectionChangeListener( - e -> log("Selected: " + e.getValue())); + selectionListenerRegistration = ((SingleSelectionModelImpl) grid + .getSelectionModel()) + .addSelectionListener(this::onSingleSelect); layout.addComponent(createMenu()); layout.addComponent(grid); addComponent(layout); } + private void onSingleSelect(SingleSelectionEvent event) { + log("SingleSelectionEvent: Selected: " + + (event.getSelectedItem().isPresent() + ? event.getSelectedItem().get().toString() : "none")); + } + + private void onMultiSelect(MultiSelectionEvent event) { + Optional firstAdded = event.getNewSelection().stream() + .findFirst(); + Optional firstRemoved = event.getOldSelection().stream() + .findFirst(); + String addedRow = firstAdded.isPresent() ? firstAdded.toString() + : "none"; + String removedRow = firstRemoved.isPresent() ? firstRemoved.toString() + : "none"; + log("SelectionEvent: Added " + addedRow + ", Removed " + removedRow); + } + private Component createMenu() { MenuBar menu = new MenuBar(); menu.setErrorHandler(error -> log("Exception occured, " @@ -388,6 +412,8 @@ public class GridBasics extends AbstractTestUIWithLog { e -> grid.setEnabled(e.isChecked())); enableItem.setCheckable(true); enableItem.setChecked(true); + + createSelectionMenu(stateMenu); } private void createRowStyleMenu(MenuItem rowStyleMenu) { @@ -449,6 +475,28 @@ public class GridBasics extends AbstractTestUIWithLog { grid.getSelectionModel().select(item); } }); + rowMenu.addItem("Deselect all", menuItem -> { + grid.getSelectionModel().deselectAll(); + }); + } + + private void createSelectionMenu(MenuItem stateItem) { + MenuItem selectionModelItem = stateItem.addItem("Selection model", + null); + selectionModelItem.addItem("single", menuItem -> { + selectionListenerRegistration.remove(); + grid.setSelectionModel(new SingleSelectionModelImpl<>(grid)); + selectionListenerRegistration = ((SingleSelectionModelImpl) grid + .getSelectionModel()) + .addSelectionListener(this::onSingleSelect); + }); + selectionModelItem.addItem("multi", menuItem -> { + selectionListenerRegistration.remove(); + grid.setSelectionModel(new MultiSelectionModelImpl<>(grid)); + selectionListenerRegistration = ((MultiSelectionModelImpl) grid + .getSelectionModel()) + .addSelectionListener(this::onMultiSelect); + }); } private void createHeaderMenu(MenuItem headerMenu) { diff --git a/uitest/src/main/java/com/vaadin/tests/widgetset/client/v7/grid/MySelectionModelConnector.java b/uitest/src/main/java/com/vaadin/tests/widgetset/client/grid/MySelectionModelConnector.java similarity index 58% rename from uitest/src/main/java/com/vaadin/tests/widgetset/client/v7/grid/MySelectionModelConnector.java rename to uitest/src/main/java/com/vaadin/tests/widgetset/client/grid/MySelectionModelConnector.java index b5b1e688eb..78851dd7ef 100644 --- a/uitest/src/main/java/com/vaadin/tests/widgetset/client/v7/grid/MySelectionModelConnector.java +++ b/uitest/src/main/java/com/vaadin/tests/widgetset/client/grid/MySelectionModelConnector.java @@ -13,28 +13,32 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.tests.widgetset.client.v7.grid; +package com.vaadin.tests.widgetset.client.grid; import com.vaadin.client.ServerConnector; +import com.vaadin.client.connectors.grid.MultiSelectionModelConnector; +import com.vaadin.client.renderers.Renderer; +import com.vaadin.client.widget.grid.selection.ClickSelectHandler; import com.vaadin.shared.ui.Connect; -import com.vaadin.v7.client.connectors.MultiSelectionModelConnector; -import com.vaadin.v7.client.renderers.ComplexRenderer; -import com.vaadin.v7.client.widget.grid.selection.ClickSelectHandler; -import com.vaadin.v7.client.widget.grid.selection.SelectionModel.Multi; -import com.vaadin.v7.client.widgets.Grid; -import com.vaadin.v7.tests.components.grid.GridCustomSelectionModel.MySelectionModel; import elemental.json.JsonObject; -@Connect(MySelectionModel.class) +@Connect(com.vaadin.tests.components.grid.GridCustomSelectionModel.MySelectionModel.class) public class MySelectionModelConnector extends MultiSelectionModelConnector { + protected class MyMultiSelectionModel extends MultiSelectionModel { + @Override + public Renderer getRenderer() { + return null; + } + } + private ClickSelectHandler handler; @Override protected void extend(ServerConnector target) { - super.extend(target); handler = new ClickSelectHandler<>(getGrid()); + getGrid().setSelectionModel(new MyMultiSelectionModel()); } @Override @@ -44,18 +48,4 @@ public class MySelectionModelConnector extends MultiSelectionModelConnector { handler = null; } - @Override - protected Multi createSelectionModel() { - return new MySelectionModel(); - } - - public class MySelectionModel extends MultiSelectionModel { - - @Override - protected ComplexRenderer createSelectionColumnRenderer( - Grid grid) { - // No Selection Column. - return null; - } - } } diff --git a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridCustomSelectionModel.java b/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridCustomSelectionModel.java deleted file mode 100644 index 89029a36fe..0000000000 --- a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridCustomSelectionModel.java +++ /dev/null @@ -1,37 +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.v7.tests.components.grid; - -import com.vaadin.annotations.Widgetset; -import com.vaadin.server.VaadinRequest; -import com.vaadin.tests.components.AbstractReindeerTestUI; -import com.vaadin.tests.widgetset.TestingWidgetSet; -import com.vaadin.v7.ui.Grid.MultiSelectionModel; - -@Widgetset(TestingWidgetSet.NAME) -public class GridCustomSelectionModel extends AbstractReindeerTestUI { - - public static class MySelectionModel extends MultiSelectionModel { - } - - @Override - protected void setup(VaadinRequest request) { - PersonTestGrid grid = new PersonTestGrid(500); - grid.setSelectionModel(new MySelectionModel()); - addComponent(grid); - } - -} diff --git a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridDragSelectionWhileScrolled.java b/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridDragSelectionWhileScrolled.java deleted file mode 100644 index 33da0feb9d..0000000000 --- a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridDragSelectionWhileScrolled.java +++ /dev/null @@ -1,51 +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.v7.tests.components.grid; - -import com.vaadin.server.VaadinRequest; -import com.vaadin.tests.components.AbstractReindeerTestUI; -import com.vaadin.ui.HorizontalLayout; -import com.vaadin.ui.Layout; -import com.vaadin.ui.VerticalLayout; -import com.vaadin.v7.ui.Grid.SelectionMode; - -public class GridDragSelectionWhileScrolled extends AbstractReindeerTestUI { - - @Override - protected void setup(VaadinRequest request) { - Layout layout = new VerticalLayout(); - - HorizontalLayout spacer = new HorizontalLayout(); - spacer.setHeight("1000px"); - layout.addComponent(spacer); - - PersonTestGrid grid = new PersonTestGrid(100); - grid.setSelectionMode(SelectionMode.MULTI); - layout.addComponent(grid); - - addComponent(layout); - } - - @Override - protected Integer getTicketNumber() { - return 17895; - } - - @Override - protected String getTestDescription() { - return "Drag selecting rows in Grid malfunctions if page is scrolled"; - } -} diff --git a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridMultiSelectionOnInit.java b/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridMultiSelectionOnInit.java deleted file mode 100644 index 8f6022846d..0000000000 --- a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridMultiSelectionOnInit.java +++ /dev/null @@ -1,46 +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.v7.tests.components.grid; - -import com.vaadin.server.VaadinRequest; -import com.vaadin.tests.components.AbstractReindeerTestUI; -import com.vaadin.ui.Button; -import com.vaadin.ui.Button.ClickEvent; -import com.vaadin.v7.ui.Grid; -import com.vaadin.v7.ui.Grid.MultiSelectionModel; -import com.vaadin.v7.ui.Grid.SelectionMode; - -public class GridMultiSelectionOnInit extends AbstractReindeerTestUI { - - @Override - protected void setup(VaadinRequest request) { - final Grid grid = new Grid(); - grid.addColumn("foo", String.class); - grid.addRow("Foo 1"); - grid.addRow("Foo 2"); - grid.setSelectionMode(SelectionMode.MULTI); - addComponent(grid); - - addComponent(new Button("Select rows", new Button.ClickListener() { - - @Override - public void buttonClick(ClickEvent event) { - ((MultiSelectionModel) grid.getSelectionModel()).setSelected( - grid.getContainerDataSource().getItemIds()); - } - })); - } -} diff --git a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridSelectAllCell.java b/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridSelectAllCell.java deleted file mode 100644 index 98913f037b..0000000000 --- a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridSelectAllCell.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.vaadin.v7.tests.components.grid; - -import com.vaadin.server.VaadinRequest; -import com.vaadin.tests.components.AbstractTestUI; -import com.vaadin.v7.ui.Grid; - -public class GridSelectAllCell extends AbstractTestUI { - @Override - protected void setup(VaadinRequest request) { - Grid grid = new Grid(); - - grid.addColumn("foo", String.class); - grid.addRow("bar"); - - grid.setSelectionMode(Grid.SelectionMode.MULTI); - - addComponent(grid); - } -} diff --git a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridCustomSelectionModelTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/GridCustomSelectionModelTest.java similarity index 94% rename from uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridCustomSelectionModelTest.java rename to uitest/src/test/java/com/vaadin/tests/components/grid/GridCustomSelectionModelTest.java index 31c90c401d..d6a4c0b33c 100644 --- a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridCustomSelectionModelTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/GridCustomSelectionModelTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.v7.tests.components.grid; +package com.vaadin.tests.components.grid; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -22,10 +22,10 @@ import org.junit.Test; import org.openqa.selenium.By; import org.openqa.selenium.Keys; +import com.vaadin.testbench.customelements.GridElement; import com.vaadin.testbench.elements.GridElement.GridCellElement; import com.vaadin.testbench.parallel.TestCategory; import com.vaadin.tests.tb3.MultiBrowserTest; -import com.vaadin.testbench.customelements.GridElement; @TestCategory("grid") public class GridCustomSelectionModelTest extends MultiBrowserTest { @@ -38,7 +38,7 @@ public class GridCustomSelectionModelTest extends MultiBrowserTest { GridElement grid = $(GridElement.class).first(); GridCellElement cell = grid.getCell(0, 0); assertTrue("First column of Grid should not have an input element", - cell.findElements(By.className("input")).isEmpty()); + cell.findElements(By.tagName("input")).isEmpty()); assertFalse("Row should not be selected initially", grid.getRow(0).isSelected()); diff --git a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridDefaultSelectionModeTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/GridDefaultSelectionModeTest.java similarity index 98% rename from uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridDefaultSelectionModeTest.java rename to uitest/src/test/java/com/vaadin/tests/components/grid/GridDefaultSelectionModeTest.java index 01d8e890f3..3dbe8b5426 100644 --- a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridDefaultSelectionModeTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/GridDefaultSelectionModeTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.v7.tests.components.grid; +package com.vaadin.tests.components.grid; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; diff --git a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridDisabledMultiselectTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/GridDisabledMultiselectTest.java similarity index 80% rename from uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridDisabledMultiselectTest.java rename to uitest/src/test/java/com/vaadin/tests/components/grid/GridDisabledMultiselectTest.java index d822051992..8bc58450c8 100644 --- a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridDisabledMultiselectTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/GridDisabledMultiselectTest.java @@ -1,4 +1,4 @@ -package com.vaadin.v7.tests.components.grid; +package com.vaadin.tests.components.grid; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; @@ -39,7 +39,7 @@ public class GridDisabledMultiselectTest extends MultiBrowserTest { } private WebElement getFirstSelectCheckBox() { - return findCheckBoxes().get(1); + return findCheckBoxes().get(0); } @Test @@ -48,20 +48,22 @@ public class GridDisabledMultiselectTest extends MultiBrowserTest { setMultiselect(); - assertThat(getSelectAllCheckBox().isEnabled(), is(false)); + // TODO enable once select all is added back + // assertThat(getSelectAllCheckBox().isEnabled(), is(false)); assertThat(getFirstSelectCheckBox().isEnabled(), is(false)); } @Test public void checkBoxesAreDisabledAfterDisabled() { setMultiselect(); - - assertThat(getSelectAllCheckBox().isEnabled(), is(true)); + // TODO enable once select all is added back + // assertThat(getSelectAllCheckBox().isEnabled(), is(true)); assertThat(getFirstSelectCheckBox().isEnabled(), is(true)); disable(); - assertThat(getSelectAllCheckBox().isEnabled(), is(false)); + // TODO enable once select all is added back + // assertThat(getSelectAllCheckBox().isEnabled(), is(false)); assertThat(getFirstSelectCheckBox().isEnabled(), is(false)); } diff --git a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridDragSelectionWhileScrolledTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/GridDragSelectionWhileScrolledTest.java similarity index 94% rename from uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridDragSelectionWhileScrolledTest.java rename to uitest/src/test/java/com/vaadin/tests/components/grid/GridDragSelectionWhileScrolledTest.java index af5d77adce..5690263336 100644 --- a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridDragSelectionWhileScrolledTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/GridDragSelectionWhileScrolledTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.v7.tests.components.grid; +package com.vaadin.tests.components.grid; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -24,11 +24,9 @@ import org.junit.Test; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.interactions.Actions; -import com.vaadin.testbench.parallel.TestCategory; -import com.vaadin.tests.tb3.MultiBrowserTest; import com.vaadin.testbench.customelements.GridElement; +import com.vaadin.tests.tb3.MultiBrowserTest; -@TestCategory("grid") public class GridDragSelectionWhileScrolledTest extends MultiBrowserTest { @Override diff --git a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridMultiSelectionOnInitTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/GridMultiSelectionOnInitTest.java similarity index 61% rename from uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridMultiSelectionOnInitTest.java rename to uitest/src/test/java/com/vaadin/tests/components/grid/GridMultiSelectionOnInitTest.java index 79f6aecb02..6abcb71e8c 100644 --- a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridMultiSelectionOnInitTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/GridMultiSelectionOnInitTest.java @@ -13,23 +13,30 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.v7.tests.components.grid; +package com.vaadin.tests.components.grid; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import java.io.IOException; + import org.junit.Test; import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; +import com.vaadin.testbench.customelements.GridElement; import com.vaadin.testbench.elements.ButtonElement; import com.vaadin.testbench.parallel.TestCategory; import com.vaadin.tests.tb3.MultiBrowserTest; -import com.vaadin.testbench.customelements.GridElement; @TestCategory("grid") public class GridMultiSelectionOnInitTest extends MultiBrowserTest { - @Test + // TODO enable when select all is added back + // @Test public void testSelectAllCheckBoxExists() { openTestURL(); assertTrue("The select all checkbox was missing.", @@ -37,6 +44,20 @@ public class GridMultiSelectionOnInitTest extends MultiBrowserTest { .isElementPresent(By.tagName("input"))); } + // TODO enable when select all is added back + // @Test + public void selectAllCellCanBeClicked() throws IOException { + GridElement.GridCellElement selectAllCell = $(GridElement.class).first() + .getHeaderCell(0, 0); + + new Actions(getDriver()).moveToElement(selectAllCell, 2, 2).click() + .perform(); + + WebElement selectAllCheckbox = selectAllCell + .findElement(By.cssSelector("input")); + assertThat(selectAllCheckbox.isSelected(), is(true)); + } + @Test public void testSetSelectedUpdatesClient() { openTestURL(); @@ -46,4 +67,13 @@ public class GridMultiSelectionOnInitTest extends MultiBrowserTest { assertTrue("Rows should be selected after button click.", $(GridElement.class).first().getRow(0).isSelected()); } + + @Test + public void testInitialSelection() { + openTestURL("initialSelection=yes"); + + assertTrue("Initial selection should be visible", + $(GridElement.class).first().getRow(1).isSelected()); + } + } diff --git a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridMultiSelectionScrollBarTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/GridMultiSelectionScrollBarTest.java similarity index 96% rename from uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridMultiSelectionScrollBarTest.java rename to uitest/src/test/java/com/vaadin/tests/components/grid/GridMultiSelectionScrollBarTest.java index eff1a69b3c..4363737746 100644 --- a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridMultiSelectionScrollBarTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/GridMultiSelectionScrollBarTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.v7.tests.components.grid; +package com.vaadin.tests.components.grid; import static org.junit.Assert.assertTrue; diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/GridSelectAllTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/GridSelectAllTest.java new file mode 100644 index 0000000000..26fdeedd7b --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/GridSelectAllTest.java @@ -0,0 +1,108 @@ +package com.vaadin.tests.components.grid; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.openqa.selenium.WebElement; + +import com.vaadin.testbench.By; +import com.vaadin.testbench.elements.GridElement.GridCellElement; +import com.vaadin.tests.components.grid.basics.GridBasicsTest; + +public class GridSelectAllTest extends GridBasicsTest { + + // TODO remove once select all is added + @Test + public void testSelectAllCheckBoxNotVisble() { + setSelectionModelMulti(); + GridCellElement header = getGridElement().getHeaderCell(0, 0); + + assertFalse("Checkbox visible", + header.isElementPresent(By.tagName("input"))); + } + + // TODO enable once select all is added + // @Test + public void testSelectAllCheckbox() { + setSelectionModelMulti(); + GridCellElement header = getGridElement().getHeaderCell(0, 0); + + assertTrue("No checkbox", header.isElementPresent(By.tagName("input"))); + header.findElement(By.tagName("input")).click(); + + for (int i = 0; i < GridBasicsTest.ROWS; i += 100) { + assertTrue("Row " + i + " was not selected.", + getGridElement().getRow(i).isSelected()); + } + + header.findElement(By.tagName("input")).click(); + assertFalse("Row 100 was still selected", + getGridElement().getRow(100).isSelected()); + } + + // TODO enable once select all is added + // @Test + public void testSelectAllAndSort() { + setSelectionModelMulti(); + GridCellElement header = getGridElement().getHeaderCell(0, 0); + + header.findElement(By.tagName("input")).click(); + + getGridElement().getHeaderCell(0, 1).click(); + + WebElement selectionBox = getGridElement().getCell(4, 0) + .findElement(By.tagName("input")); + selectionBox.click(); + selectionBox.click(); + + assertFalse("Exception occured on row reselection.", logContainsText( + "Exception occured, java.lang.IllegalStateException: No item id for key 101 found.")); + } + + // TODO enable once select all is added + // @Test + public void testSelectAllCheckboxWhenChangingModels() { + GridCellElement header; + header = getGridElement().getHeaderCell(0, 0); + assertFalse( + "Check box shouldn't have been in header for None Selection Model", + header.isElementPresent(By.tagName("input"))); + + setSelectionModelMulti(); + header = getGridElement().getHeaderCell(0, 0); + assertTrue("Multi Selection Model should have select all checkbox", + header.isElementPresent(By.tagName("input"))); + + setSelectionModelSingle(); + header = getGridElement().getHeaderCell(0, 0); + assertFalse( + "Check box shouldn't have been in header for Single Selection Model", + header.isElementPresent(By.tagName("input"))); + + // Single selection model shouldn't have selection column to begin with + assertFalse( + "Selection columnn shouldn't have been in grid for Single Selection Model", + getGridElement().getCell(0, 1) + .isElementPresent(By.tagName("input"))); + + setSelectionModelSingle(); + header = getGridElement().getHeaderCell(0, 0); + assertFalse( + "Check box shouldn't have been in header for None Selection Model", + header.isElementPresent(By.tagName("input"))); + } + + // TODO enable once select all is added + // @Test + public void testSelectAllCheckboxWithHeaderOperations() { + setSelectionModelMulti(); + selectMenuPath("Component", "Header", "Prepend row"); + selectMenuPath("Component", "Header", "Append row"); + + GridCellElement header = getGridElement().getHeaderCell(1, 0); + assertTrue("Multi Selection Model should have select all checkbox", + header.isElementPresent(By.tagName("input"))); + } + +} diff --git a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/basicfeatures/server/GridSelectionTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java similarity index 54% rename from uitest/src/test/java/com/vaadin/v7/tests/components/grid/basicfeatures/server/GridSelectionTest.java rename to uitest/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java index 1bb989be1d..f7bd514e7b 100644 --- a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/basicfeatures/server/GridSelectionTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java @@ -1,19 +1,4 @@ -/* - * 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.v7.tests.components.grid.basicfeatures.server; +package com.vaadin.tests.components.grid; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -29,11 +14,9 @@ import com.vaadin.testbench.By; import com.vaadin.testbench.customelements.GridElement; import com.vaadin.testbench.elements.GridElement.GridCellElement; import com.vaadin.testbench.elements.GridElement.GridRowElement; -import com.vaadin.v7.tests.components.grid.basicfeatures.GridBasicFeatures; -import com.vaadin.v7.tests.components.grid.basicfeatures.GridBasicFeaturesTest; - -public class GridSelectionTest extends GridBasicFeaturesTest { +import com.vaadin.tests.components.grid.basics.GridBasicsTest; +public class GridSelectionTest extends GridBasicsTest { @Test public void testSelectOnOff() throws Exception { openTestURL(); @@ -95,11 +78,12 @@ public class GridSelectionTest extends GridBasicFeaturesTest { assertFalse("row shouldn't start out as selected", getRow(0).isSelected()); - scrollGridVerticallyTo(10000); // make sure the row is out of cache toggleFirstRowSelection(); + assertTrue("row should be selected", getRow(0).isSelected()); + scrollGridVerticallyTo(10000); // make sure the row is out of cache toggleFirstRowSelection(); - scrollGridVerticallyTo(0); // make sure the row is out of cache + scrollGridVerticallyTo(0); assertFalse( "row shouldn't be selected when scrolling " + "back into view", getRow(0).isSelected()); @@ -115,35 +99,36 @@ public class GridSelectionTest extends GridBasicFeaturesTest { grid.getRow(0).isSelected()); toggleFirstRowSelection(); assertTrue("First row was not selected.", getRow(0).isSelected()); - assertTrue("Selection event was not correct", - logContainsText("Added 0, Removed none")); + assertTrue("Selection event was not correct", logContainsText( + "SingleSelectionEvent: Selected: DataObject[0]")); grid.getCell(5, 0).click(); assertTrue("Fifth row was not selected.", getRow(5).isSelected()); assertFalse("First row was still selected.", getRow(0).isSelected()); - assertTrue("Selection event was not correct", - logContainsText("Added 5, Removed 0")); + assertTrue("Selection event was not correct", logContainsText( + "SingleSelectionEvent: Selected: DataObject[5]")); grid.getCell(0, 6).click(); - assertTrue("Selection event was not correct", - logContainsText("Added 0, Removed 5")); + assertTrue("Selection event was not correct", logContainsText( + "SingleSelectionEvent: Selected: DataObject[0]")); toggleFirstRowSelection(); assertTrue("Selection event was not correct", - logContainsText("Added none, Removed 0")); + logContainsText("SingleSelectionEvent: Selected: none")); assertFalse("First row was still selected.", getRow(0).isSelected()); assertFalse("Fifth row was still selected.", getRow(5).isSelected()); grid.scrollToRow(600); - grid.getCell(595, 3).click(); + grid.getCell(595, 4).click(); assertTrue("Row 595 was not selected.", getRow(595).isSelected()); - assertTrue("Selection event was not correct", - logContainsText("Added 595, Removed none")); + assertTrue("Selection event was not correct", logContainsText( + "SingleSelectionEvent: Selected: DataObject[595]")); toggleFirstRowSelection(); assertFalse("Row 595 was still selected.", getRow(595).isSelected()); assertTrue("First row was not selected.", getRow(0).isSelected()); - assertTrue("Selection event was not correct", - logContainsText("Added 0, Removed 595")); + assertTrue("Selection event was not correct", logContainsText( + "SingleSelectionEvent: Selected: DataObject[0]")); } - @Test + // TODO enable once select with space key is added + // @Test public void testKeyboardSelection() { openTestURL(); setSelectionModelMulti(); @@ -168,7 +153,8 @@ public class GridSelectionTest extends GridBasicFeaturesTest { grid.getRow(3).isSelected()); } - @Test + // TODO enable once select with space key is added + // @Test public void testKeyboardWithSingleSelection() { openTestURL(); setSelectionModelSingle(); @@ -197,141 +183,17 @@ public class GridSelectionTest extends GridBasicFeaturesTest { !grid.getRow(3).isSelected()); } - @Test - public void testSelectAllCheckbox() { - openTestURL(); - - setSelectionModelMulti(); - GridCellElement header = getGridElement().getHeaderCell(0, 0); - - assertTrue("No checkbox", header.isElementPresent(By.tagName("input"))); - header.findElement(By.tagName("input")).click(); - - for (int i = 0; i < GridBasicFeatures.ROWS; i += 100) { - assertTrue("Row " + i + " was not selected.", - getGridElement().getRow(i).isSelected()); - } - - header.findElement(By.tagName("input")).click(); - assertFalse("Row 100 was still selected", - getGridElement().getRow(100).isSelected()); - } - - @Test - public void testSelectAllAndSort() { - openTestURL(); - - setSelectionModelMulti(); - GridCellElement header = getGridElement().getHeaderCell(0, 0); - - header.findElement(By.tagName("input")).click(); - - getGridElement().getHeaderCell(0, 1).click(); - - WebElement selectionBox = getGridElement().getCell(4, 0) - .findElement(By.tagName("input")); - selectionBox.click(); - selectionBox.click(); - - assertFalse("Exception occured on row reselection.", logContainsText( - "Exception occured, java.lang.IllegalStateException: No item id for key 101 found.")); - } - - @Test - public void testSelectAllCheckboxWhenChangingModels() { - openTestURL(); - - GridCellElement header; - header = getGridElement().getHeaderCell(0, 0); - assertFalse( - "Check box shouldn't have been in header for None Selection Model", - header.isElementPresent(By.tagName("input"))); - - setSelectionModelMulti(); - header = getGridElement().getHeaderCell(0, 0); - assertTrue("Multi Selection Model should have select all checkbox", - header.isElementPresent(By.tagName("input"))); - - setSelectionModelSingle(); - header = getGridElement().getHeaderCell(0, 0); - assertFalse( - "Check box shouldn't have been in header for Single Selection Model", - header.isElementPresent(By.tagName("input"))); - - // Single selection model shouldn't have selection column to begin with - assertFalse( - "Selection columnn shouldn't have been in grid for Single Selection Model", - getGridElement().getCell(0, 1) - .isElementPresent(By.tagName("input"))); - - setSelectionModelNone(); - header = getGridElement().getHeaderCell(0, 0); - assertFalse( - "Check box shouldn't have been in header for None Selection Model", - header.isElementPresent(By.tagName("input"))); - - } - - @Test - public void testSelectAllCheckboxWithHeaderOperations() { - openTestURL(); - - setSelectionModelMulti(); - selectMenuPath("Component", "Header", "Prepend row"); - selectMenuPath("Component", "Header", "Append row"); - - GridCellElement header = getGridElement().getHeaderCell(1, 0); - assertTrue("Multi Selection Model should have select all checkbox", - header.isElementPresent(By.tagName("input"))); - - } - - @Test - public void testToggleDeselectAllowed() { - openTestURL(); - - setSelectionModelSingle(); - // Deselect allowed already enabled - - getGridElement().getCell(5, 1).click(); - getGridElement().getCell(5, 1).click(); - assertFalse("Row should be not selected after two clicks", - getRow(5).isSelected()); - - selectMenuPath("Component", "State", "Single select allow deselect"); - getGridElement().getCell(5, 1).click(); - getGridElement().getCell(5, 1).click(); - assertTrue("Row should be selected after two clicks", - getRow(5).isSelected()); - - selectMenuPath("Component", "State", "Single select allow deselect"); - getGridElement().getCell(5, 1).click(); - assertFalse("Row should be not selected after another click", - getRow(5).isSelected()); - - // Also verify that state is updated together with the model - setSelectionModelNone(); - selectMenuPath("Component", "State", "Single select allow deselect"); - setSelectionModelSingle(); - - getGridElement().getCell(5, 1).click(); - getGridElement().getCell(5, 1).click(); - - assertTrue("Row should stay selected after two clicks", - getRow(5).isSelected()); - } - @Test public void testChangeSelectionModelUpdatesUI() { openTestURL(); - setSelectionModelSingle(); + setSelectionModelMulti(); getGridElement().getCell(5, 1).click(); assertTrue("Row should be selected after clicking", getRow(5).isSelected()); - setSelectionModelNone(); + setSelectionModelSingle(); assertFalse("Row should not be selected after changing selection model", getRow(5).isSelected()); } @@ -348,13 +210,16 @@ public class GridSelectionTest extends GridBasicFeaturesTest { .getAttribute("class") .contains("v-grid-selection-checkbox")); - GridCellElement header = getGridElement().getHeaderCell(0, 0); - assertTrue("Select all CheckBox should have the proper style name set", - header.findElement(By.tagName("span")).getAttribute("class") - .contains("v-grid-select-all-checkbox")); + // TODO enable once select all is added + // GridCellElement header = getGridElement().getHeaderCell(0, 0); + // assertTrue("Select all CheckBox should have the proper style name + // set", + // header.findElement(By.tagName("span")).getAttribute("class") + // .contains("v-grid-select-all-checkbox")); } - @Test + // TODO enable once select all is added + // @Test public void testServerSideSelectTogglesSelectAllCheckBox() { openTestURL(); @@ -388,7 +253,7 @@ public class GridSelectionTest extends GridBasicFeaturesTest { setSelectionModelSingle(); getGridElement().getCell(0, 0).click(); - selectMenuPath("Component", "Body rows", "Remove selected rows"); + selectMenuPath("Component", "Body rows", "Deselect all"); assertFalse( "Unexpected NullPointerException when removing selected rows", @@ -407,23 +272,12 @@ public class GridSelectionTest extends GridBasicFeaturesTest { }, 5); } - private void setSelectionModelMulti() { - selectMenuPath("Component", "State", "Selection mode", "multi"); - } - - private void setSelectionModelSingle() { - selectMenuPath("Component", "State", "Selection mode", "single"); - } - - private void setSelectionModelNone() { - selectMenuPath("Component", "State", "Selection mode", "none"); - } - private void toggleFirstRowSelection() { - selectMenuPath("Component", "Body rows", "Select first row"); + selectMenuPath("Component", "Body rows", "Toggle first row selection"); } private GridRowElement getRow(int i) { return getGridElement().getRow(i); } + } diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicSelectionTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicSelectionTest.java index 2c329ca01c..82de23a90b 100644 --- a/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicSelectionTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicSelectionTest.java @@ -46,32 +46,32 @@ public class GridBasicSelectionTest extends GridBasicsTest { grid.getRow(0).isSelected()); toggleFirstRowSelection(); assertTrue("First row was not selected.", getRow(0).isSelected()); - assertTrue("Selection event was not correct", - logContainsText("Selected: DataObject[0]")); + assertTrue("Selection event was not correct", logContainsText( + "SingleSelectionEvent: Selected: DataObject[0]")); grid.getCell(5, 0).click(); assertTrue("Fifth row was not selected.", getRow(5).isSelected()); assertFalse("First row was still selected.", getRow(0).isSelected()); - assertTrue("Selection event was not correct", - logContainsText("Selected: DataObject[5]")); - grid.getCell(0, 6).click(); - assertTrue("Selection event was not correct", - logContainsText("Selected: DataObject[0]")); + assertTrue("Selection event was not correct", logContainsText( + "SingleSelectionEvent: Selected: DataObject[5]")); + grid.getCell(0, 3).click(); + assertTrue("Selection event was not correct", logContainsText( + "SingleSelectionEvent: Selected: DataObject[0]")); toggleFirstRowSelection(); assertTrue("Selection event was not correct", - logContainsText("Selected: null")); + logContainsText("SingleSelectionEvent: Selected: none")); assertFalse("First row was still selected.", getRow(0).isSelected()); assertFalse("Fifth row was still selected.", getRow(5).isSelected()); grid.scrollToRow(600); grid.getCell(595, 3).click(); assertTrue("Row 595 was not selected.", getRow(595).isSelected()); - assertTrue("Selection event was not correct", - logContainsText("Selected: DataObject[595]")); + assertTrue("Selection event was not correct", logContainsText( + "SingleSelectionEvent: Selected: DataObject[595]")); toggleFirstRowSelection(); assertFalse("Row 595 was still selected.", getRow(595).isSelected()); assertTrue("First row was not selected.", getRow(0).isSelected()); - assertTrue("Selection event was not correct", - logContainsText("Selected: DataObject[0]")); + assertTrue("Selection event was not correct", logContainsText( + "SingleSelectionEvent: Selected: DataObject[0]")); } private void toggleFirstRowSelection() { diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicsTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicsTest.java index 55098eb768..ff6789f600 100644 --- a/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicsTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicsTest.java @@ -26,6 +26,8 @@ import com.vaadin.v7.tests.components.grid.basicfeatures.GridBasicFeaturesTest.C */ public abstract class GridBasicsTest extends MultiBrowserTest { + public static final int ROWS = 1000; + /* Identical List of test data */ private List testData; @@ -131,7 +133,7 @@ public abstract class GridBasicsTest extends MultiBrowserTest { return 5; } else { int half = columnHeader.getSize().getWidth() / 2; - return half + (half / 2); + return half + half / 2; } } @@ -197,8 +199,7 @@ public abstract class GridBasicsTest extends MultiBrowserTest { List elements = sidebar .findElements(By.className("column-hiding-toggle")); for (WebElement e : elements) { - if ((e.getText().toLowerCase()) - .startsWith("column " + columnIndex)) { + if (e.getText().toLowerCase().startsWith("column " + columnIndex)) { return e; } } @@ -214,4 +215,17 @@ public abstract class GridBasicsTest extends MultiBrowserTest { getGridVerticalScrollbar())).intValue(); } + protected void setSelectionModelMulti() { + selectMenuPath("Component", "State", "Selection model", "multi"); + } + + protected void setSelectionModelSingle() { + selectMenuPath("Component", "State", "Selection model", "single"); + } + + // TODO enable once select all is added + // protected void setSelectionModelNone() { + // selectMenuPath("Component", "State", "Selection model", "none"); + // } + } diff --git a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridSelectAllCellTest.java b/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridSelectAllCellTest.java deleted file mode 100644 index 6b1feed7c1..0000000000 --- a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridSelectAllCellTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.vaadin.v7.tests.components.grid; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.is; - -import java.io.IOException; - -import org.junit.Test; -import org.openqa.selenium.By; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.interactions.Actions; - -import com.vaadin.tests.tb3.MultiBrowserTest; -import com.vaadin.testbench.customelements.GridElement; - -public class GridSelectAllCellTest extends MultiBrowserTest { - - @Override - public void setup() throws Exception { - super.setup(); - - openTestURL(); - } - - @Test - public void selectAllCellCanBeClicked() throws IOException { - GridElement.GridCellElement selectAllCell = $(GridElement.class).first() - .getHeaderCell(0, 0); - - new Actions(getDriver()).moveToElement(selectAllCell, 2, 2).click() - .perform(); - - WebElement selectAllCheckbox = selectAllCell - .findElement(By.cssSelector("input")); - assertThat(selectAllCheckbox.isSelected(), is(true)); - } -} -- 2.39.5