diff options
author | Denis <denis@vaadin.com> | 2016-12-06 21:38:08 +0200 |
---|---|---|
committer | Pekka Hyvönen <pekka@vaadin.com> | 2016-12-06 21:38:08 +0200 |
commit | 620b2ba314f0f88760803a084cc38e47b57632d3 (patch) | |
tree | 5fcb2d08009e874cecf08669a74f041b0622a741 | |
parent | 77fd42425b59805b5c30dcafc3512eecaa78e24c (diff) | |
download | vaadin-framework-620b2ba314f0f88760803a084cc38e47b57632d3.tar.gz vaadin-framework-620b2ba314f0f88760803a084cc38e47b57632d3.zip |
Add read-only support for single and multi select views for Grid. (#84)
* Add read-only support for single and multi select views for Grid.
Fixes vaadin/framework8-issues#516
17 files changed, 524 insertions, 99 deletions
diff --git a/client/src/main/java/com/vaadin/client/connectors/grid/AbstractSelectionModelConnector.java b/client/src/main/java/com/vaadin/client/connectors/grid/AbstractSelectionModelConnector.java index 5f705cb237..114bc313d6 100644 --- a/client/src/main/java/com/vaadin/client/connectors/grid/AbstractSelectionModelConnector.java +++ b/client/src/main/java/com/vaadin/client/connectors/grid/AbstractSelectionModelConnector.java @@ -16,6 +16,7 @@ package com.vaadin.client.connectors.grid; import com.vaadin.client.ServerConnector; +import com.vaadin.client.annotations.OnStateChange; import com.vaadin.client.extensions.AbstractExtensionConnector; import com.vaadin.client.widget.grid.selection.SelectionModel; import com.vaadin.client.widgets.Grid; @@ -61,6 +62,12 @@ public abstract class AbstractSelectionModelConnector return getParent().getWidget(); } + @OnStateChange("selectionAllowed") + private void onSelectionAllowedChange() { + getGrid().getSelectionModel() + .setSelectionAllowed(getState().selectionAllowed); + } + @Override public AbstractSelectionModelState getState() { return (AbstractSelectionModelState) super.getState(); 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 index 8ad86f1541..83008b5a88 100644 --- a/client/src/main/java/com/vaadin/client/connectors/grid/MultiSelectionModelConnector.java +++ b/client/src/main/java/com/vaadin/client/connectors/grid/MultiSelectionModelConnector.java @@ -22,6 +22,7 @@ import com.vaadin.client.annotations.OnStateChange; import com.vaadin.client.data.DataSource; import com.vaadin.client.data.DataSource.RowHandle; import com.vaadin.client.renderers.Renderer; +import com.vaadin.client.widget.grid.events.GridSelectionAllowedEvent; import com.vaadin.client.widget.grid.events.SelectAllEvent; import com.vaadin.client.widget.grid.selection.MultiSelectionRenderer; import com.vaadin.client.widget.grid.selection.SelectionModel; @@ -64,6 +65,8 @@ public class MultiSelectionModelConnector protected class MultiSelectionModel implements SelectionModel<JsonObject>, SelectionModelWithSelectionColumn { + private boolean isSelectionAllowed = true; + @Override public Renderer<Boolean> getRenderer() { // this method is only called once when the selection model is set @@ -106,6 +109,18 @@ public class MultiSelectionModelConnector return MultiSelectionModelConnector.this.isSelected(item); } + @Override + public void setSelectionAllowed(boolean selectionAllowed) { + isSelectionAllowed = selectionAllowed; + getGrid() + .fireEvent(new GridSelectionAllowedEvent(selectionAllowed)); + } + + @Override + public boolean isSelectionAllowed() { + return isSelectionAllowed; + } + } @Override diff --git a/client/src/main/java/com/vaadin/client/connectors/grid/NoSelectionModelConnector.java b/client/src/main/java/com/vaadin/client/connectors/grid/NoSelectionModelConnector.java index 383fae2882..bc21b5990e 100644 --- a/client/src/main/java/com/vaadin/client/connectors/grid/NoSelectionModelConnector.java +++ b/client/src/main/java/com/vaadin/client/connectors/grid/NoSelectionModelConnector.java @@ -20,8 +20,6 @@ import com.vaadin.client.extensions.AbstractExtensionConnector; import com.vaadin.client.widget.grid.selection.SelectionModel; import com.vaadin.shared.ui.Connect; -import elemental.json.JsonObject; - /** * Connector for grids selection model that doesn't allow selecting anything. * @@ -37,25 +35,7 @@ public class NoSelectionModelConnector extends AbstractExtensionConnector { assert target instanceof GridConnector : "NoSelectionModelConnector cannot extend anything else than Grid."; ((GridConnector) target).getWidget() - .setSelectionModel(new SelectionModel<JsonObject>() { - - @Override - public void select(JsonObject item) { - } - - @Override - public void deselect(JsonObject item) { - } - - @Override - public boolean isSelected(JsonObject item) { - return false; - } - - @Override - public void deselectAll() { - } - }); + .setSelectionModel(new SelectionModel.NoSelectionModel<>()); } } 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 d18f372a2c..9b1d7cc27a 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,6 +15,7 @@ */ package com.vaadin.client.connectors.grid; +import com.vaadin.client.widget.grid.events.GridSelectionAllowedEvent; import com.vaadin.client.widget.grid.selection.ClickSelectHandler; import com.vaadin.client.widget.grid.selection.SelectionModel; import com.vaadin.shared.data.DataCommunicatorConstants; @@ -41,6 +42,8 @@ public class SingleSelectionModelConnector */ protected class SingleSelectionModel implements SelectionModel<JsonObject> { + private boolean isSelectionAllowed = true; + @Override public void select(JsonObject item) { getRpcProxy(SelectionServerRpc.class) @@ -62,6 +65,18 @@ public class SingleSelectionModelConnector public void deselectAll() { getRpcProxy(SelectionServerRpc.class).select(null); } + + @Override + public void setSelectionAllowed(boolean selectionAllowed) { + isSelectionAllowed = selectionAllowed; + getGrid() + .fireEvent(new GridSelectionAllowedEvent(selectionAllowed)); + } + + @Override + public boolean isSelectionAllowed() { + return isSelectionAllowed; + } } @Override diff --git a/client/src/main/java/com/vaadin/client/widget/grid/events/GridSelectionAllowedEvent.java b/client/src/main/java/com/vaadin/client/widget/grid/events/GridSelectionAllowedEvent.java new file mode 100644 index 0000000000..e0aeed8871 --- /dev/null +++ b/client/src/main/java/com/vaadin/client/widget/grid/events/GridSelectionAllowedEvent.java @@ -0,0 +1,63 @@ +/* + * 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.widget.grid.events; + +import com.google.gwt.event.shared.GwtEvent; + +/** + * A selection allowed event, fired by the Grid when its selection allowed value + * changes. + * + * @since 8.0 + * @author Vaadin Ltd + */ +public class GridSelectionAllowedEvent + extends GwtEvent<GridSelectionAllowedHandler> { + /** + * The type of this event + */ + public static final Type<GridSelectionAllowedHandler> TYPE = new Type<>(); + private final boolean isSelectionAllowed; + + /** + * Creates a new event instance. + * + * @param selectionAllowed + * selection allowed value + */ + public GridSelectionAllowedEvent(boolean selectionAllowed) { + isSelectionAllowed = selectionAllowed; + } + + @Override + public Type<GridSelectionAllowedHandler> getAssociatedType() { + return TYPE; + } + + /** + * Gets selection allowed value. + * + * @return {@code true} if selection is allowed, {@code false} otherwise + */ + public boolean isSelectionAllowed() { + return isSelectionAllowed; + } + + @Override + protected void dispatch(final GridSelectionAllowedHandler handler) { + handler.onSelectionAllowed(this); + } +} diff --git a/client/src/main/java/com/vaadin/client/widget/grid/events/GridSelectionAllowedHandler.java b/client/src/main/java/com/vaadin/client/widget/grid/events/GridSelectionAllowedHandler.java new file mode 100644 index 0000000000..23788b329b --- /dev/null +++ b/client/src/main/java/com/vaadin/client/widget/grid/events/GridSelectionAllowedHandler.java @@ -0,0 +1,39 @@ +/* + * 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.widget.grid.events; + +import com.google.gwt.event.shared.EventHandler; + +/** + * Handler for a Grid {@link GridSelectionAllowedEvent}, called when the Grid is + * becomes allowed for selection or disallowed. + * + * @see GridSelectionAllowedEvent + * @author Vaadin Ltd + * @since 8.0 + * + */ +public interface GridSelectionAllowedHandler extends EventHandler { + + /** + * Called when Grid selection is allowed value changes. + * + * @param event + * the {@link GridSelectionAllowedEvent} that was fired + * + */ + public void onSelectionAllowed(GridSelectionAllowedEvent event); +} diff --git a/client/src/main/java/com/vaadin/client/widget/grid/selection/ClickSelectHandler.java b/client/src/main/java/com/vaadin/client/widget/grid/selection/ClickSelectHandler.java index 7d24fd8488..2f96909f9d 100644 --- a/client/src/main/java/com/vaadin/client/widget/grid/selection/ClickSelectHandler.java +++ b/client/src/main/java/com/vaadin/client/widget/grid/selection/ClickSelectHandler.java @@ -36,6 +36,9 @@ public class ClickSelectHandler<T> { @Override public void onClick(GridClickEvent event) { + if (!grid.getSelectionModel().isSelectionAllowed()) { + return; + } T row = (T) event.getTargetCell().getRow(); if (!grid.isSelected(row)) { grid.select(row); diff --git a/client/src/main/java/com/vaadin/client/widget/grid/selection/MultiSelectionRenderer.java b/client/src/main/java/com/vaadin/client/widget/grid/selection/MultiSelectionRenderer.java index e1f9ebb18e..14c2210696 100644 --- a/client/src/main/java/com/vaadin/client/widget/grid/selection/MultiSelectionRenderer.java +++ b/client/src/main/java/com/vaadin/client/widget/grid/selection/MultiSelectionRenderer.java @@ -42,8 +42,9 @@ import com.vaadin.client.WidgetUtil; import com.vaadin.client.renderers.ClickableRenderer; import com.vaadin.client.widget.grid.CellReference; import com.vaadin.client.widget.grid.RendererCellReference; -import com.vaadin.client.widget.grid.events.GridEnabledEvent; import com.vaadin.client.widget.grid.events.GridEnabledHandler; +import com.vaadin.client.widget.grid.events.GridSelectionAllowedEvent; +import com.vaadin.client.widget.grid.events.GridSelectionAllowedHandler; import com.vaadin.client.widgets.Grid; /** @@ -76,8 +77,9 @@ public class MultiSelectionRenderer<T> * * @since 7.5 */ - private final class CheckBoxEventHandler implements MouseDownHandler, - TouchStartHandler, ClickHandler, GridEnabledHandler { + private final class CheckBoxEventHandler + implements MouseDownHandler, TouchStartHandler, ClickHandler, + GridEnabledHandler, GridSelectionAllowedHandler { private final CheckBox checkBox; /** @@ -114,7 +116,17 @@ public class MultiSelectionRenderer<T> @Override public void onEnabled(boolean enabled) { - checkBox.setEnabled(enabled); + updateEnable(); + } + + @Override + public void onSelectionAllowed(GridSelectionAllowedEvent event) { + updateEnable(); + } + + private void updateEnable() { + checkBox.setEnabled(grid.isEnabled() + && grid.getSelectionModel().isSelectionAllowed()); } } @@ -607,9 +619,8 @@ public class MultiSelectionRenderer<T> checkBox.addMouseDownHandler(handler); checkBox.addTouchStartHandler(handler); checkBox.addClickHandler(handler); - grid.addHandler(handler, GridEnabledEvent.TYPE); - - checkBox.setEnabled(grid.isEnabled()); + grid.addEnabledHandler(handler); + grid.addSelectionAllowedHandler(handler); return checkBox; } @@ -618,7 +629,8 @@ public class MultiSelectionRenderer<T> public void render(final RendererCellReference cell, final Boolean data, CheckBox checkBox) { checkBox.setValue(data, false); - checkBox.setEnabled(grid.isEnabled() && !grid.isEditorActive()); + checkBox.setEnabled(grid.isEnabled() && !grid.isEditorActive() + && grid.getSelectionModel().isSelectionAllowed()); checkBox.getElement().setPropertyInt(LOGICAL_ROW_PROPERTY_INT, cell.getRowIndex()); } diff --git a/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModel.java b/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModel.java index 27e099ad46..28499621df 100644 --- a/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModel.java +++ b/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModel.java @@ -31,6 +31,35 @@ import elemental.json.JsonObject; */ public interface SelectionModel<T> { + public static class NoSelectionModel<T> implements SelectionModel<T> { + + @Override + public void select(T item) { + } + + @Override + public void deselect(T item) { + } + + @Override + public boolean isSelected(T item) { + return false; + } + + @Override + public void deselectAll() { + } + + @Override + public void setSelectionAllowed(boolean selectionAllowed) { + } + + @Override + public boolean isSelectionAllowed() { + return false; + } + } + /** * Selects the given item. If another item was already selected, that item * is deselected. @@ -64,6 +93,29 @@ public interface SelectionModel<T> { void deselectAll(); /** + * Sets whether the user is allowed to change the selection. + * <p> + * The check is done only for the client side actions. It doesn't affect + * selection requests sent from the server side. + * + * @param selectionAllowed + * <code>true</code> if the user is allowed to change the + * selection, <code>false</code> otherwise + */ + void setSelectionAllowed(boolean selectionAllowed); + + /** + * Checks if the user is allowed to change the selection. + * <p> + * The check is done only for the client side actions. It doesn't affect + * selection requests sent from the server side. + * + * @return <code>true</code> if the user is allowed to change the selection, + * <code>false</code> otherwise + */ + boolean isSelectionAllowed(); + + /** * Gets the selected state from a given grid row json object. This is a * helper method for grid selection models. * 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 afed21b045..368ad8693b 100644 --- a/client/src/main/java/com/vaadin/client/widgets/Grid.java +++ b/client/src/main/java/com/vaadin/client/widgets/Grid.java @@ -145,6 +145,8 @@ import com.vaadin.client.widget.grid.events.GridEnabledHandler; import com.vaadin.client.widget.grid.events.GridKeyDownEvent; import com.vaadin.client.widget.grid.events.GridKeyPressEvent; import com.vaadin.client.widget.grid.events.GridKeyUpEvent; +import com.vaadin.client.widget.grid.events.GridSelectionAllowedEvent; +import com.vaadin.client.widget.grid.events.GridSelectionAllowedHandler; import com.vaadin.client.widget.grid.events.HeaderClickHandler; import com.vaadin.client.widget.grid.events.HeaderDoubleClickHandler; import com.vaadin.client.widget.grid.events.HeaderKeyDownHandler; @@ -1870,15 +1872,12 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, grid.isSelected(pinnedRowHandle.getRow())); checkBox.sinkEvents(Event.ONCLICK); - checkBox.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - T row = pinnedRowHandle.getRow(); - if (grid.isSelected(row)) { - grid.deselect(row); - } else { - grid.select(row); - } + checkBox.addClickHandler(event -> { + T row = pinnedRowHandle.getRow(); + if (grid.isSelected(row)) { + grid.deselect(row); + } else { + grid.select(row); } }); grid.attachWidget(checkBox, cell); @@ -2832,7 +2831,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, } public final class SelectionColumn extends Column<Boolean, T> - implements GridEnabledHandler { + implements GridEnabledHandler, GridSelectionAllowedHandler { private boolean initDone = false; private boolean selected = false; @@ -2844,6 +2843,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, super(selectColumnRenderer); addEnabledHandler(this); + addSelectionAllowedHandler(this); } void initDone() { @@ -2851,6 +2851,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, setEditable(false); setResizable(false); + updateEnable(); initDone = true; } @@ -2951,23 +2952,9 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, return this; } - /** - * Sets whether the selection column is enabled. - * - * @since 7.7 - * @param enabled - * <code>true</code> to enable the column, <code>false</code> - * to disable it. - */ - public void setEnabled(boolean enabled) { - if (selectAllCheckBox != null) { - selectAllCheckBox.setEnabled(enabled); - } - } - @Override public void onEnabled(boolean enabled) { - setEnabled(enabled); + updateEnable(); } /** @@ -3024,19 +3011,30 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, } } - private void onHeaderClickEvent(GridClickEvent event) { - CellReference<?> targetCell = event.getTargetCell(); - int defaultRowIndex = getHeader().getRows() - .indexOf(getDefaultHeaderRow()); + private void updateEnable() { + if (selectAllCheckBox != null) { + selectAllCheckBox.setEnabled(isEnabled() + && getSelectionModel().isSelectionAllowed()); + } + } - if (targetCell.getColumnIndex() == 0 - && targetCell.getRowIndex() == defaultRowIndex) { - selectAllCheckBox.setValue(!selectAllCheckBox.getValue(), true); + private void onHeaderClickEvent(GridClickEvent event) { + if (selectAllCheckBox.isEnabled()) { + CellReference<?> targetCell = event.getTargetCell(); + int defaultRowIndex = getHeader().getRows() + .indexOf(getDefaultHeaderRow()); + + if (targetCell.getColumnIndex() == 0 + && targetCell.getRowIndex() == defaultRowIndex) { + selectAllCheckBox.setValue(!selectAllCheckBox.getValue(), + true); + } } } private void onHeaderKeyUpEvent(GridKeyUpEvent event) { - if (event.getNativeKeyCode() != KeyCodes.KEY_SPACE) { + if (event.getNativeKeyCode() != KeyCodes.KEY_SPACE + || !selectAllCheckBox.isEnabled()) { return; } HeaderRow targetHeaderRow = getHeader() @@ -3050,6 +3048,11 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, } } + @Override + public void onSelectionAllowed(GridSelectionAllowedEvent event) { + updateEnable(); + } + } /** @@ -5906,26 +5909,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, editor.setGrid(this); - setSelectionModel(new SelectionModel<T>() { - - @Override - public void select(T item) { - } - - @Override - public void deselect(T item) { - } - - @Override - public boolean isSelected(T item) { - return false; - } - - @Override - public void deselectAll() { - } - - }); + setSelectionModel(new SelectionModel.NoSelectionModel<>()); escalator.getBody().setSpacerUpdater(gridSpacerUpdater); @@ -7590,7 +7574,6 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, addColumnSkipSelectionColumnCheck(selectionColumn, 0); - selectionColumn.setEnabled(isEnabled()); selectionColumn.initDone(); } else { selectionColumn = null; @@ -7662,7 +7645,9 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, * {@link SelectionModel.Single} or {@link SelectionModel.Multi} */ public void select(T row) { - getSelectionModel().select(row); + if (getSelectionModel().isSelectionAllowed()) { + getSelectionModel().select(row); + } } /** @@ -7679,7 +7664,9 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, * {@link SelectionModel.Single} or {@link SelectionModel.Multi} */ public void deselect(T row) { - getSelectionModel().deselect(row); + if (getSelectionModel().isSelectionAllowed()) { + getSelectionModel().deselect(row); + } } /** @@ -8101,6 +8088,20 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, return addHandler(handler, GridEnabledEvent.TYPE); } + /** + * Register a selection allowed status change handler to this Grid. The + * event for this handler is fired when the Grid changes selection allowed + * state. + * + * @param handler + * the handler for the event + * @return the registration for the event + */ + public HandlerRegistration addSelectionAllowedHandler( + GridSelectionAllowedHandler handler) { + return addHandler(handler, GridSelectionAllowedEvent.TYPE); + } + public HandlerRegistration addRowHeightChangedHandler( RowHeightChangedHandler handler) { return escalator.addHandler(handler, RowHeightChangedEvent.TYPE); 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 index f85c781345..9c2b506107 100644 --- a/server/src/main/java/com/vaadin/ui/components/grid/MultiSelectionModelImpl.java +++ b/server/src/main/java/com/vaadin/ui/components/grid/MultiSelectionModelImpl.java @@ -324,16 +324,12 @@ public class MultiSelectionModelImpl<T> extends AbstractSelectionModel<T> @Override public void setReadOnly(boolean readOnly) { - // TODO support read only in grid ? - throw new UnsupportedOperationException( - "Read only mode is not supported for grid."); + getState().selectionAllowed = readOnly; } @Override public boolean isReadOnly() { - // TODO support read only in grid ? - throw new UnsupportedOperationException( - "Read only mode is not supported for grid."); + return isUserSelectionAllowed(); } @Override @@ -429,6 +425,11 @@ public class MultiSelectionModelImpl<T> extends AbstractSelectionModel<T> Objects.requireNonNull(addedItems); Objects.requireNonNull(removedItems); + if (userOriginated && !isUserSelectionAllowed()) { + throw new IllegalStateException("Client tried to update selection" + + " although user selection is disallowed"); + } + // 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)); @@ -457,6 +458,10 @@ public class MultiSelectionModelImpl<T> extends AbstractSelectionModel<T> }, userOriginated); } + private boolean isUserSelectionAllowed() { + return getState(false).selectionAllowed; + } + private void doUpdateSelection(Consumer<Set<T>> handler, boolean userOriginated) { if (getParent() == null) { diff --git a/server/src/main/java/com/vaadin/ui/components/grid/SingleSelectionModelImpl.java b/server/src/main/java/com/vaadin/ui/components/grid/SingleSelectionModelImpl.java index ac1edaf576..6db6bfa61c 100644 --- a/server/src/main/java/com/vaadin/ui/components/grid/SingleSelectionModelImpl.java +++ b/server/src/main/java/com/vaadin/ui/components/grid/SingleSelectionModelImpl.java @@ -169,6 +169,10 @@ public class SingleSelectionModelImpl<T> extends AbstractSelectionModel<T> * selection */ protected void setSelectedFromClient(String key) { + if (!isUserSelectionAllowed()) { + throw new IllegalStateException("Client tried to update selection" + + " although user selection is disallowed"); + } if (isKeySelected(key)) { return; } @@ -224,6 +228,10 @@ public class SingleSelectionModelImpl<T> extends AbstractSelectionModel<T> } } + private boolean isUserSelectionAllowed() { + return getState(false).selectionAllowed; + } + /** * Gets a wrapper for using this grid as a single select in a binder. * @@ -267,15 +275,12 @@ public class SingleSelectionModelImpl<T> extends AbstractSelectionModel<T> @Override public void setReadOnly(boolean readOnly) { - // TODO support read only when grid is used in binder ? - throw new UnsupportedOperationException( - "Read only is not supported for Grid."); + getState().selectionAllowed = readOnly; } @Override public boolean isReadOnly() { - throw new UnsupportedOperationException( - "Read only is not supported for Grid."); + return isUserSelectionAllowed(); } }; } 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 index 3a57311bd7..aa601c67be 100644 --- a/server/src/test/java/com/vaadin/tests/components/grid/GridMultiSelectionModelTest.java +++ b/server/src/test/java/com/vaadin/tests/components/grid/GridMultiSelectionModelTest.java @@ -76,6 +76,21 @@ public class GridMultiSelectionModelTest { } } + private static class TestMultiSelectionModel + extends MultiSelectionModelImpl<Object> { + + public TestMultiSelectionModel() { + getState(false).selectionAllowed = false; + } + + @Override + protected void updateSelection(Set<Object> addedItems, + Set<Object> removedItems, boolean userOriginated) { + super.updateSelection(addedItems, removedItems, userOriginated); + } + + } + @Before public void setUp() { grid = new Grid<>(); @@ -98,6 +113,13 @@ public class GridMultiSelectionModelTest { } @Test(expected = IllegalStateException.class) + public void throwExcpetionWhenSelectionIsDisallowed() { + TestMultiSelectionModel model = new TestMultiSelectionModel(); + model.updateSelection(Collections.emptySet(), Collections.emptySet(), + true); + } + + @Test(expected = IllegalStateException.class) public void selectionModelChanged_usingPreviousSelectionModel_throws() { grid.setSelectionMode(SelectionMode.SINGLE); 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 06ceb8bfc3..bae7c9c19b 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 @@ -48,6 +48,19 @@ public class GridSingleSelectionModelTest { } + private static class TestSingleSelectionModel + extends SingleSelectionModelImpl<Object> { + + public TestSingleSelectionModel() { + getState(false).selectionAllowed = false; + } + + @Override + protected void setSelectedFromClient(String key) { + super.setSelectedFromClient(key); + } + } + private List<Person> selectionChanges; private Grid<Person> grid; private SingleSelectionModelImpl<Person> selectionModel; @@ -65,6 +78,12 @@ public class GridSingleSelectionModelTest { } @Test(expected = IllegalStateException.class) + public void throwExceptionWhenSelectionIsDisallowed() { + TestSingleSelectionModel model = new TestSingleSelectionModel(); + model.setSelectedFromClient("foo"); + } + + @Test(expected = IllegalStateException.class) public void selectionModelChanged_usingPreviousSelectionModel_throws() { grid.setSelectionMode(SelectionMode.MULTI); diff --git a/shared/src/main/java/com/vaadin/shared/ui/grid/AbstractSelectionModelState.java b/shared/src/main/java/com/vaadin/shared/ui/grid/AbstractSelectionModelState.java index 9cd29341b1..4e0e05c8bd 100644 --- a/shared/src/main/java/com/vaadin/shared/ui/grid/AbstractSelectionModelState.java +++ b/shared/src/main/java/com/vaadin/shared/ui/grid/AbstractSelectionModelState.java @@ -25,4 +25,9 @@ import com.vaadin.shared.communication.SharedState; * @since 8.0 */ public class AbstractSelectionModelState extends SharedState { + + /** + * Whether the selection model allows selection from the client side. + */ + public boolean selectionAllowed = true; } 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 70b7eab470..430692477d 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 @@ -36,8 +36,10 @@ import com.vaadin.ui.Label; import com.vaadin.ui.MenuBar; import com.vaadin.ui.MenuBar.Command; import com.vaadin.ui.MenuBar.MenuItem; +import com.vaadin.ui.MultiSelect; import com.vaadin.ui.Notification; import com.vaadin.ui.Panel; +import com.vaadin.ui.SingleSelect; import com.vaadin.ui.StyleGenerator; import com.vaadin.ui.TextField; import com.vaadin.ui.VerticalLayout; @@ -64,6 +66,8 @@ public class GridBasics extends AbstractTestUIWithLog { public static final String CELL_STYLE_GENERATOR_EMPTY = "Empty string"; public static final String CELL_STYLE_GENERATOR_NULL = "Null"; + private boolean isUserSelectionAllowed = true; + public static final String[] COLUMN_CAPTIONS = { "Column 0", "Column 1", "Column 2", "Row Number", "Date", "HTML String", "Big Random", "Small Random" }; @@ -403,6 +407,23 @@ public class GridBasics extends AbstractTestUIWithLog { } }).setCheckable(true); + MenuItem selectionAllowedItem = stateMenu + .addItem("Allow user selection", item -> { + isUserSelectionAllowed = !isUserSelectionAllowed; + if (grid.getSelectionModel() instanceof MultiSelectionModelImpl) { + MultiSelect<DataObject> multiSelect = grid + .asMultiSelect(); + multiSelect.setReadOnly(isUserSelectionAllowed); + } + if (grid.getSelectionModel() instanceof SingleSelectionModelImpl) { + SingleSelect<DataObject> singleSelect = grid + .asSingleSelect(); + singleSelect.setReadOnly(isUserSelectionAllowed); + } + }); + selectionAllowedItem.setChecked(true); + selectionAllowedItem.setCheckable(true); + stateMenu.addItem("Column reorder listener", toggleReorderListenerCommand).setCheckable(true); @@ -492,6 +513,7 @@ public class GridBasics extends AbstractTestUIWithLog { selectionListenerRegistration = ((SingleSelectionModelImpl<DataObject>) grid .getSelectionModel()) .addSelectionListener(this::onSingleSelect); + grid.asSingleSelect().setReadOnly(isUserSelectionAllowed); }); selectionModelItem.addItem("multi", menuItem -> { switchToMultiSelect(); @@ -534,9 +556,12 @@ public class GridBasics extends AbstractTestUIWithLog { private void switchToMultiSelect() { if (!(grid.getSelectionModel() instanceof MultiSelectionModel)) { selectionListenerRegistration.remove(); - ((MultiSelectionModelImpl<DataObject>) grid - .setSelectionMode(SelectionMode.MULTI)) - .addSelectionListener(this::onMultiSelect); + MultiSelectionModelImpl<DataObject> model = (MultiSelectionModelImpl<DataObject>) grid + .setSelectionMode(SelectionMode.MULTI); + model.addSelectionListener(this::onMultiSelect); + grid.asMultiSelect().setReadOnly(isUserSelectionAllowed); + selectionListenerRegistration = model + .addSelectionListener(this::onMultiSelect); } } diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java index e42fcf2d69..6682d0bec5 100644 --- a/uitest/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java @@ -255,6 +255,163 @@ public class GridSelectionTest extends GridBasicsTest { "Exception occured, java.lang.NullPointerException: null")); } + @Test + public void singleSelectUserSelectionDisallowedSpaceSelectionNoOp() { + openTestURL(); + setSelectionModelSingle(); + getGridElement().focus(); + getGridElement().sendKeys(Keys.DOWN, Keys.SPACE); + assertTrue("row was selected when selection was allowed", + getRow(1).isSelected()); + toggleUserSelectionAllowed(); + getGridElement().sendKeys(Keys.SPACE); + assertTrue("deselect disallowed", getRow(1).isSelected()); + getGridElement().sendKeys(Keys.DOWN, Keys.SPACE); + assertFalse("select disallowed", getRow(2).isSelected()); + assertTrue("old selection remains", getRow(1).isSelected()); + toggleUserSelectionAllowed(); + getGridElement().sendKeys(Keys.SPACE); + assertTrue("select allowed again", getRow(2).isSelected()); + assertFalse("old selection removed", getRow(1).isSelected()); + + } + + @Test + public void singleSelectUserSelectionDisallowedClickSelectionNoOp() { + openTestURL(); + setSelectionModelSingle(); + getGridElement().getCell(1, 0).click(); + assertTrue("selection allowed, should have been selected", + getRow(1).isSelected()); + toggleUserSelectionAllowed(); + getGridElement().getCell(1, 0).click(); + assertTrue("deselect disallowed, should remain selected", + getRow(1).isSelected()); + getGridElement().getCell(2, 0).click(); + assertFalse("select disallowed, should not have been selected", + getRow(2).isSelected()); + assertTrue("select disallowed, old selection should have remained", + getRow(1).isSelected()); + toggleUserSelectionAllowed(); + getGridElement().getCell(2, 0).click(); + assertTrue("select allowed again, row should have been selected", + getRow(2).isSelected()); + assertFalse("old selection removed", getRow(1).isSelected()); + + } + + @Test + public void multiSelectUserSelectionDisallowedSpaceSelectionNoOp() { + openTestURL(); + setSelectionModelMulti(); + getGridElement().focus(); + getGridElement().sendKeys(Keys.DOWN, Keys.SPACE); + assertTrue("selection allowed, should have been selected", + getRow(1).isSelected()); + toggleUserSelectionAllowed(); + getGridElement().sendKeys(Keys.SPACE); + assertTrue("deselect disallowed, should remain selected", + getRow(1).isSelected()); + getGridElement().sendKeys(Keys.DOWN, Keys.SPACE); + assertFalse("select disallowed, should not have been selected", + getRow(2).isSelected()); + assertTrue("select disallowed, old selection should have remained", + getRow(1).isSelected()); + + toggleUserSelectionAllowed(); + getGridElement().sendKeys(Keys.SPACE); + assertTrue("select allowed again, row should have been selected", + getRow(2).isSelected()); + assertTrue( + "select allowed again but old selection should have remained", + getRow(1).isSelected()); + } + + @Test + public void multiSelectUserSelectionDisallowedCheckboxSelectionNoOp() { + openTestURL(); + setSelectionModelMulti(); + assertTrue(getSelectionCheckbox(0).isEnabled()); + toggleUserSelectionAllowed(); + assertFalse(getSelectionCheckbox(0).isEnabled()); + + // Select by clicking on checkbox (should always fail as it is disabled) + getSelectionCheckbox(0).click(); + assertFalse(getGridElement().getRow(0).isSelected()); + // Select by clicking on cell (should fail) + getGridElement().getCell(0, 0).click(); + assertFalse(getGridElement().getRow(0).isSelected()); + + toggleUserSelectionAllowed(); + assertTrue(getSelectionCheckbox(0).isEnabled()); + getSelectionCheckbox(0).click(); + assertTrue(getGridElement().getRow(0).isSelected()); + } + + @Test + public void multiSelectUserSelectionDisallowedCheckboxSelectAllNoOp() { + openTestURL(); + setSelectionModelMulti(); + + assertTrue(getSelectAllCheckbox().isEnabled()); + toggleUserSelectionAllowed(); + assertFalse(getSelectAllCheckbox().isEnabled()); + + // Select all by clicking on checkbox (should not select) + getSelectAllCheckbox().click(); + assertFalse(getSelectAllCheckbox().isSelected()); + assertFalse(getGridElement().getRow(0).isSelected()); + assertFalse(getGridElement().getRow(10).isSelected()); + + // Select all by clicking on header cell (should not select) + getGridElement().getHeaderCell(0, 0).click(); + assertFalse(getSelectAllCheckbox().isSelected()); + assertFalse(getGridElement().getRow(0).isSelected()); + assertFalse(getGridElement().getRow(10).isSelected()); + + // Select all by press SPACE on the header cell (should not select) + getGridElement().getHeaderCell(0, 0).sendKeys(Keys.SPACE); + assertFalse(getSelectAllCheckbox().isSelected()); + assertFalse(getGridElement().getRow(0).isSelected()); + assertFalse(getGridElement().getRow(10).isSelected()); + + toggleUserSelectionAllowed(); + + assertTrue(getSelectAllCheckbox().isEnabled()); + getSelectAllCheckbox().click(); + assertTrue(getGridElement().getRow(0).isSelected()); + assertTrue(getGridElement().getRow(10).isSelected()); + } + + @Test + public void singleSelectUserSelectionDisallowedServerSelect() { + openTestURL(); + setSelectionModelSingle(); + toggleUserSelectionAllowed(); + + toggleFirstRowSelection(); + assertTrue(getGridElement().getRow(0).isSelected()); + } + + @Test + public void multiSelectUserSelectionDisallowedServerSelect() { + openTestURL(); + setSelectionModelMulti(); + toggleUserSelectionAllowed(); + + toggleFirstRowSelection(); + assertTrue(getGridElement().getRow(0).isSelected()); + } + + private void toggleUserSelectionAllowed() { + selectMenuPath("Component", "State", "Allow user selection"); + } + + private WebElement getSelectionCheckbox(int row) { + return getGridElement().getCell(row, 0) + .findElement(By.tagName("input")); + } + private void waitUntilCheckBoxValue(final WebElement checkBoxElememnt, final boolean expectedValue) { waitUntil(new ExpectedCondition<Boolean>() { |