summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Dahlström <johannesd@vaadin.com>2014-07-16 11:16:38 +0300
committerJohn Ahlroos <john@vaadin.com>2014-07-29 09:53:00 +0300
commitcfad6d54de44fead836ec9355bbc77c06fa26a40 (patch)
treef93d1813a1360d2a7890ddf3f95aea56fd8e7476
parentff310e8bdbfc773153e4faa9eb5749bb3e44700e (diff)
downloadvaadin-framework-cfad6d54de44fead836ec9355bbc77c06fa26a40.tar.gz
vaadin-framework-cfad6d54de44fead836ec9355bbc77c06fa26a40.zip
Client-side Grid header/footer rewrite: add default header support (#13334)
Currently supported: * Adding and removal of header and footer rows * Header is single-row by default * Footer is zero-row by default * Text captions * Showing and hiding the whole header or footer * Default header rows for sorting UI TODO: * Column spanning * HTML content * Widget content * Component content * Server side API * Shared state handling Change-Id: I3d6a2b75fad87780f83238ab792bbbcfe99a48fd
-rw-r--r--client/src/com/vaadin/client/ui/grid/Grid.java74
-rw-r--r--client/src/com/vaadin/client/ui/grid/GridConnector.java5
-rw-r--r--client/src/com/vaadin/client/ui/grid/GridHeader.java73
-rw-r--r--client/src/com/vaadin/client/ui/grid/GridStaticSection.java9
-rw-r--r--server/src/com/vaadin/ui/components/grid/Grid.java7
-rw-r--r--shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java2
-rw-r--r--uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridHeaderTest.java38
-rw-r--r--uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeatures.java30
8 files changed, 206 insertions, 32 deletions
diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java
index 403b0d1e3f..ff75417c12 100644
--- a/client/src/com/vaadin/client/ui/grid/Grid.java
+++ b/client/src/com/vaadin/client/ui/grid/Grid.java
@@ -45,6 +45,7 @@ import com.vaadin.client.Util;
import com.vaadin.client.data.DataChangeHandler;
import com.vaadin.client.data.DataSource;
import com.vaadin.client.ui.SubPartAware;
+import com.vaadin.client.ui.grid.GridHeader.HeaderRow;
import com.vaadin.client.ui.grid.renderers.ComplexRenderer;
import com.vaadin.client.ui.grid.renderers.TextRenderer;
import com.vaadin.client.ui.grid.renderers.WidgetRenderer;
@@ -616,6 +617,13 @@ public class Grid<T> extends Composite implements
protected abstract <T> SelectionModel<T> createModel();
}
+ class SortableColumnHeaderRenderer extends
+ AbstractGridColumn.SortableColumnHeaderRenderer {
+ SortableColumnHeaderRenderer(Renderer<String> cellRenderer) {
+ super(Grid.this, cellRenderer);
+ }
+ }
+
/**
* Base class for grid columns internally used by the Grid. The user should
* use {@link GridColumn} when creating new columns.
@@ -633,9 +641,11 @@ public class Grid<T> extends Composite implements
*
* FIXME Currently assumes multisorting
*/
- private class SortableColumnHeaderRenderer extends
+ static class SortableColumnHeaderRenderer extends
ComplexRenderer<String> {
+ private Grid<?> grid;
+
/**
* Delay before a long tap action is triggered. Number in
* milliseconds.
@@ -658,7 +668,8 @@ public class Grid<T> extends Composite implements
@Override
public void run() {
- SortOrder sortingOrder = getSortingOrder();
+ SortOrder sortingOrder = getSortingOrder(grid
+ .getColumnFromVisibleIndex(cell.getColumn()));
if (sortingOrder == null) {
/*
* No previous sorting, sort Ascending
@@ -697,7 +708,9 @@ public class Grid<T> extends Composite implements
* @param cellRenderer
* The actual cell renderer
*/
- public SortableColumnHeaderRenderer(Renderer<String> cellRenderer) {
+ public SortableColumnHeaderRenderer(Grid<?> grid,
+ Renderer<String> cellRenderer) {
+ this.grid = grid;
this.cellRenderer = cellRenderer;
}
@@ -715,9 +728,11 @@ public class Grid<T> extends Composite implements
* this is fixed.
*/
if (grid != null) {
- SortOrder sortingOrder = getSortingOrder();
+ GridColumn<?, ?> column = grid
+ .getColumnFromVisibleIndex(cell.getColumn());
+ SortOrder sortingOrder = getSortingOrder(column);
Element cellElement = cell.getElement();
- if (grid.getColumn(cell.getColumn()).isSortable()) {
+ if (column.isSortable()) {
if (sortingOrder != null) {
if (SortDirection.ASCENDING == sortingOrder
.getDirection()) {
@@ -836,6 +851,18 @@ public class Grid<T> extends Composite implements
}
+ protected void removeFromRow(HeaderRow row) {
+ row.setRenderer(new Renderer<String>() {
+ @Override
+ public void render(FlyweightCell cell, String data) {
+ cleanup(cell);
+ }
+ });
+ grid.refreshHeader();
+ row.setRenderer(cellRenderer);
+ grid.refreshHeader();
+ }
+
/**
* Sorts the column in a direction
*/
@@ -844,13 +871,14 @@ public class Grid<T> extends Composite implements
TableCellElement th = TableCellElement.as(cell.getElement());
// Apply primary sorting on clicked column
- GridColumn<C, T> columnInstance = getColumnInstance();
+ GridColumn<?, ?> columnInstance = grid
+ .getColumnFromVisibleIndex(cell.getColumn());
Sort sorting = Sort.by(columnInstance, direction);
// Re-apply old sorting to the sort order
if (multisort) {
for (SortOrder order : grid.getSortOrder()) {
- if (order.getColumn() != AbstractGridColumn.this) {
+ if (order.getColumn() != columnInstance) {
sorting = sorting.then(order.getColumn(),
order.getDirection());
}
@@ -862,23 +890,11 @@ public class Grid<T> extends Composite implements
}
/**
- * Resolves a GridColumn out of a AbstractGridColumn
- */
- private GridColumn<C, T> getColumnInstance() {
- for (GridColumn<?, T> column : grid.getColumns()) {
- if (column == AbstractGridColumn.this) {
- return (GridColumn<C, T>) column;
- }
- }
- return null;
- }
-
- /**
* Finds the sorting order for this column
*/
- private SortOrder getSortingOrder() {
+ private SortOrder getSortingOrder(GridColumn<?, ?> column) {
for (SortOrder order : grid.getSortOrder()) {
- if (order.getColumn() == AbstractGridColumn.this) {
+ if (order.getColumn() == column) {
return order;
}
}
@@ -922,8 +938,7 @@ public class Grid<T> extends Composite implements
* Renderer for rendering the header cell value into the cell
*/
@Deprecated
- private Renderer<String> headerRenderer = new SortableColumnHeaderRenderer(
- new TextRenderer());
+ private Renderer<String> headerRenderer = new TextRenderer();
/**
* Renderer for rendering the footer cell value into the cell
@@ -964,8 +979,7 @@ public class Grid<T> extends Composite implements
throw new IllegalArgumentException("Renderer cannot be null.");
}
- this.headerRenderer = new SortableColumnHeaderRenderer(
- headerRenderer);
+ this.headerRenderer = headerRenderer;
this.footerRenderer = footerRenderer;
}
@@ -1016,7 +1030,7 @@ public class Grid<T> extends Composite implements
if (renderer == null) {
throw new IllegalArgumentException("Renderer cannot be null.");
}
- headerRenderer = new SortableColumnHeaderRenderer(headerRenderer);
+ this.headerRenderer = headerRenderer;
if (grid != null) {
grid.refreshHeader();
}
@@ -1472,7 +1486,8 @@ public class Grid<T> extends Composite implements
escalator.getFooter().setEscalatorUpdater(createFooterUpdater());
header.setGrid(this);
- header.appendRow();
+ HeaderRow defaultRow = header.appendRow();
+ header.setDefaultRow(defaultRow);
footer.setGrid(this);
@@ -2470,13 +2485,14 @@ public class Grid<T> extends Composite implements
if (container != null) {
cell = container.getCell(e);
if (cell != null) {
+ // FIXME getFromVisibleIndex???
GridColumn<?, T> gridColumn = columns.get(cell.getColumn());
Renderer<?> renderer;
if (container == escalator.getHeader()) {
- renderer = gridColumn.getHeaderRenderer();
+ renderer = header.getRow(cell.getRow()).getRenderer();
} else if (container == escalator.getFooter()) {
- renderer = gridColumn.getFooterRenderer();
+ renderer = footer.getRow(cell.getRow()).getRenderer();
} else {
renderer = gridColumn.getRenderer();
}
diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java
index 67f4dc4045..3c7629a60d 100644
--- a/client/src/com/vaadin/client/ui/grid/GridConnector.java
+++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java
@@ -36,6 +36,7 @@ import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.data.DataSource.RowHandle;
import com.vaadin.client.data.RpcDataSourceConnector.RpcDataSource;
import com.vaadin.client.ui.AbstractComponentConnector;
+import com.vaadin.client.ui.grid.GridHeader.HeaderRow;
import com.vaadin.client.ui.grid.GridStaticSection.StaticRow;
import com.vaadin.client.ui.grid.renderers.AbstractRendererConnector;
import com.vaadin.client.ui.grid.selection.AbstractRowHandleSelectionModel;
@@ -368,6 +369,10 @@ public class GridConnector extends AbstractComponentConnector {
for (CellState cellState : rowState.cells) {
row.getCell(i++).setText(cellState.text);
}
+
+ if (section instanceof GridHeader && rowState.defaultRow) {
+ ((GridHeader) section).setDefaultRow((HeaderRow) row);
+ }
}
section.setVisible(state.visible);
diff --git a/client/src/com/vaadin/client/ui/grid/GridHeader.java b/client/src/com/vaadin/client/ui/grid/GridHeader.java
index a2207c49c7..c5b0febeca 100644
--- a/client/src/com/vaadin/client/ui/grid/GridHeader.java
+++ b/client/src/com/vaadin/client/ui/grid/GridHeader.java
@@ -15,6 +15,8 @@
*/
package com.vaadin.client.ui.grid;
+import com.vaadin.client.ui.grid.Grid.AbstractGridColumn.SortableColumnHeaderRenderer;
+
/**
* Represents the header section of a Grid. A header consists of a single header
* row containing a header cell for each column. Each cell has a simple textual
@@ -41,6 +43,16 @@ public class GridHeader extends GridStaticSection<GridHeader.HeaderRow> {
*/
public class HeaderRow extends GridStaticSection.StaticRow<HeaderCell> {
+ private boolean isDefault = false;
+
+ protected void setDefault(boolean isDefault) {
+ this.isDefault = isDefault;
+ }
+
+ public boolean isDefault() {
+ return isDefault;
+ }
+
@Override
protected HeaderCell createCell() {
return new HeaderCell();
@@ -54,6 +66,67 @@ public class GridHeader extends GridStaticSection<GridHeader.HeaderRow> {
public class HeaderCell extends GridStaticSection.StaticCell {
}
+ private HeaderRow defaultRow;
+
+ @Override
+ public void removeRow(int index) {
+ HeaderRow removedRow = getRow(index);
+ super.removeRow(index);
+ if (removedRow == defaultRow) {
+ setDefaultRow(null);
+ }
+ }
+
+ /**
+ * Sets the default row of this header. The default row is a special header
+ * row providing a user interface for sorting columns.
+ *
+ * @param row
+ * the new default row, or null for no default row
+ *
+ * @throws IllegalArgumentException
+ * this header does not contain the row
+ */
+ public void setDefaultRow(HeaderRow row) {
+ if (row == defaultRow) {
+ return;
+ }
+ if (row != null && !getRows().contains(row)) {
+ throw new IllegalArgumentException(
+ "Cannot set a default row that does not exist in the container");
+ }
+ if (defaultRow != null) {
+ assert defaultRow.getRenderer() instanceof SortableColumnHeaderRenderer;
+
+ // Eclipse is wrong about this warning - javac does not accept the
+ // parameterized version
+ ((Grid.SortableColumnHeaderRenderer) defaultRow.getRenderer())
+ .removeFromRow(defaultRow);
+
+ defaultRow.setDefault(false);
+ }
+ if (row != null) {
+ assert !(row.getRenderer() instanceof SortableColumnHeaderRenderer);
+
+ row.setRenderer(getGrid().new SortableColumnHeaderRenderer(row
+ .getRenderer()));
+
+ row.setDefault(true);
+ }
+ defaultRow = row;
+ refreshGrid();
+ }
+
+ /**
+ * Returns the current default row of this header. The default row is a
+ * special header row providing a user interface for sorting columns.
+ *
+ * @return the default row or null if no default row set
+ */
+ public HeaderRow getDefaultRow() {
+ return defaultRow;
+ }
+
@Override
protected HeaderRow createRow() {
return new HeaderRow();
diff --git a/client/src/com/vaadin/client/ui/grid/GridStaticSection.java b/client/src/com/vaadin/client/ui/grid/GridStaticSection.java
index f455acc92e..4318811ca2 100644
--- a/client/src/com/vaadin/client/ui/grid/GridStaticSection.java
+++ b/client/src/com/vaadin/client/ui/grid/GridStaticSection.java
@@ -111,6 +111,10 @@ abstract class GridStaticSection<ROWTYPE extends GridStaticSection.StaticRow<?>>
cells.remove(index);
}
+ protected void setRenderer(Renderer<String> renderer) {
+ this.renderer = renderer;
+ }
+
protected Renderer<String> getRenderer() {
return renderer;
}
@@ -227,11 +231,12 @@ abstract class GridStaticSection<ROWTYPE extends GridStaticSection.StaticRow<?>>
* if the row does not exist in this section
*/
public void removeRow(ROWTYPE row) {
- if (!rows.remove(row)) {
+ try {
+ removeRow(rows.indexOf(row));
+ } catch (IndexOutOfBoundsException e) {
throw new IllegalArgumentException(
"Section does not contain the given row");
}
- refreshGrid();
}
/**
diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java
index e4e6dcf4be..67a97c74b7 100644
--- a/server/src/com/vaadin/ui/components/grid/Grid.java
+++ b/server/src/com/vaadin/ui/components/grid/Grid.java
@@ -225,8 +225,13 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier {
*/
public Grid(final Container.Indexed datasource) {
- getState().header.rows.add(new RowState());
+ RowState headerDefaultRow = new RowState();
+ headerDefaultRow.defaultRow = true;
+ getState().header.rows.add(headerDefaultRow);
+
+ // FIXME By default there shouldn't be any footer row
getState().footer.rows.add(new RowState());
+
setColumnFootersVisible(false);
setContainerDataSource(datasource);
diff --git a/shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java b/shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java
index 358c06d089..859e01f089 100644
--- a/shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java
+++ b/shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java
@@ -33,6 +33,8 @@ public class GridStaticSectionState implements Serializable {
public static class RowState implements Serializable {
public List<CellState> cells = new ArrayList<CellState>();
+
+ public boolean defaultRow = false;
}
public List<RowState> rows = new ArrayList<RowState>();
diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridHeaderTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridHeaderTest.java
index 2ced9e16d4..1b27350f25 100644
--- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridHeaderTest.java
+++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridHeaderTest.java
@@ -16,12 +16,16 @@
package com.vaadin.tests.components.grid.basicfeatures;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import com.vaadin.testbench.TestBenchElement;
+import com.vaadin.tests.components.grid.GridElement.GridCellElement;
public class GridHeaderTest extends GridStaticSectionTest {
@@ -126,7 +130,41 @@ public class GridHeaderTest extends GridStaticSectionTest {
assertHeaderTexts(0, 0);
}
+ @Test
+ public void testDefaultRow() throws Exception {
+ openTestURL();
+
+ selectMenuPath("Component", "Columns", "Column 0", "Sortable");
+
+ GridCellElement headerCell = getGridElement().getHeaderCell(0, 0);
+
+ headerCell.click();
+
+ assertTrue(hasClassName(headerCell, "sort-asc"));
+
+ headerCell.click();
+
+ assertFalse(hasClassName(headerCell, "sort-asc"));
+ assertTrue(hasClassName(headerCell, "sort-desc"));
+
+ selectMenuPath("Component", "Header", "Prepend row");
+ selectMenuPath("Component", "Header", "Default row", "Top");
+
+ assertFalse(hasClassName(headerCell, "sort-desc"));
+ headerCell = getGridElement().getHeaderCell(0, 0);
+ assertTrue(hasClassName(headerCell, "sort-desc"));
+
+ selectMenuPath("Component", "Header", "Default row", "Unset");
+
+ assertFalse(hasClassName(headerCell, "sort-desc"));
+ }
+
private void assertHeaderCount(int count) {
assertEquals("header count", count, getGridElement().getHeaderCount());
}
+
+ private boolean hasClassName(TestBenchElement element, String name) {
+ return Arrays.asList(element.getAttribute("class").split(" "))
+ .contains(name);
+ }
}
diff --git a/uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeatures.java b/uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeatures.java
index 21d65d56ac..e013306dc0 100644
--- a/uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeatures.java
+++ b/uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeatures.java
@@ -31,6 +31,7 @@ import com.vaadin.client.ui.grid.GridHeader;
import com.vaadin.client.ui.grid.GridHeader.HeaderRow;
import com.vaadin.client.ui.grid.Renderer;
import com.vaadin.client.ui.grid.datasources.ListDataSource;
+import com.vaadin.client.ui.grid.datasources.ListSorter;
import com.vaadin.client.ui.grid.renderers.DateRenderer;
import com.vaadin.client.ui.grid.renderers.HtmlRenderer;
import com.vaadin.client.ui.grid.renderers.NumberRenderer;
@@ -57,6 +58,7 @@ public class GridBasicClientFeatures extends
private final Grid<List<Data>> grid;
private final List<List<Data>> data;
private final ListDataSource<List<Data>> ds;
+ private final ListSorter<List<Data>> sorter;
/**
* Our basic data object
@@ -124,6 +126,8 @@ public class GridBasicClientFeatures extends
grid.setDataSource(ds);
grid.setSelectionMode(SelectionMode.NONE);
+ sorter = new ListSorter<List<Data>>(grid);
+
// Create a bunch of grid columns
// Data source layout:
@@ -247,6 +251,13 @@ public class GridBasicClientFeatures extends
!grid.getColumn(index).isVisible());
}
}, "Component", "Columns", "Column " + i);
+ addMenuCommand("Sortable", new ScheduledCommand() {
+ @Override
+ public void execute() {
+ grid.getColumn(index).setSortable(
+ !grid.getColumn(index).isSortable());
+ }
+ }, "Component", "Columns", "Column " + i);
}
}
@@ -278,6 +289,25 @@ public class GridBasicClientFeatures extends
}
}, menuPath);
+ addMenuCommand("Top", new ScheduledCommand() {
+ @Override
+ public void execute() {
+ header.setDefaultRow(header.getRow(0));
+ }
+ }, "Component", "Header", "Default row");
+ addMenuCommand("Bottom", new ScheduledCommand() {
+ @Override
+ public void execute() {
+ header.setDefaultRow(header.getRow(header.getRowCount() - 1));
+ }
+ }, "Component", "Header", "Default row");
+ addMenuCommand("Unset", new ScheduledCommand() {
+ @Override
+ public void execute() {
+ header.setDefaultRow(null);
+ }
+ }, "Component", "Header", "Default row");
+
addMenuCommand("Prepend row", new ScheduledCommand() {
@Override
public void execute() {