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;
* 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);
}
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),
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.
*/
}
return false;
}
+
+ private List<String> mapColumnsToIds(List<Column<?, JsonObject>> columns) {
+ return columns.stream().map(this::getColumnId).filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
}
*/
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.
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() {
Column<?, T>[] array = reordered
.toArray(new Column[reordered.size()]);
- setColumnOrder(array);
+ setColumnOrder(true, array);
transferCellFocusOnDrop();
} // else no reordering
}
* 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
columnHider.updateTogglesOrder();
- fireEvent(new ColumnReorderEvent<T>());
+ fireEvent(new ColumnReorderEvent<T>(oldOrder, newOrder,
+ isUserOriginated));
}
/**
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;
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;
*/
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",
"columnVisibilityChanged",
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 {
/**
void columnResize(ColumnResizeEvent event);
}
+ /**
+ * 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 {
*
* @since 7.5.0
*/
+ @FunctionalInterface
public interface ColumnVisibilityChangeListener extends Serializable {
/**
@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
/**
* 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
/**
* 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
/**
* 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
*/
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;
}
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}.
*
*
* @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()));
}
/**
* 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)
* Adds a new row at the bottom of the header section.
*
* @return the appended header row
- *
+ *
* @see #prependHeaderRow()
* @see #addHeaderRowAt(int)
* @see #removeHeaderRow(HeaderRow)
* Adds a new row at the top of the header section.
*
* @return the prepended header row
- *
+ *
* @see #appendHeaderRow()
* @see #addHeaderRowAt(int)
* @see #removeHeaderRow(HeaderRow)
*
* @throws IllegalArgumentException
* if the header does not contain the row
- *
+ *
* @see #removeHeaderRow(int)
* @see #addHeaderRowAt(int)
* @see #appendHeaderRow()
*
* @throws IndexOutOfBoundsException
* if {@code index < 0 || index >= getHeaderRowCount()}
- *
+ *
* @see #removeHeaderRow(HeaderRow)
* @see #addHeaderRowAt(int)
* @see #appendHeaderRow()
* 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() {
return header;
}
+ /**
+ * 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.
*
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);
}
}
+ private void fireColumnReorderEvent(boolean userOriginated) {
+ fireEvent(new ColumnReorderEvent(this, userOriginated));
+ }
+
private void fireColumnResizeEvent(Column<?, ?> column,
boolean userOriginated) {
fireEvent(new ColumnResizeEvent(this, column, userOriginated));
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;
"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> {
private List<DataObject> data;
private int watchingCount = 0;
private PersistingDetailsGenerator persistingDetails;
+ private List<Column<DataObject, ?>> initialColumnOrder;
public GridBasics() {
generators.put("NULL", null);
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");
}
}
}).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) {
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
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();
}
"//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();
+ }
+
}
--- /dev/null
+/*
+ * 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"));
+ }
+}