summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksi Hietanen <aleksi@vaadin.com>2016-10-03 10:30:05 +0300
committerVaadin Code Review <review@vaadin.com>2016-10-06 08:43:28 +0000
commit6abb9c5c64c60db47e8f2dde87afaf0e76b2228c (patch)
treed7831d622a019523d22e63da8ada12075b79d590
parent45082d1abcfec7f281c986c08fe0c1f065af29f2 (diff)
downloadvaadin-framework-6abb9c5c64c60db47e8f2dde87afaf0e76b2228c.tar.gz
vaadin-framework-6abb9c5c64c60db47e8f2dde87afaf0e76b2228c.zip
Reintroduce reordering of grid columns
Change-Id: If8a23427ef5500a0177081c4be8065d2d5a0ca4c
-rw-r--r--client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java26
-rw-r--r--client/src/main/java/com/vaadin/client/widget/grid/events/ColumnReorderEvent.java45
-rw-r--r--client/src/main/java/com/vaadin/client/widgets/Grid.java11
-rw-r--r--server/src/main/java/com/vaadin/ui/Grid.java251
-rw-r--r--uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java82
-rw-r--r--uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicsTest.java100
-rw-r--r--uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridColumnReorderTest.java204
7 files changed, 685 insertions, 34 deletions
diff --git a/client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java b/client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java
index 6596137d9d..02909e1fc3 100644
--- a/client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java
+++ b/client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java
@@ -20,7 +20,9 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
+import java.util.stream.Collectors;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
@@ -132,7 +134,7 @@ public class GridConnector
* the id of the column to get
* @return the column with the given id
*/
- public Column<?, ?> getColumn(String columnId) {
+ public Column<?, JsonObject> getColumn(String columnId) {
return idToColumn.get(columnId);
}
@@ -180,6 +182,16 @@ public class GridConnector
getColumnId(event.getColumn()), event.isHidden());
}
});
+ getWidget().addColumnReorderHandler(event -> {
+ if (event.isUserOriginated()) {
+ List<String> newColumnOrder = mapColumnsToIds(
+ event.getNewColumnOrder());
+ List<String> oldColumnOrder = mapColumnsToIds(
+ event.getOldColumnOrder());
+ getRpcProxy(GridServerRpc.class)
+ .columnsReordered(newColumnOrder, oldColumnOrder);
+ }
+ });
getWidget().addColumnResizeHandler(event -> {
Column<?, JsonObject> column = event.getColumn();
getRpcProxy(GridServerRpc.class).columnResized(getColumnId(column),
@@ -219,6 +231,13 @@ public class GridConnector
layout();
}
+ @SuppressWarnings("unchecked")
+ @OnStateChange("columnOrder")
+ void updateColumnOrder() {
+ getWidget().setColumnOrder(getState().columnOrder.stream()
+ .map(this::getColumn).toArray(size -> new Column[size]));
+ }
+
/**
* Updates the grid header section on state change.
*/
@@ -446,4 +465,9 @@ public class GridConnector
}
return false;
}
+
+ private List<String> mapColumnsToIds(List<Column<?, JsonObject>> columns) {
+ return columns.stream().map(this::getColumnId).filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
}
diff --git a/client/src/main/java/com/vaadin/client/widget/grid/events/ColumnReorderEvent.java b/client/src/main/java/com/vaadin/client/widget/grid/events/ColumnReorderEvent.java
index 72c0eb5878..0063c20b28 100644
--- a/client/src/main/java/com/vaadin/client/widget/grid/events/ColumnReorderEvent.java
+++ b/client/src/main/java/com/vaadin/client/widget/grid/events/ColumnReorderEvent.java
@@ -15,7 +15,10 @@
*/
package com.vaadin.client.widget.grid.events;
+import java.util.List;
+
import com.google.gwt.event.shared.GwtEvent;
+import com.vaadin.client.widgets.Grid.Column;
/**
* An event for notifying that the columns in the Grid have been reordered.
@@ -37,6 +40,48 @@ public class ColumnReorderEvent<T> extends GwtEvent<ColumnReorderHandler<T>> {
return TYPE;
}
+ private final List<Column<?, T>> oldColumnOrder;
+
+ private final List<Column<?, T>> newColumnOrder;
+
+ private final boolean userOriginated;
+
+ public ColumnReorderEvent(List<Column<?, T>> oldColumnOrder,
+ List<Column<?, T>> newColumnOrder, boolean userOriginated) {
+ this.oldColumnOrder = oldColumnOrder;
+ this.newColumnOrder = newColumnOrder;
+ this.userOriginated = userOriginated;
+ }
+
+ /**
+ * Gets the ordering of columns prior to this event.
+ *
+ * @return the list of columns in the grid's order prior to this event
+ */
+ public List<Column<?, T>> getOldColumnOrder() {
+ return oldColumnOrder;
+ }
+
+ /**
+ * Gets the new ordering of columns.
+ *
+ * @return the list of columns in the grid's current order
+ */
+ public List<Column<?, T>> getNewColumnOrder() {
+ return newColumnOrder;
+ }
+
+ /**
+ * Check whether this event originated from the user reordering columns or
+ * via API call.
+ *
+ * @return {@code true} if columns were reordered by the user, {@code false}
+ * if not
+ */
+ public boolean isUserOriginated() {
+ return userOriginated;
+ }
+
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public Type<ColumnReorderHandler<T>> getAssociatedType() {
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 eac958fdce..7d6c1369b8 100644
--- a/client/src/main/java/com/vaadin/client/widgets/Grid.java
+++ b/client/src/main/java/com/vaadin/client/widgets/Grid.java
@@ -4372,7 +4372,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>,
Column<?, T>[] array = reordered
.toArray(new Column[reordered.size()]);
- setColumnOrder(array);
+ setColumnOrder(true, array);
transferCellFocusOnDrop();
} // else no reordering
}
@@ -8171,6 +8171,12 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>,
* array of columns in wanted order
*/
public void setColumnOrder(Column<?, T>... orderedColumns) {
+ setColumnOrder(false, orderedColumns);
+ }
+
+ private void setColumnOrder(boolean isUserOriginated,
+ Column<?, T>... orderedColumns) {
+ List<Column<?, T>> oldOrder = new ArrayList<>(columns);
ColumnConfiguration conf = getEscalator().getColumnConfiguration();
// Trigger ComplexRenderer.destroy for old content
@@ -8221,7 +8227,8 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>,
columnHider.updateTogglesOrder();
- fireEvent(new ColumnReorderEvent<T>());
+ fireEvent(new ColumnReorderEvent<T>(oldOrder, newOrder,
+ isUserOriginated));
}
/**
diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java
index e2df8b437b..891c08aa47 100644
--- a/server/src/main/java/com/vaadin/ui/Grid.java
+++ b/server/src/main/java/com/vaadin/ui/Grid.java
@@ -17,6 +17,7 @@ package com.vaadin.ui;
import java.io.Serializable;
import java.lang.reflect.Method;
+import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -32,11 +33,14 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.vaadin.event.ConnectorEvent;
import com.vaadin.event.ContextClickEvent;
import com.vaadin.event.EventListener;
+import com.vaadin.server.EncodeResult;
+import com.vaadin.server.JsonCodec;
import com.vaadin.server.KeyMapper;
import com.vaadin.server.data.SortOrder;
import com.vaadin.shared.MouseEventDetails;
@@ -73,6 +77,11 @@ import elemental.json.JsonValue;
public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
@Deprecated
+ private static final Method COLUMN_REORDER_METHOD = ReflectTools.findMethod(
+ ColumnReorderListener.class, "columnReorder",
+ ColumnReorderEvent.class);
+
+ @Deprecated
private static final Method COLUMN_RESIZE_METHOD = ReflectTools.findMethod(
ColumnResizeListener.class, "columnResize",
ColumnResizeEvent.class);
@@ -88,10 +97,24 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
ColumnVisibilityChangeEvent.class);
/**
+ * An event listener for column reorder events in the Grid.
+ */
+ @FunctionalInterface
+ public interface ColumnReorderListener extends Serializable {
+
+ /**
+ * Called when the columns of the grid have been reordered.
+ *
+ * @param event
+ * An event providing more information
+ */
+ void columnReorder(ColumnReorderEvent event);
+ }
+
+ /**
* An event listener for column resize events in the Grid.
- *
- * @since 7.6
*/
+ @FunctionalInterface
public interface ColumnResizeListener extends Serializable {
/**
@@ -104,10 +127,39 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
}
/**
+ * An event that is fired when the columns are reordered.
+ */
+ public static class ColumnReorderEvent extends Component.Event {
+
+ private final boolean userOriginated;
+
+ /**
+ *
+ * @param source
+ * the grid where the event originated from
+ * @param userOriginated
+ * <code>true</code> if event is a result of user
+ * interaction, <code>false</code> if from API call
+ */
+ public ColumnReorderEvent(Grid source, boolean userOriginated) {
+ super(source);
+ this.userOriginated = userOriginated;
+ }
+
+ /**
+ * Returns <code>true</code> if the column reorder was done by the user,
+ * <code>false</code> if not and it was triggered by server side code.
+ *
+ * @return <code>true</code> if event is a result of user interaction
+ */
+ public boolean isUserOriginated() {
+ return userOriginated;
+ }
+ }
+
+ /**
* An event that is fired when a column is resized, either programmatically
* or by the user.
- *
- * @since 7.6
*/
public static class ColumnResizeEvent extends Component.Event {
@@ -324,6 +376,7 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
*
* @since 7.5.0
*/
+ @FunctionalInterface
public interface ColumnVisibilityChangeListener extends Serializable {
/**
@@ -522,7 +575,39 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
@Override
public void columnsReordered(List<String> newColumnOrder,
List<String> oldColumnOrder) {
- // TODO Auto-generated method stub
+ final String diffStateKey = "columnOrder";
+ ConnectorTracker connectorTracker = getUI().getConnectorTracker();
+ JsonObject diffState = connectorTracker.getDiffState(Grid.this);
+ // discard the change if the columns have been reordered from
+ // the server side, as the server side is always right
+ if (getState(false).columnOrder.equals(oldColumnOrder)) {
+ // Don't mark as dirty since client has the state already
+ getState(false).columnOrder = newColumnOrder;
+ // write changes to diffState so that possible reverting the
+ // column order is sent to client
+ assert diffState
+ .hasKey(diffStateKey) : "Field name has changed";
+ Type type = null;
+ try {
+ type = (getState(false).getClass()
+ .getDeclaredField(diffStateKey).getGenericType());
+ } catch (NoSuchFieldException e) {
+ e.printStackTrace();
+ } catch (SecurityException e) {
+ e.printStackTrace();
+ }
+ EncodeResult encodeResult = JsonCodec.encode(
+ getState(false).columnOrder, diffState, type,
+ connectorTracker);
+
+ diffState.put(diffStateKey, encodeResult.getEncodedValue());
+ fireColumnReorderEvent(true);
+ } else {
+ // make sure the client is reverted to the order that the
+ // server thinks it is
+ diffState.remove(diffStateKey);
+ markAsDirty();
+ }
}
@Override
@@ -1433,7 +1518,7 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
/**
* Returns the cell on this row corresponding to the given column id.
- *
+ *
* @param columnId
* the id of the column whose header cell to get, not null
* @return the header cell
@@ -1444,7 +1529,7 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
/**
* Returns the cell on this row corresponding to the given column.
- *
+ *
* @param column
* the column whose header cell to get, not null
* @return the header cell
@@ -1463,14 +1548,14 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
/**
* Returns the textual caption of this cell.
- *
+ *
* @return the header caption
*/
public String getText();
/**
* Sets the textual caption of this cell.
- *
+ *
* @param text
* the header caption to set, not null
*/
@@ -1555,19 +1640,7 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
AbstractRenderer<? super T, V> renderer) {
final Column<T, V> column = new Column<>(caption, valueProvider,
renderer);
- final String columnId = columnKeys.key(column);
-
- column.extend(this);
- column.setId(columnId);
- columnSet.add(column);
- addDataGenerator(column);
-
- getHeader().addColumn(columnId);
-
- if (getDefaultHeaderRow() != null) {
- getDefaultHeaderRow().getCell(columnId).setText(caption);
- }
-
+ addColumn(column);
return column;
}
@@ -1587,6 +1660,27 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
return addColumn(caption, valueProvider, new TextRenderer());
}
+ private void addColumn(Column<T, ?> column) {
+ if (getColumns().contains(column)) {
+ return;
+ }
+
+ final String columnId = columnKeys.key(column);
+
+ column.extend(this);
+ column.setId(columnId);
+ columnSet.add(column);
+ addDataGenerator(column);
+
+ getState().columnOrder.add(columnId);
+ getHeader().addColumn(columnId);
+
+ if (getDefaultHeaderRow() != null) {
+ getDefaultHeaderRow().getCell(columnId)
+ .setText(column.getCaption());
+ }
+ }
+
/**
* Removes the given column from this {@link Grid}.
*
@@ -1645,8 +1739,9 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
*
* @return unmodifiable collection of columns
*/
- public Collection<Column<T, ?>> getColumns() {
- return Collections.unmodifiableSet(columnSet);
+ public List<Column<T, ?>> getColumns() {
+ return Collections.unmodifiableList(getState(false).columnOrder.stream()
+ .map(this::getColumn).collect(Collectors.toList()));
}
/**
@@ -1894,10 +1989,10 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
* the index at which to insert the row, where the topmost row
* has index zero
* @return the inserted header row
- *
+ *
* @throws IndexOutOfBoundsException
* if {@code rowIndex < 0 || rowIndex > getHeaderRowCount()}
- *
+ *
* @see #appendHeaderRow()
* @see #prependHeaderRow()
* @see #removeHeaderRow(HeaderRow)
@@ -1911,7 +2006,7 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
* Adds a new row at the bottom of the header section.
*
* @return the appended header row
- *
+ *
* @see #prependHeaderRow()
* @see #addHeaderRowAt(int)
* @see #removeHeaderRow(HeaderRow)
@@ -1925,7 +2020,7 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
* Adds a new row at the top of the header section.
*
* @return the prepended header row
- *
+ *
* @see #appendHeaderRow()
* @see #addHeaderRowAt(int)
* @see #removeHeaderRow(HeaderRow)
@@ -1944,7 +2039,7 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
*
* @throws IllegalArgumentException
* if the header does not contain the row
- *
+ *
* @see #removeHeaderRow(int)
* @see #addHeaderRowAt(int)
* @see #appendHeaderRow()
@@ -1963,7 +2058,7 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
*
* @throws IndexOutOfBoundsException
* if {@code index < 0 || index >= getHeaderRowCount()}
- *
+ *
* @see #removeHeaderRow(HeaderRow)
* @see #addHeaderRowAt(int)
* @see #appendHeaderRow()
@@ -1977,7 +2072,7 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
* Returns the current default row of the header.
*
* @return the default row or null if no default row set
- *
+ *
* @see #setDefaultHeaderRow(HeaderRow)
*/
public HeaderRow getDefaultHeaderRow() {
@@ -2013,6 +2108,20 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
}
/**
+ * Registers a new column reorder listener.
+ *
+ * @param listener
+ * the listener to register, not null
+ * @return a registration for the listener
+ */
+ public Registration addColumnReorderListener(
+ ColumnReorderListener listener) {
+ addListener(ColumnReorderEvent.class, listener, COLUMN_REORDER_METHOD);
+ return () -> removeListener(ColumnReorderEvent.class, listener,
+ COLUMN_REORDER_METHOD);
+ }
+
+ /**
* Registers a new column resize listener.
*
* @param listener
@@ -2055,6 +2164,82 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
COLUMN_VISIBILITY_METHOD);
}
+ /**
+ * Returns whether column reordering is allowed. Default value is
+ * <code>false</code>.
+ *
+ * @return true if reordering is allowed
+ */
+ public boolean isColumnReorderingAllowed() {
+ return getState(false).columnReorderingAllowed;
+ }
+
+ /**
+ * Sets whether or not column reordering is allowed. Default value is
+ * <code>false</code>.
+ *
+ * @param columnReorderingAllowed
+ * specifies whether column reordering is allowed
+ */
+ public void setColumnReorderingAllowed(boolean columnReorderingAllowed) {
+ if (isColumnReorderingAllowed() != columnReorderingAllowed) {
+ getState().columnReorderingAllowed = columnReorderingAllowed;
+ }
+ }
+
+ /**
+ * Sets the columns and their order for the grid. Columns currently in this
+ * grid that are not present in columns are removed. Similarly, any new
+ * column in columns will be added to this grid.
+ *
+ * @param columns
+ * the columns to set
+ */
+ public void setColumns(Column<T, ?>... columns) {
+ List<Column<T, ?>> currentColumns = getColumns();
+ Set<Column<T, ?>> removeColumns = new HashSet<>(currentColumns);
+ Set<Column<T, ?>> addColumns = Arrays.stream(columns)
+ .collect(Collectors.toSet());
+
+ removeColumns.removeAll(addColumns);
+ removeColumns.stream().forEach(this::removeColumn);
+
+ addColumns.removeAll(currentColumns);
+ addColumns.stream().forEach(this::addColumn);
+
+ setColumnOrder(columns);
+ }
+
+ /**
+ * Sets a new column order for the grid. All columns which are not ordered
+ * here will remain in the order they were before as the last columns of
+ * grid.
+ *
+ * @param columns
+ * the columns in the order they should be
+ */
+ public void setColumnOrder(Column<T, ?>... columns) {
+ List<String> columnOrder = new ArrayList<>();
+ for (Column<T, ?> column : columns) {
+ if (columnSet.contains(column)) {
+ columnOrder.add(column.getId());
+ } else {
+ throw new IllegalArgumentException(
+ "setColumnOrder should not be called "
+ + "with columns that are not in the grid.");
+ }
+ }
+
+ List<String> stateColumnOrder = getState().columnOrder;
+ if (stateColumnOrder.size() != columnOrder.size()) {
+ stateColumnOrder.removeAll(columnOrder);
+ columnOrder.addAll(stateColumnOrder);
+ }
+
+ getState().columnOrder = columnOrder;
+ fireColumnReorderEvent(false);
+ }
+
@Override
protected GridState getState() {
return getState(true);
@@ -2079,6 +2264,10 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
}
}
+ private void fireColumnReorderEvent(boolean userOriginated) {
+ fireEvent(new ColumnReorderEvent(this, userOriginated));
+ }
+
private void fireColumnResizeEvent(Column<?, ?> column,
boolean userOriginated) {
fireEvent(new ColumnResizeEvent(this, column, userOriginated));
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 f75dbd27cc..28c8a987b7 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
@@ -1,6 +1,8 @@
package com.vaadin.tests.components.grid.basics;
import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@@ -50,6 +52,35 @@ public class GridBasics extends AbstractTestUIWithLog {
"Column 2", "Row Number", "Date", "HTML String", "Big Random",
"Small Random" };
+ private final Command toggleReorderListenerCommand = new Command() {
+ private Registration registration = null;
+
+ @Override
+ public void menuSelected(MenuItem selectedItem) {
+ removeRegistration();
+ if (selectedItem.isChecked()) {
+ registration = grid.addColumnReorderListener(event -> {
+ List<String> columnCaptions = new ArrayList<String>();
+ for (Column<DataObject, ?> column : grid.getColumns()) {
+ columnCaptions.add(column.getCaption());
+ }
+ log("Columns reordered, userOriginated: "
+ + event.isUserOriginated());
+ log("Column order: " + columnCaptions.toString());
+ });
+ log("Registered a column reorder listener.");
+ }
+ }
+
+ private void removeRegistration() {
+ if (registration != null) {
+ registration.remove();
+ registration = null;
+ log("Removed a column reorder listener.");
+ }
+ }
+ };
+
private static class DetailedDetailsGenerator
implements DetailsGenerator<DataObject> {
@@ -109,6 +140,7 @@ public class GridBasics extends AbstractTestUIWithLog {
private List<DataObject> data;
private int watchingCount = 0;
private PersistingDetailsGenerator persistingDetails;
+ private List<Column<DataObject, ?>> initialColumnOrder;
public GridBasics() {
generators.put("NULL", null);
@@ -167,9 +199,51 @@ public class GridBasics extends AbstractTestUIWithLog {
createDetailsMenu(componentMenu.addItem("Details", null));
createBodyMenu(componentMenu.addItem("Body rows", null));
createHeaderMenu(componentMenu.addItem("Header", null));
+ createColumnsMenu(componentMenu.addItem("Columns", null));
return menu;
}
+ @SuppressWarnings("unchecked")
+ private void createColumnsMenu(MenuItem columnsMenu) {
+ for (int i = 0; i < grid.getColumns().size(); i++) {
+ final int index = i;
+ MenuItem columnMenu = columnsMenu.addItem("Column " + i, null);
+ columnMenu.addItem("Move left", selectedItem -> {
+ if (index > 0) {
+ List<Column<DataObject, ?>> columnOrder = new ArrayList<>(
+ grid.getColumns());
+ Collections.swap(columnOrder, index, index - 1);
+ grid.setColumnOrder(columnOrder
+ .toArray(new Column[columnOrder.size()]));
+ }
+ });
+ columnMenu.addItem("Move right", selectedItem -> {
+ if (index < grid.getColumns().size() - 1) {
+ List<Column<DataObject, ?>> columnOrder = new ArrayList<>(
+ grid.getColumns());
+ Collections.swap(columnOrder, index, index + 1);
+ grid.setColumnOrder(columnOrder
+ .toArray(new Column[columnOrder.size()]));
+ }
+ });
+ columnMenu
+ .addItem("Sortable",
+ selectedItem -> grid.getColumns().get(index)
+ .setSortable(selectedItem.isChecked()))
+ .setCheckable(true);
+ columnMenu
+ .addItem("Hidable",
+ selectedItem -> grid.getColumns().get(index)
+ .setHidable(selectedItem.isChecked()))
+ .setCheckable(true);
+ columnMenu
+ .addItem("Hidden",
+ selectedItem -> grid.getColumns().get(index)
+ .setHidden(selectedItem.isChecked()))
+ .setCheckable(true);
+ }
+ }
+
private void createSizeMenu(MenuItem sizeMenu) {
MenuItem heightByRows = sizeMenu.addItem("Height by Rows", null);
DecimalFormat df = new DecimalFormat("0.00");
@@ -238,6 +312,14 @@ public class GridBasics extends AbstractTestUIWithLog {
}
}
}).setCheckable(true);
+
+ stateMenu.addItem("Column reorder listener",
+ toggleReorderListenerCommand).setCheckable(true);
+
+ stateMenu
+ .addItem("Column Reordering", selectedItem -> grid
+ .setColumnReorderingAllowed(selectedItem.isChecked()))
+ .setCheckable(true);
}
private void createRowStyleMenu(MenuItem rowStyleMenu) {
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 1a1bc30e81..eed08032de 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
@@ -1,16 +1,24 @@
package com.vaadin.tests.components.grid.basics;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import org.junit.Before;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.remote.DesiredCapabilities;
+import com.vaadin.testbench.TestBenchElement;
import com.vaadin.testbench.customelements.GridElement;
+import com.vaadin.testbench.elements.GridElement.GridCellElement;
import com.vaadin.testbench.parallel.Browser;
import com.vaadin.tests.tb3.MultiBrowserTest;
+import com.vaadin.v7.tests.components.grid.basicfeatures.GridBasicFeaturesTest.CellSide;
/**
* Base class for all {@link GridBasics} UI tests
@@ -41,6 +49,14 @@ public abstract class GridBasicsTest extends MultiBrowserTest {
return $(GridElement.class).first();
}
+ protected List<TestBenchElement> getGridHeaderRowCells() {
+ List<TestBenchElement> headerCells = new ArrayList<>();
+ for (int i = 0; i < getGridElement().getHeaderCount(); ++i) {
+ headerCells.addAll(getGridElement().getHeaderCells(i));
+ }
+ return headerCells;
+ }
+
protected Stream<DataObject> getTestData() {
return testData.stream();
}
@@ -65,4 +81,88 @@ public abstract class GridBasicsTest extends MultiBrowserTest {
"//div[contains(@class, \"v-grid-scroller-horizontal\")]"));
}
+ protected GridCellElement getDefaultColumnHeader(int index) {
+ List<GridCellElement> headerRowCells = getGridElement()
+ .getHeaderCells(0);
+ return headerRowCells.get(index);
+ }
+
+ protected void dragAndDropDefaultColumnHeader(int draggedColumnHeaderIndex,
+ int onTopOfColumnHeaderIndex, CellSide cellSide) {
+ GridCellElement columnHeader = getDefaultColumnHeader(
+ onTopOfColumnHeaderIndex);
+ new Actions(getDriver())
+ .clickAndHold(getDefaultColumnHeader(draggedColumnHeaderIndex))
+ .moveToElement(columnHeader, getHorizontalOffsetForDragAndDrop(
+ columnHeader, cellSide), 0)
+ .release().perform();
+ }
+
+ protected void dragAndDropColumnHeader(int headerRow,
+ int draggedColumnHeaderIndex, int onTopOfColumnHeaderIndex,
+ CellSide cellSide) {
+ GridCellElement headerCell = getGridElement().getHeaderCell(headerRow,
+ onTopOfColumnHeaderIndex);
+ new Actions(getDriver())
+ .clickAndHold(getGridElement().getHeaderCell(headerRow,
+ draggedColumnHeaderIndex))
+ .moveToElement(headerCell,
+ getHorizontalOffsetForDragAndDrop(headerCell, cellSide),
+ 0)
+ .release().perform();
+ }
+
+ protected void dragAndDropColumnHeader(int headerRow,
+ int draggedColumnHeaderIndex, int onTopOfColumnHeaderIndex,
+ int horizontalOffset) {
+ GridCellElement headerCell = getGridElement().getHeaderCell(headerRow,
+ onTopOfColumnHeaderIndex);
+ new Actions(getDriver())
+ .clickAndHold(getGridElement().getHeaderCell(headerRow,
+ draggedColumnHeaderIndex))
+ .moveToElement(headerCell, horizontalOffset, 0).release()
+ .perform();
+ }
+
+ private int getHorizontalOffsetForDragAndDrop(GridCellElement columnHeader,
+ CellSide cellSide) {
+ if (cellSide == CellSide.LEFT) {
+ return 5;
+ } else {
+ int half = columnHeader.getSize().getWidth() / 2;
+ return half + (half / 2);
+ }
+ }
+
+ protected void toggleColumnReorder() {
+ selectMenuPath("Component", "State", "Column Reordering");
+ }
+
+ protected void assertColumnHeaderOrder(int... indices) {
+ List<TestBenchElement> headers = getGridHeaderRowCells();
+ for (int i = 0; i < indices.length; i++) {
+ assertColumnHeader("HEADER CELL " + indices[i], headers.get(i));
+ }
+ }
+
+ protected void assertColumnHeader(String expectedHeaderCaption,
+ TestBenchElement testBenchElement) {
+ assertEquals(expectedHeaderCaption.toLowerCase(),
+ testBenchElement.getText().toLowerCase());
+ }
+
+ protected void assertColumnIsSorted(int index) {
+ WebElement columnHeader = getDefaultColumnHeader(index);
+ assertTrue(columnHeader.getAttribute("class").contains("sort"));
+ }
+
+ protected void assertFocusedCell(int row, int column) {
+ assertTrue(getGridElement().getCell(row, column).getAttribute("class")
+ .contains("focused"));
+ }
+
+ protected void focusCell(int row, int column) {
+ getGridElement().getCell(row, column).click();
+ }
+
}
diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridColumnReorderTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridColumnReorderTest.java
new file mode 100644
index 0000000000..62156acec7
--- /dev/null
+++ b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridColumnReorderTest.java
@@ -0,0 +1,204 @@
+/*
+ * 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.basics;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
+
+import com.vaadin.testbench.parallel.TestCategory;
+import com.vaadin.v7.tests.components.grid.basicfeatures.GridBasicFeaturesTest.CellSide;
+
+/**
+ *
+ * @author Vaadin Ltd
+ */
+@TestCategory("grid")
+public class GridColumnReorderTest extends GridBasicsTest {
+
+ @Before
+ public void before() {
+ openTestURL();
+ }
+
+ @Test
+ public void testColumnReorder_onReorder_columnReorderEventTriggered() {
+ selectMenuPath("Component", "Header", "Prepend header row");
+ selectMenuPath("Component", "State", "Column reorder listener");
+ selectMenuPath("Component", "Columns", "Column " + 3, "Move left");
+
+ assertEquals("1. Registered a column reorder listener.", getLogRow(2));
+ assertEquals("2. Columns reordered, userOriginated: false",
+ getLogRow(1));
+ assertColumnHeaderOrder(0, 1, 3, 2);
+
+ // trigger another event
+ selectMenuPath("Component", "Columns", "Column " + 3, "Move left");
+ assertColumnHeaderOrder(0, 1, 2, 3);
+
+ // test drag and drop is user originated
+ toggleColumnReorder();
+ dragAndDropColumnHeader(0, 0, 1, CellSide.RIGHT);
+ assertEquals("6. Columns reordered, userOriginated: true",
+ getLogRow(1));
+ assertColumnHeaderOrder(1, 0, 2, 3);
+ }
+
+ @Test
+ public void testColumnReorder_draggingSortedColumn_sortIndicatorShownOnDraggedElement() {
+ // given
+ toggleColumnReorder();
+ toggleSortableColumn(0);
+ sortColumn(0);
+
+ // when
+ startDragButDontDropOnDefaultColumnHeader(0);
+
+ // then
+ WebElement draggedElement = getDraggedHeaderElement();
+ assertTrue(draggedElement.getAttribute("class").contains("sort"));
+ }
+
+ @Test
+ public void testColumnReorder_draggingSortedColumn_sortStays() {
+ // given
+ toggleColumnReorder();
+ toggleSortableColumn(0);
+ sortColumn(0);
+
+ // when
+ dragAndDropDefaultColumnHeader(0, 2, CellSide.LEFT);
+
+ // then
+ assertColumnIsSorted(1);
+ }
+
+ @Test
+ public void testColumnReorder_draggingFocusedHeader_focusShownOnDraggedElement() {
+ // given
+ toggleColumnReorder();
+ focusDefaultHeader(0);
+
+ // when
+ startDragButDontDropOnDefaultColumnHeader(0);
+
+ // then
+ WebElement draggedElement = getDraggedHeaderElement();
+ assertTrue(draggedElement.getAttribute("class").contains("focused"));
+ }
+
+ @Test
+ public void testColumnReorder_draggingFocusedHeader_focusIsKeptOnHeader() {
+ // given
+ toggleColumnReorder();
+ focusDefaultHeader(0);
+
+ // when
+ dragAndDropDefaultColumnHeader(0, 3, CellSide.LEFT);
+
+ // then
+ WebElement defaultColumnHeader = getDefaultColumnHeader(2);
+ String attribute = defaultColumnHeader.getAttribute("class");
+ assertTrue(attribute.contains("focused"));
+ }
+
+ @Test
+ public void testColumnReorder_draggingFocusedCellColumn_focusIsKeptOnCell() {
+ // given
+ toggleColumnReorder();
+ focusCell(2, 2);
+
+ // when
+ dragAndDropDefaultColumnHeader(2, 0, CellSide.LEFT);
+
+ // then
+ assertFocusedCell(2, 0);
+ }
+
+ @Test
+ public void testColumnReorderWithHiddenColumn_draggingFocusedCellColumnOverHiddenColumn_focusIsKeptOnCell() {
+ // given
+ toggleColumnReorder();
+ selectMenuPath("Component", "Columns", "Column 1", "Hidden");
+ focusCell(2, 2);
+ assertFocusedCell(2, 2);
+
+ // when
+ dragAndDropDefaultColumnHeader(1, 0, CellSide.LEFT);
+
+ // then
+ assertFocusedCell(2, 2);
+
+ // when
+ dragAndDropDefaultColumnHeader(0, 2, CellSide.LEFT);
+
+ // then
+ assertFocusedCell(2, 2);
+ }
+
+ @Test
+ public void testColumnReorder_dragColumnFromRightToLeftOfFocusedCellColumn_focusIsKept() {
+ // given
+ toggleColumnReorder();
+ focusCell(1, 3);
+
+ // when
+ dragAndDropDefaultColumnHeader(4, 1, CellSide.LEFT);
+
+ // then
+ assertFocusedCell(1, 4);
+ }
+
+ @Test
+ public void testColumnReorder_dragColumnFromLeftToRightOfFocusedCellColumn_focusIsKept() {
+ // given
+ toggleColumnReorder();
+ focusCell(4, 2);
+
+ // when
+ dragAndDropDefaultColumnHeader(0, 4, CellSide.LEFT);
+
+ // then
+ assertFocusedCell(4, 1);
+ }
+
+ private void toggleSortableColumn(int index) {
+ selectMenuPath("Component", "Columns", "Column " + index, "Sortable");
+ }
+
+ private void startDragButDontDropOnDefaultColumnHeader(int index) {
+ new Actions(getDriver())
+ .clickAndHold(getGridHeaderRowCells().get(index))
+ .moveByOffset(100, 0).perform();
+ }
+
+ private void sortColumn(int index) {
+ getGridHeaderRowCells().get(index).click();
+ }
+
+ private void focusDefaultHeader(int index) {
+ getGridHeaderRowCells().get(index).click();
+ }
+
+ private WebElement getDraggedHeaderElement() {
+ return findElement(By.className("dragged-column-header"));
+ }
+}