summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client/src/main/java/com/vaadin/client/connectors/grid/GridDropTargetConnector.java63
-rw-r--r--documentation/advanced/advanced-dragndrop.asciidoc11
-rw-r--r--server/src/main/java/com/vaadin/ui/components/grid/GridDropEvent.java27
-rw-r--r--server/src/main/java/com/vaadin/ui/components/grid/GridDropTarget.java116
-rw-r--r--server/src/test/java/com/vaadin/tests/components/grid/GridDropTargetTest.java152
-rw-r--r--shared/src/main/java/com/vaadin/shared/ui/grid/DropLocation.java5
-rw-r--r--shared/src/main/java/com/vaadin/shared/ui/grid/DropMode.java13
-rw-r--r--shared/src/main/java/com/vaadin/shared/ui/grid/GridDropTargetState.java1
-rw-r--r--uitest/src/main/java/com/vaadin/tests/components/grid/GridDragAndDrop.java9
9 files changed, 361 insertions, 36 deletions
diff --git a/client/src/main/java/com/vaadin/client/connectors/grid/GridDropTargetConnector.java b/client/src/main/java/com/vaadin/client/connectors/grid/GridDropTargetConnector.java
index 3d8164b63f..da27ba65ec 100644
--- a/client/src/main/java/com/vaadin/client/connectors/grid/GridDropTargetConnector.java
+++ b/client/src/main/java/com/vaadin/client/connectors/grid/GridDropTargetConnector.java
@@ -79,7 +79,8 @@ public class GridDropTargetConnector extends DropTargetExtensionConnector {
private String styleDragBottom;
/**
- * Class name to apply when dragged over an empty grid.
+ * Class name to apply when dragged over an empty grid, or when dropping on
+ * rows is not possible (see {@link #isDroppingOnRowsPossible()}).
*/
private String styleDragEmpty;
@@ -99,28 +100,47 @@ public class GridDropTargetConnector extends DropTargetExtensionConnector {
super.extend(target);
}
+ /**
+ * Inspects whether the current drop would happen on the whole grid instead
+ * of specific row as the drop target. This is based on used drop mode,
+ * whether dropping on sorted grid rows is allowed (determined on server
+ * side and automatically updated to drop mode) and whether the grid is
+ * empty.
+ *
+ * @return {@code true} when the drop target is the whole grid, or
+ * {@code false} when it is one of the rows
+ */
+ protected boolean isDroppingOnRowsPossible() {
+ if (getState().dropMode == DropMode.ON_GRID) {
+ return false;
+ }
+
+ if (getEscalator().getVisibleRowRange().isEmpty()) {
+ return false;
+ }
+
+ return true;
+ }
+
@Override
protected void sendDropEventToServer(List<String> types,
Map<String, String> data, String dropEffect,
NativeEvent dropEvent) {
+ Element targetElement = getTargetElement(
+ (Element) dropEvent.getEventTarget().cast());
+
+ DropLocation dropLocation = getDropLocation(targetElement, dropEvent);
+ MouseEventDetails mouseEventDetails = MouseEventDetailsBuilder
+ .buildMouseEventDetails(dropEvent, targetElement);
String rowKey = null;
- DropLocation dropLocation = null;
- Element targetElement = getTargetElement(
- (Element) dropEvent.getEventTarget().cast());
- // the target element is either the tablewrapper or one of the body rows
+ // the target is either on a row element or the table wrapper
if (TableRowElement.is(targetElement)) {
rowKey = getRowData(targetElement.cast())
.getString(GridState.JSONKEY_ROWKEY);
- dropLocation = getDropLocation(targetElement, dropEvent);
- } else {
- dropLocation = DropLocation.EMPTY;
}
- MouseEventDetails mouseEventDetails = MouseEventDetailsBuilder
- .buildMouseEventDetails(dropEvent, targetElement);
-
getRpcProxy(GridDropTargetRpc.class).drop(types, data, dropEffect,
rowKey, dropLocation, mouseEventDetails);
}
@@ -145,8 +165,13 @@ public class GridDropTargetConnector extends DropTargetExtensionConnector {
* drop target element
* @param event
* drop event
+ * @return the drop location to use
*/
protected DropLocation getDropLocation(Element target, NativeEvent event) {
+ if (!isDroppingOnRowsPossible()) {
+ return DropLocation.EMPTY;
+ }
+
if (TableRowElement.is(target)) {
if (getState().dropMode == DropMode.BETWEEN) {
if (getRelativeY(target,
@@ -266,6 +291,10 @@ public class GridDropTargetConnector extends DropTargetExtensionConnector {
final BodyRowContainer gridBody = getGridBody();
final Range visibleRowRange = getEscalator().getVisibleRowRange();
+ if (!isDroppingOnRowsPossible()) {
+ return tableWrapper;
+ }
+
while (!Objects.equals(source, tableWrapper)) {
// the drop might happen on top of header, body or footer rows
if (TableRowElement.is(source)) {
@@ -296,11 +325,9 @@ public class GridDropTargetConnector extends DropTargetExtensionConnector {
}
source = source.getParentElement();
}
- // the drag is on top of the tablewrapper
- // if no rows in grid, or if the drop mode is ON_TOP, then there is no
- // target row for the drop
- if (visibleRowRange.isEmpty()
- || getState().dropMode == DropMode.ON_TOP) {
+ // the drag is on top of the tablewrapper, if the drop mode is ON_TOP,
+ // then there is no target row for the drop
+ if (getState().dropMode == DropMode.ON_TOP) {
return tableWrapper;
}
// if dragged under the last row to empty space, drop target
@@ -328,6 +355,10 @@ public class GridDropTargetConnector extends DropTargetExtensionConnector {
return getEscalator().getBody();
}
+ private boolean isGridSortedByUser() {
+ return !gridConnector.getWidget().getSortOrder().isEmpty();
+ }
+
@Override
public GridDropTargetState getState() {
return (GridDropTargetState) super.getState();
diff --git a/documentation/advanced/advanced-dragndrop.asciidoc b/documentation/advanced/advanced-dragndrop.asciidoc
index 42cd73f33e..6ebf69a905 100644
--- a/documentation/advanced/advanced-dragndrop.asciidoc
+++ b/documentation/advanced/advanced-dragndrop.asciidoc
@@ -302,6 +302,13 @@ Additionally, the style name `v-grid-row-dragged` is applied to all the dragged
To make a Grid component's rows accept a drop event, apply the [classname]#GridDropTarget# extension to the component. When creating the extension, you need to specify where the transferred data can be dropped on.
+[NOTE]
+====
+Since 8.2, there is an option to make the grid not accept drops on rows if the grid has been sorted by the user. This is because the drop location might not be in the place that is shown to the users due to the sorting – and this can cause bad user experience. This is controlled with the method `setDropAllowedOnSortedGridRows` and is by default set to `true` to not change behavior in comparison to Framework version 8.1. When this is set to `false` and the user has sorted the grid, there will not be a target drop row for drops for the grid, and the indicator is always the same as with `DropMode.ON_GRID`.
+
+When the grid has been sorted, you should put the dropped data to the correct location (according to the sorting), and then scroll to the row where the dropped data ended up into and possibly also selecting it.
+====
+
[source,java]
----
Grid<Person> grid = new Grid<>();
@@ -310,7 +317,7 @@ GridDropTarget<Person> dropTarget = new GridDropTarget<>(grid, DropMode.BETWEEN)
dropTarget.setDropEffect(DropEffect.MOVE);
----
-The _drop mode_ specifies the behaviour of the row when an element is dragged over or dropped onto it. Use `DropMode.ON_TOP` when you want to drop elements on top of a row and `DropMode.BETWEEN` when you want to drop elements between rows. `DropMode_ON_TOP_OR_BETWEEN` allows to drop on between or top rows.
+The _drop mode_ specifies the behaviour of the row when an element is dragged over or dropped onto it. Use `DropMode.ON_TOP` when you want to drop elements on top of a row and `DropMode.BETWEEN` when you want to drop elements between rows. `DropMode_ON_TOP_OR_BETWEEN` allows to drop on between or top rows. `DropMode.ON_GRID` (since version 8.2) does not allow dropping on the grid rows, but just into the grid, without a specific target row.
The [classname]#GridDropEvent# is fired when data is dropped onto one of the Grid's rows. The following example shows how you can insert items into the Grid at the drop position. If the drag source is another Grid, you can access the generated drag data with the event's [methodname]#getDataTransferText()# method.
If the drag source Grid uses a custom generator for a different type than `"text"`, you can access it's generated data using the [methodname]#getDataTransferData(type)# method. You can also check all the received data transfer data by fetching the type-to-data map with the [methodname]#getDataTransferData()# method.
@@ -346,7 +353,7 @@ dropTarget.addGridDropListener(event -> {
The _drop location_ property in the [classname]#GridDropEvent# specifies the dropped location in relative to grid row the drop happened on and depends on the used [classname]#DropMode#. When the drop happened on top of a row, the possible options for the location are `ON_TOP`, `ABOVE` and `BELOW`.
-If the grid is empty or if the drop was on empty space after the last row in grid, and the [classname]#DropMode.ON_TOP# was used, then the drop location `EMPTY` will be used. If the drop modes [classname]#DropMode.BETWEEN# or [classname]#DropMode.ON_TOP_OR_BETWEEN# are used, then the location can be `EMPTY` only when the grid was empty; otherwise the drop happened `BELOW` the last visible row. When the drop location is `EMPTY`, the [methodname]#getDropTargetRow# method will also return an empty optional.
+If the grid is empty or if the drop was on empty space after the last row in grid, and the [classname]#DropMode.ON_TOP# was used, then the drop location `EMPTY` will be used. If the drop modes [classname]#DropMode.BETWEEN# or [classname]#DropMode.ON_TOP_OR_BETWEEN# are used, then the location can be `EMPTY` only when the grid was empty; otherwise the drop happened `BELOW` the last visible row. When the drop location is `EMPTY`, the [methodname]#getDropTargetRow# method will also return an empty optional. If the grid has been sorted by the user and `setDropAllowedOnSortedGridRows` has been set to `false`, the location will be `EMPTY` and there will not be a target row for the drops.
When dropping on top of the grid's header or footer, the drop location will be `EMPTY` if there are no rows in the grid or if [classname]#DropMode.ON_TOP# was used. If there are rows in the grid, dropping on top of the header will set the drop location to `ABOVE` and the dropped row will be the first currently visible row in grid. Similarly, if dropping on top of the footer, the drop location will be `BELOW` and the dropped row will be the last visible row in the grid.
diff --git a/server/src/main/java/com/vaadin/ui/components/grid/GridDropEvent.java b/server/src/main/java/com/vaadin/ui/components/grid/GridDropEvent.java
index 11d56713f8..a137255383 100644
--- a/server/src/main/java/com/vaadin/ui/components/grid/GridDropEvent.java
+++ b/server/src/main/java/com/vaadin/ui/components/grid/GridDropEvent.java
@@ -57,7 +57,7 @@ public class GridDropEvent<T> extends DropEvent<Grid<T>> {
* event.
* @param dropTargetRow
* Target row that received the drop, or {@code null} if dropped
- * on empty grid
+ * on empty grid or {@link DropMode#ON_GRID} is used
* @param dropLocation
* Location of the drop within the target row.
* @param mouseEventDetails
@@ -78,11 +78,11 @@ public class GridDropEvent<T> extends DropEvent<Grid<T>> {
/**
* Get the row the drop happened on.
* <p>
- * If the drop was not on top of a row (see {@link #getDropLocation()}),
- * then returns an empty optional.
+ * If the drop was not on top of a row (see {@link #getDropLocation()}) or
+ * {@link DropMode#ON_GRID} is used, then returns an empty optional.
*
- * @return The row the drop happened on, or an empty optional if dropped on
- * the in grid but not on top of any row, like to an empty grid
+ * @return The row the drop happened on, or an empty optional if drop was
+ * not on a row
*/
public Optional<T> getDropTargetRow() {
return Optional.ofNullable(dropTargetRow);
@@ -91,12 +91,19 @@ public class GridDropEvent<T> extends DropEvent<Grid<T>> {
/**
* Get the location of the drop within the row.
* <p>
- * <em>NOTE: when dropped on an empty grid, or when {@link DropMode#ON_TOP}
- * is used and the drop happened on empty space after last row or on top of
- * the header / footer, the location will be
- * {@link DropLocation#EMPTY}.</em>
+ * <em>NOTE: the location will be {@link DropLocation#EMPTY} if:
+ * <ul>
+ * <li>dropped on an empty grid</li>
+ * <li>dropping on rows was not possible because of
+ * {@link DropMode#ON_GRID } was used</li>
+ * <li>{@link DropMode#ON_TOP} is used and the drop happened on empty space
+ * after last row or on top of the header / footer</li>
+ * </ul>
+ * </em>
*
- * @return Location of the drop within the row.
+ * @return location of the drop in relative to the
+ * {@link #getDropTargetRow()} or {@link DropLocation#EMPTY} if no
+ * target row present
* @see GridDropTarget#setDropMode(DropMode)
*/
public DropLocation getDropLocation() {
diff --git a/server/src/main/java/com/vaadin/ui/components/grid/GridDropTarget.java b/server/src/main/java/com/vaadin/ui/components/grid/GridDropTarget.java
index edb1534a05..3f7ee6128e 100644
--- a/server/src/main/java/com/vaadin/ui/components/grid/GridDropTarget.java
+++ b/server/src/main/java/com/vaadin/ui/components/grid/GridDropTarget.java
@@ -38,6 +38,10 @@ import com.vaadin.ui.dnd.DropTargetExtension;
*/
public class GridDropTarget<T> extends DropTargetExtension<Grid<T>> {
+ private Registration sortListenerRegistration;
+ private DropMode cachedDropMode;
+ private boolean dropAllowedOnSortedGridRows = true;
+
/**
* Extends a Grid and makes it's rows drop targets for HTML5 drag and drop.
*
@@ -57,27 +61,49 @@ public class GridDropTarget<T> extends DropTargetExtension<Grid<T>> {
/**
* Sets the drop mode of this drop target.
* <p>
- * Note that when using {@link DropMode#ON_TOP}, and the grid is either
- * empty or has empty space after the last row, the drop can still happen on
- * the empty space, and the {@link GridDropEvent#getDropTargetRow()} will
- * return an empty optional.
+ * When using {@link DropMode#ON_TOP}, and the grid is either empty or has
+ * empty space after the last row, the drop can still happen on the empty
+ * space, and the {@link GridDropEvent#getDropTargetRow()} will return an
+ * empty optional.
* <p>
* When using {@link DropMode#BETWEEN} or
* {@link DropMode#ON_TOP_OR_BETWEEN}, and there is at least one row in the
* grid, any drop after the last row in the grid will get the last row as
* the {@link GridDropEvent#getDropTargetRow()}. If there are no rows in the
* grid, then it will return an empty optional.
+ * <p>
+ * If using {@link DropMode#ON_GRID}, then the drop will not happen on any
+ * row, but instead just "on the grid". The target row will not be present
+ * in this case.
+ * <p>
+ * <em>NOTE: {@link DropMode#ON_GRID} is used automatically when the grid
+ * has been sorted and {@link #setDropAllowedOnSortedGridRows(boolean)} is
+ * {@code false} - since the drop location would not necessarily match the
+ * correct row because of the sorting. During the sorting, any calls to this
+ * method don't have any effect until the sorting has been removed, or
+ * {@link #setDropAllowedOnSortedGridRows(boolean)} is set back to
+ * {@code true}.</em>
*
* @param dropMode
* Drop mode that describes the allowed drop locations within the
* Grid's row.
* @see GridDropEvent#getDropLocation()
+ * @see #setDropAllowedOnSortedGridRows(boolean)
*/
public void setDropMode(DropMode dropMode) {
if (dropMode == null) {
throw new IllegalArgumentException("Drop mode cannot be null");
}
+ if (cachedDropMode != null) {
+ cachedDropMode = dropMode;
+ } else {
+ internalSetDropMode(dropMode);
+ }
+
+ }
+
+ private void internalSetDropMode(DropMode dropMode) {
getState().dropMode = dropMode;
}
@@ -92,6 +118,76 @@ public class GridDropTarget<T> extends DropTargetExtension<Grid<T>> {
}
/**
+ * Sets whether the grid accepts drop on rows as target when the grid has
+ * been sorted by the user.
+ * <p>
+ * Default value is {@code true} for backwards compatibility with 8.1. When
+ * {@code true} is used or the grid is not sorted, the mode used in
+ * {@link #setDropMode(DropMode)} is always used.
+ * <p>
+ * {@code false} value means that when the grid has been sorted, the drop
+ * mode is always {@link DropMode#ON_GRID}, regardless of what was set with
+ * {@link #setDropMode(DropMode)}. Once the grid is not sorted anymore, the
+ * sort mode is reverted back to what was set with
+ * {@link #setDropMode(DropMode)}.
+ *
+ * @param dropAllowedOnSortedGridRows
+ * {@code true} for allowing, {@code false} for not allowing
+ * drops on sorted grid rows
+ * @since
+ */
+ public void setDropAllowedOnSortedGridRows(
+ boolean dropAllowedOnSortedGridRows) {
+ if (this.dropAllowedOnSortedGridRows != dropAllowedOnSortedGridRows) {
+ this.dropAllowedOnSortedGridRows = dropAllowedOnSortedGridRows;
+
+ if (!dropAllowedOnSortedGridRows) {
+
+ sortListenerRegistration = getParent()
+ .addSortListener(event -> {
+ updateDropModeForSortedGrid(
+ !event.getSortOrder().isEmpty());
+ });
+
+ updateDropModeForSortedGrid(
+ !getParent().getSortOrder().isEmpty());
+
+ } else {
+ // if the grid has been sorted, but now dropping on sorted grid
+ // is allowed, switch back to the previously allowed drop mode
+ if (cachedDropMode != null) {
+ internalSetDropMode(cachedDropMode);
+ }
+ sortListenerRegistration.remove();
+ sortListenerRegistration = null;
+ cachedDropMode = null;
+ }
+ }
+ }
+
+ private void updateDropModeForSortedGrid(boolean sorted) {
+ if (sorted && cachedDropMode == null) {
+ cachedDropMode = getDropMode();
+ internalSetDropMode(DropMode.ON_GRID);
+ } else if (!sorted && cachedDropMode != null) {
+ internalSetDropMode(cachedDropMode);
+ cachedDropMode = null;
+ }
+ }
+
+ /**
+ * Gets whether drops are allowed on rows as target, when the user has
+ * sorted the grid.
+ *
+ * @return whether drop are allowed for the grid's rows when user has sorted
+ * the grid
+ * @since
+ */
+ public boolean isDropAllowedOnSortedGridRows() {
+ return dropAllowedOnSortedGridRows;
+ }
+
+ /**
* Attaches drop listener for the current drop target.
* {@link GridDropListener#drop(GridDropEvent)} is called when drop event
* happens on the client side.
@@ -174,4 +270,16 @@ public class GridDropTarget<T> extends DropTargetExtension<Grid<T>> {
protected GridDropTargetState getState(boolean markAsDirty) {
return (GridDropTargetState) super.getState(markAsDirty);
}
+
+ @Override
+ public void remove() {
+ super.remove();
+
+ // this handler can be removed from the grid and cannot be added to
+ // another grid, thus enough to just remove the listener
+ if (sortListenerRegistration != null) {
+ sortListenerRegistration.remove();
+ sortListenerRegistration = null;
+ }
+ }
}
diff --git a/server/src/test/java/com/vaadin/tests/components/grid/GridDropTargetTest.java b/server/src/test/java/com/vaadin/tests/components/grid/GridDropTargetTest.java
new file mode 100644
index 0000000000..d5de0ad808
--- /dev/null
+++ b/server/src/test/java/com/vaadin/tests/components/grid/GridDropTargetTest.java
@@ -0,0 +1,152 @@
+package com.vaadin.tests.components.grid;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.vaadin.shared.ui.grid.DropMode;
+import com.vaadin.ui.Grid;
+import com.vaadin.ui.components.grid.GridDropTarget;
+
+public class GridDropTargetTest {
+
+ private Grid<String> grid;
+ private GridDropTarget<String> target;
+
+ @Before
+ public void setup() {
+ grid = new Grid<>();
+ grid.addColumn(s -> s).setId("1");
+ grid.addColumn(s -> s).setId("2");
+ target = new GridDropTarget<>(grid, DropMode.BETWEEN);
+ }
+
+ @Test
+ public void dropAllowedOnSortedGridRows_defaultValue_isTrue() {
+ Assert.assertTrue("Default drop allowed should be backwards compatible",
+ target.isDropAllowedOnSortedGridRows());
+ }
+
+ @Test
+ public void dropAllowedOnSortedGridRows_notAllowed_changesDropModeWhenSorted() {
+ Assert.assertEquals(DropMode.BETWEEN, target.getDropMode());
+
+ target.setDropAllowedOnSortedGridRows(false);
+
+ Assert.assertEquals(DropMode.BETWEEN, target.getDropMode());
+
+ grid.sort("1");
+
+ Assert.assertEquals(DropMode.ON_GRID, target.getDropMode());
+
+ grid.sort("2");
+
+ Assert.assertEquals(DropMode.ON_GRID, target.getDropMode());
+
+ grid.clearSortOrder();
+
+ Assert.assertEquals(DropMode.BETWEEN, target.getDropMode());
+
+ grid.clearSortOrder();
+
+ Assert.assertEquals(DropMode.BETWEEN, target.getDropMode());
+
+ grid.sort("2");
+
+ Assert.assertEquals(DropMode.ON_GRID, target.getDropMode());
+ }
+
+ @Test
+ public void dropAllowedOnSortedGridRows_sortedGridIsDisallowed_modeChangesToOnGrid() {
+ Assert.assertEquals(DropMode.BETWEEN, target.getDropMode());
+
+ grid.sort("1");
+
+ Assert.assertEquals(DropMode.BETWEEN, target.getDropMode());
+
+ target.setDropAllowedOnSortedGridRows(false);
+
+ Assert.assertEquals(DropMode.ON_GRID, target.getDropMode());
+
+ target.setDropAllowedOnSortedGridRows(true);
+
+ Assert.assertEquals(DropMode.BETWEEN, target.getDropMode());
+ }
+
+ @Test
+ public void dropAllowedOnSortedGridRows_notAllowedBackToAllowed_changesBackToUserDefinedMode() {
+ Assert.assertEquals(DropMode.BETWEEN, target.getDropMode());
+
+ target.setDropAllowedOnSortedGridRows(false);
+
+ Assert.assertEquals(DropMode.BETWEEN, target.getDropMode());
+
+ grid.sort("1");
+
+ Assert.assertEquals(DropMode.ON_GRID, target.getDropMode());
+
+ target.setDropAllowedOnSortedGridRows(true);
+
+ Assert.assertEquals(DropMode.BETWEEN, target.getDropMode());
+
+ grid.clearSortOrder();
+
+ Assert.assertEquals(DropMode.BETWEEN, target.getDropMode());
+ }
+
+ @Test
+ public void dropAllowedOnSortedGridRows_swappingAllowedDropOnSortedOffAndOn() {
+ Assert.assertEquals(DropMode.BETWEEN, target.getDropMode());
+
+ target.setDropAllowedOnSortedGridRows(false);
+
+ Assert.assertEquals(DropMode.BETWEEN, target.getDropMode());
+
+ target.setDropAllowedOnSortedGridRows(false);
+
+ Assert.assertEquals(DropMode.BETWEEN, target.getDropMode());
+
+ target.setDropAllowedOnSortedGridRows(true);
+
+ Assert.assertEquals(DropMode.BETWEEN, target.getDropMode());
+
+ target.setDropAllowedOnSortedGridRows(true);
+
+ Assert.assertEquals(DropMode.BETWEEN, target.getDropMode());
+ }
+
+ @Test
+ public void dropAllowedOnSortedGridRows_changingDropModeWhileSorted_replacesPreviouslyCachedButDoesntOverride() {
+ Assert.assertEquals(DropMode.BETWEEN, target.getDropMode());
+
+ target.setDropAllowedOnSortedGridRows(false);
+
+ Assert.assertEquals(DropMode.BETWEEN, target.getDropMode());
+
+ grid.sort("1");
+
+ Assert.assertEquals(DropMode.ON_GRID, target.getDropMode());
+
+ target.setDropMode(DropMode.ON_TOP);
+
+ Assert.assertEquals(DropMode.ON_GRID, target.getDropMode());
+ Assert.assertFalse("Changing drop mode should not have any effect here",
+ target.isDropAllowedOnSortedGridRows());
+
+ grid.clearSortOrder();
+
+ Assert.assertEquals(DropMode.ON_TOP, target.getDropMode());
+
+ grid.sort("1");
+
+ Assert.assertEquals(DropMode.ON_GRID, target.getDropMode());
+
+ target.setDropMode(DropMode.ON_TOP_OR_BETWEEN);
+
+ Assert.assertEquals(DropMode.ON_GRID, target.getDropMode());
+
+ target.setDropAllowedOnSortedGridRows(true);
+
+ Assert.assertEquals(DropMode.ON_TOP_OR_BETWEEN, target.getDropMode());
+ }
+}
diff --git a/shared/src/main/java/com/vaadin/shared/ui/grid/DropLocation.java b/shared/src/main/java/com/vaadin/shared/ui/grid/DropLocation.java
index 3e031e6c98..aa211875e9 100644
--- a/shared/src/main/java/com/vaadin/shared/ui/grid/DropLocation.java
+++ b/shared/src/main/java/com/vaadin/shared/ui/grid/DropLocation.java
@@ -39,8 +39,9 @@ public enum DropLocation {
BELOW,
/**
- * Dropping into an empty grid, or to the empty area below the grid rows
- * when {@link DropMode#ON_TOP} is used.
+ * Dropping into an empty grid, to a sorted grid, when
+ * {@link DropMode#ON_GRID} is used, or to the empty area below the grid
+ * rows when {@link DropMode#ON_TOP} is used.
*/
EMPTY;
}
diff --git a/shared/src/main/java/com/vaadin/shared/ui/grid/DropMode.java b/shared/src/main/java/com/vaadin/shared/ui/grid/DropMode.java
index c458b9d76c..4294a4fa1a 100644
--- a/shared/src/main/java/com/vaadin/shared/ui/grid/DropMode.java
+++ b/shared/src/main/java/com/vaadin/shared/ui/grid/DropMode.java
@@ -47,5 +47,16 @@ public enum DropMode {
* above conditions.</li>
* </ul>
*/
- ON_TOP_OR_BETWEEN
+ ON_TOP_OR_BETWEEN,
+
+ /**
+ * The drop event will not happen on any specific row, it will show the drop
+ * target outline around the whole grid. For this drop target, the drop
+ * event will not contain any target row information. This is the drop
+ * target used when the grid is completely empty. It can also be configured
+ * to be used automatically when the user has sorted the grid.
+ *
+ * @since
+ */
+ ON_GRID;
}
diff --git a/shared/src/main/java/com/vaadin/shared/ui/grid/GridDropTargetState.java b/shared/src/main/java/com/vaadin/shared/ui/grid/GridDropTargetState.java
index b0c434305f..f1e2ac6f3f 100644
--- a/shared/src/main/java/com/vaadin/shared/ui/grid/GridDropTargetState.java
+++ b/shared/src/main/java/com/vaadin/shared/ui/grid/GridDropTargetState.java
@@ -37,4 +37,5 @@ public class GridDropTargetState extends DropTargetState {
* @see DropMode#ON_TOP_OR_BETWEEN
*/
public int dropThreshold = 5;
+
}
diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/GridDragAndDrop.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridDragAndDrop.java
index d0bd23110e..209bb77a69 100644
--- a/uitest/src/main/java/com/vaadin/tests/components/grid/GridDragAndDrop.java
+++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridDragAndDrop.java
@@ -91,6 +91,12 @@ public class GridDragAndDrop extends AbstractTestUIWithLog {
}
});
+ CheckBox dropOnSortedGridRows = new CheckBox("Drop on Sorted Grid Rows",
+ dropTarget.isDropAllowedOnSortedGridRows());
+ dropOnSortedGridRows.addValueChangeListener(event -> {
+ dropTarget.setDropAllowedOnSortedGridRows(event.getValue());
+ });
+
RadioButtonGroup<Integer> frozenColumnSelect = new RadioButtonGroup<>(
"Frozen columns", Arrays.asList(new Integer[] { -1, 0, 1 }));
frozenColumnSelect.setValue(left.getFrozenColumnCount());
@@ -100,7 +106,8 @@ public class GridDragAndDrop extends AbstractTestUIWithLog {
});
Layout controls = new HorizontalLayout(selectionModeSelect,
- dropLocationSelect, transitionCheckBox, frozenColumnSelect);
+ dropLocationSelect, transitionCheckBox, frozenColumnSelect,
+ dropOnSortedGridRows);
addComponents(controls, grids);