diff options
author | Johannes Dahlström <johannesd@vaadin.com> | 2014-07-15 19:35:31 +0300 |
---|---|---|
committer | Johannes Dahlström <johannesd@vaadin.com> | 2014-07-21 11:42:08 +0300 |
commit | f49e1227ae149833e50188fd21a0d6461af0c25a (patch) | |
tree | 3560228290cf9dddf8c4b8b865610d14f757f5fd | |
parent | a9c124cc19b4e1bf1c8736e209b8e066a002da6f (diff) | |
download | vaadin-framework-f49e1227ae149833e50188fd21a0d6461af0c25a.tar.gz vaadin-framework-f49e1227ae149833e50188fd21a0d6461af0c25a.zip |
Client-side Grid header/footer rewrite: add simple footer support (#13334)
Currently supported:
* A single-row hard-coded header
* A zero-row hard-coded footer
* Text captions
TODO:
* 0..n headers and footers
* Column spanning
* HTML content
* Widget content
* Component content
* Sorting/Indicators
* Server side API
* Shared state handling
Change-Id: Ic85051b16ef77791f1fdae78fca47a0729e1c43d
8 files changed, 427 insertions, 116 deletions
diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java index 5ef94a296c..1edd70daac 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.GridStaticSection.StaticRow; import com.vaadin.client.ui.grid.renderers.ComplexRenderer; import com.vaadin.client.ui.grid.renderers.TextRenderer; import com.vaadin.client.ui.grid.renderers.WidgetRenderer; @@ -409,7 +410,9 @@ public class Grid<T> extends Composite implements */ private Escalator escalator = GWT.create(Escalator.class); - private GridHeader header = GWT.create(GridHeader.class); + private final GridHeader header = GWT.create(GridHeader.class); + + private final GridFooter footer = GWT.create(GridFooter.class); /** * List of columns in the grid. Order defines the visible order. @@ -1292,6 +1295,57 @@ public class Grid<T> extends Composite implements } } + protected class StaticSectionUpdater implements EscalatorUpdater { + + private GridStaticSection<?> section; + + public StaticSectionUpdater(GridStaticSection<?> section) { + super(); + this.section = section; + } + + @Override + public void update(Row row, Iterable<FlyweightCell> cellsToUpdate) { + StaticRow<?> gridRow = section.getRow(row.getRow()); + + final List<Integer> columnIndices = getVisibleColumnIndices(); + + for (FlyweightCell cell : cellsToUpdate) { + int index = columnIndices.get(cell.getColumn()); + gridRow.getRenderer().render(cell, + gridRow.getCell(index).getText()); + + activeCellHandler.updateActiveCellStyle(cell); + } + } + + @Override + public void preAttach(Row row, Iterable<FlyweightCell> cellsToAttach) { + } + + @Override + public void postAttach(Row row, Iterable<FlyweightCell> attachedCells) { + } + + @Override + public void preDetach(Row row, Iterable<FlyweightCell> cellsToDetach) { + } + + @Override + public void postDetach(Row row, Iterable<FlyweightCell> detachedCells) { + } + + private List<Integer> getVisibleColumnIndices() { + List<Integer> indices = new ArrayList<Integer>(getColumnCount()); + for (int i = 0; i < getColumnCount(); i++) { + if (getColumn(i).isVisible()) { + indices.add(i); + } + } + return indices; + } + }; + /** * Creates a new instance. */ @@ -1353,48 +1407,7 @@ public class Grid<T> extends Composite implements * @return the updater that updates the data in the escalator. */ private EscalatorUpdater createHeaderUpdater() { - return new EscalatorUpdater() { - - @Override - public void update(Row row, Iterable<FlyweightCell> cellsToUpdate) { - GridHeader.HeaderRow headerRow = header.getRow(row.getRow()); - - int colIndex = -1; - for (FlyweightCell cell : cellsToUpdate) { - activeCellHandler.updateActiveCellStyle(cell); - - if (colIndex == -1) { - colIndex = cell.getColumn(); - } - while (!columns.get(colIndex).isVisible()) { - colIndex++; - } - - headerRow.getRenderer().render(cell, - headerRow.getCell(colIndex).getText()); - - colIndex++; - } - } - - @Override - public void preAttach(Row row, Iterable<FlyweightCell> cellsToAttach) { - } - - @Override - public void postAttach(Row row, - Iterable<FlyweightCell> attachedCells) { - } - - @Override - public void preDetach(Row row, Iterable<FlyweightCell> cellsToDetach) { - } - - @Override - public void postDetach(Row row, - Iterable<FlyweightCell> detachedCells) { - } - }; + return new StaticSectionUpdater(header); } private EscalatorUpdater createBodyUpdater() { @@ -1537,7 +1550,7 @@ public class Grid<T> extends Composite implements * @return the updater that updates the data in the escalator. */ private EscalatorUpdater createFooterUpdater() { - return EscalatorUpdater.NULL; + return new StaticSectionUpdater(footer); } /** @@ -1552,18 +1565,10 @@ public class Grid<T> extends Composite implements * the footer */ private void refreshRowContainer(RowContainer rows, - boolean firstRowIsVisible, boolean isHeader) { - - // Count needed rows - int totalRows = firstRowIsVisible ? 1 : 0; - for (ColumnGroupRow<T> row : columnGroupRows) { - if (isHeader ? row.isHeaderVisible() : row.isFooterVisible()) { - totalRows++; - } - } + GridStaticSection<?> section) { // Add or Remove rows on demand - int rowDiff = totalRows - rows.getRowCount(); + int rowDiff = section.getRows().size() - rows.getRowCount(); if (rowDiff > 0) { rows.insertRows(0, rowDiff); } else if (rowDiff < 0) { @@ -1580,8 +1585,7 @@ public class Grid<T> extends Composite implements * Refreshes all header rows */ void refreshHeader() { - refreshRowContainer(escalator.getHeader(), isColumnHeadersVisible(), - true); + refreshRowContainer(escalator.getHeader(), header); } /** @@ -1595,8 +1599,7 @@ public class Grid<T> extends Composite implements * Refreshes all footer rows */ void refreshFooter() { - refreshRowContainer(escalator.getFooter(), isColumnFootersVisible(), - false); + refreshRowContainer(escalator.getFooter(), footer); } /** @@ -1638,6 +1641,7 @@ public class Grid<T> extends Composite implements columns.add(index, column); header.addColumn(column, index); + footer.addColumn(column, index); // Register this grid instance with the column ((AbstractGridColumn<?, T>) column).setGrid(this); @@ -1739,7 +1743,9 @@ public class Grid<T> extends Composite implements int columnIndex = columns.indexOf(column); int visibleIndex = findVisibleColumnIndex(column); columns.remove(columnIndex); + header.removeColumn(columnIndex); + footer.removeColumn(columnIndex); // de-register column with grid ((AbstractGridColumn<?, T>) column).setGrid(null); @@ -1992,6 +1998,25 @@ public class Grid<T> extends Composite implements } /** + * Returns the header section of this grid. The default header contains a + * single row displaying the column captions. + * + * @return the header + */ + public GridHeader getHeader() { + return header; + } + + /** + * Returns the footer section of this grid. The default footer is empty. + * + * @return the footer + */ + public GridFooter getFooter() { + return footer; + } + + /** * {@inheritDoc} * <p> * <em>Note:</em> This method will change the widget's size in the browser diff --git a/client/src/com/vaadin/client/ui/grid/GridFooter.java b/client/src/com/vaadin/client/ui/grid/GridFooter.java new file mode 100644 index 0000000000..dc0f0054a2 --- /dev/null +++ b/client/src/com/vaadin/client/ui/grid/GridFooter.java @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.grid; + +import java.util.Collections; +import java.util.List; + +/** + * Represents the footer section of a Grid. The footer is always empty. + * + * TODO Arbitrary number of footer rows (zero by default) + * + * TODO Merging footer cells + * + * TODO Widgets in cells + * + * TODO HTML in cells + * + * @since + * @author Vaadin Ltd + */ +public class GridFooter extends GridStaticSection<GridFooter.FooterRow> { + + /** + * A single row in a grid Footer section. + * + */ + public static class FooterRow extends + GridStaticSection.StaticRow<FooterCell> { + + @Override + protected FooterCell createCell() { + return new FooterCell(); + } + } + + /** + * A single cell in a grid Footer row. Has a textual caption. + * + */ + public static class FooterCell extends GridStaticSection.StaticCell { + } + + @Override + protected FooterRow createRow() { + return new FooterRow(); + } + + @Override + protected List<FooterRow> createRowList() { + return Collections.emptyList(); + } +} diff --git a/client/src/com/vaadin/client/ui/grid/GridHeader.java b/client/src/com/vaadin/client/ui/grid/GridHeader.java index 023973c511..c23c848b8d 100644 --- a/client/src/com/vaadin/client/ui/grid/GridHeader.java +++ b/client/src/com/vaadin/client/ui/grid/GridHeader.java @@ -15,18 +15,15 @@ */ package com.vaadin.client.ui.grid; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import com.vaadin.client.ui.grid.renderers.TextRenderer; - /** * 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 * caption. * - * TODO Arbitrary number of header rows (zero included) + * TODO Arbitrary number of header rows (zero included, one by default) * * TODO Merging header cells * @@ -39,67 +36,35 @@ import com.vaadin.client.ui.grid.renderers.TextRenderer; * @since * @author Vaadin Ltd */ -public class GridHeader { +public class GridHeader extends GridStaticSection<GridHeader.HeaderRow> { /** * A single row in a grid header section. * - * @since - * @author Vaadin Ltd */ - public static class HeaderRow { - - private List<HeaderCell> cells = new ArrayList<HeaderCell>(); - - private Renderer<String> renderer = new TextRenderer(); - - public HeaderCell getCell(int index) { - return cells.get(index); - } - - protected void addCell(int index) { - cells.add(index, new HeaderCell()); - } - - protected void removeCell(int index) { - cells.remove(index); - } + public static class HeaderRow extends + GridStaticSection.StaticRow<HeaderCell> { - protected Renderer<String> getRenderer() { - return renderer; + @Override + protected HeaderCell createCell() { + return new HeaderCell(); } } /** * A single cell in a grid header row. Has a textual caption. * - * @since - * @author Vaadin Ltd */ - public static class HeaderCell { - - private String text = ""; - - public void setText(String text) { - this.text = text; - } - - public String getText() { - return text; - } - } - - private List<HeaderRow> rows = Arrays.asList(new HeaderRow()); - - public HeaderRow getRow(int index) { - return rows.get(index); + public static class HeaderCell extends GridStaticSection.StaticCell { } - protected void addColumn(GridColumn<?, ?> column, int index) { - getRow(0).addCell(index); + @Override + protected HeaderRow createRow() { + return new HeaderRow(); } - protected void removeColumn(int index) { - getRow(0).removeCell(index); + @Override + protected List<HeaderRow> createRowList() { + return Arrays.asList(createRow()); } } diff --git a/client/src/com/vaadin/client/ui/grid/GridStaticSection.java b/client/src/com/vaadin/client/ui/grid/GridStaticSection.java new file mode 100644 index 0000000000..3273c2dfa2 --- /dev/null +++ b/client/src/com/vaadin/client/ui/grid/GridStaticSection.java @@ -0,0 +1,141 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.grid; + +import java.util.ArrayList; +import java.util.List; + +import com.vaadin.client.ui.grid.GridStaticSection.StaticRow; +import com.vaadin.client.ui.grid.renderers.TextRenderer; + +/** + * Abstract base class for Grid header and footer sections. + * + * @since + * @author Vaadin Ltd + * @param <ROWTYPE> + * the type of the rows in the section + */ +abstract class GridStaticSection<ROWTYPE extends StaticRow<?>> { + + /** + * A header or footer cell. Has a simple textual caption. + * + * TODO HTML content + * + * TODO Widget content + */ + static class StaticCell { + + private String text = ""; + + /** + * Sets the text displayed in this cell. + * + * @param text + * a plain text caption + */ + public void setText(String text) { + this.text = text; + } + + /** + * Returns the text displayed in this cell. + * + * @return the plain text caption + */ + public String getText() { + return text; + } + } + + /** + * Abstract base class for Grid header and footer rows. + * + * @param <CELLTYPE> + * the type of the cells in the row + */ + abstract static class StaticRow<CELLTYPE extends StaticCell> { + + private List<CELLTYPE> cells = new ArrayList<CELLTYPE>(); + + private Renderer<String> renderer = new TextRenderer(); + + /** + * Returns the cell at the given position in this row. + * + * @param index + * the position of the cell + * @return the cell at the index + * @throws IndexOutOfBoundsException + * if the index is out of bounds + */ + public CELLTYPE getCell(int index) { + return cells.get(index); + } + + protected void addCell(int index) { + cells.add(index, createCell()); + } + + protected void removeCell(int index) { + cells.remove(index); + } + + protected Renderer<String> getRenderer() { + return renderer; + } + + protected abstract CELLTYPE createCell(); + } + + private List<ROWTYPE> rows = createRowList(); + + /** + * Returns the row at the given position in this section. + * + * @param index + * the position of the row + * @return the row at the index + * @throws IndexOutOfBoundsException + * if the index is out of bounds + */ + public ROWTYPE getRow(int index) { + return rows.get(index); + } + + protected List<ROWTYPE> getRows() { + return rows; + } + + protected void addColumn(GridColumn<?, ?> column, int index) { + for (ROWTYPE row : getRows()) { + row.addCell(index); + } + } + + protected void removeColumn(int index) { + for (ROWTYPE row : getRows()) { + row.removeCell(index); + } + } + + protected List<ROWTYPE> createRowList() { + return new ArrayList<ROWTYPE>(); + } + + protected abstract ROWTYPE createRow(); +} diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeaturesTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeaturesTest.java index a3c62e0303..cc48f11c69 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeaturesTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeaturesTest.java @@ -71,6 +71,14 @@ public abstract class GridBasicFeaturesTest extends MultiBrowserTest { return headerCells; } + protected List<TestBenchElement> getGridFooterRowCells() { + List<TestBenchElement> footerCells = new ArrayList<TestBenchElement>(); + for (int i = 0; i < getGridElement().getFooterCount(); ++i) { + footerCells.addAll(getGridElement().getFooterCells(i)); + } + return footerCells; + } + private Object executeScript(String script, WebElement element) { final WebDriver driver = getDriver(); if (driver instanceof JavascriptExecutor) { diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridFooterTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridFooterTest.java new file mode 100644 index 0000000000..e126994f34 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridFooterTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2014 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.basicfeatures; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.junit.Test; + +import com.vaadin.testbench.TestBenchElement; + +public class GridFooterTest extends GridBasicClientFeaturesTest { + + @Test + public void testFooterVisibility() throws Exception { + openTestURL(); + + // Footer should have zero rows by default + List<TestBenchElement> cells = getGridFooterRowCells(); + assertEquals(0, cells.size()); + } +} 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 dfbb1679b0..716e3b30fc 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridHeaderTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridHeaderTest.java @@ -26,11 +26,54 @@ import com.vaadin.testbench.TestBenchElement; public class GridHeaderTest extends GridBasicClientFeaturesTest { @Test - public void testHeaderVisible() throws Exception { + public void testHeaderVisibility() throws Exception { openTestURL(); - // Column headers should be visible + // Column headers should be visible by default List<TestBenchElement> cells = getGridHeaderRowCells(); assertEquals(GridBasicFeatures.COLUMNS, cells.size()); } + + @Test + public void testHeaderCaptions() throws Exception { + openTestURL(); + + List<TestBenchElement> cells = getGridHeaderRowCells(); + + int i = 0; + for (TestBenchElement cell : cells) { + assertText("Column " + i, cell); + i++; + } + } + + @Test + public void testHeadersWithInvisibleColumns() throws Exception { + openTestURL(); + + selectMenuPath("Component", "Columns", "Column 1", "Visible"); + selectMenuPath("Component", "Columns", "Column 3", "Visible"); + + List<TestBenchElement> cells = getGridHeaderRowCells(); + assertEquals(GridBasicFeatures.COLUMNS - 2, cells.size()); + + assertText("Column 0", cells.get(0)); + assertText("Column 2", cells.get(1)); + assertText("Column 4", cells.get(2)); + + selectMenuPath("Component", "Columns", "Column 3", "Visible"); + + cells = getGridHeaderRowCells(); + assertEquals(GridBasicFeatures.COLUMNS - 1, cells.size()); + + assertText("Column 0", cells.get(0)); + assertText("Column 2", cells.get(1)); + assertText("Column 3", cells.get(2)); + assertText("Column 4", cells.get(3)); + } + + private static void assertText(String text, TestBenchElement e) { + // TBE.getText returns "" if the element is scrolled out of view + assertEquals(text, e.getAttribute("innerHTML")); + } } 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 857ff14528..182a5bfa29 100644 --- a/uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeatures.java +++ b/uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeatures.java @@ -25,6 +25,7 @@ import com.vaadin.client.ui.grid.FlyweightCell; import com.vaadin.client.ui.grid.Grid; import com.vaadin.client.ui.grid.Grid.SelectionMode; import com.vaadin.client.ui.grid.GridColumn; +import com.vaadin.client.ui.grid.GridHeader.HeaderCell; import com.vaadin.client.ui.grid.Renderer; import com.vaadin.client.ui.grid.datasources.ListDataSource; import com.vaadin.client.ui.grid.renderers.DateRenderer; @@ -192,35 +193,61 @@ public class GridBasicClientFeatures extends }); } + // Set captions to column headers + + for (int i = 0; i < COLUMNS; ++i) { + HeaderCell cell = grid.getHeader().getRow(0).getCell(i); + cell.setText("Column " + i); + } + // // Populate the menu // + createStateMenu(); + createColumnsMenu(); + + grid.getElement().getStyle().setZIndex(0); + add(grid); + } + + private void createStateMenu() { + String[] selectionModePath = { "Component", "State", "Selection mode" }; + addMenuCommand("multi", new ScheduledCommand() { @Override public void execute() { grid.setSelectionMode(SelectionMode.MULTI); } - }, "Component", "State", "Selection mode"); + }, selectionModePath); addMenuCommand("single", new ScheduledCommand() { - @Override public void execute() { grid.setSelectionMode(SelectionMode.SINGLE); } - }, "Component", "State", "Selection mode"); + }, selectionModePath); addMenuCommand("none", new ScheduledCommand() { - @Override public void execute() { grid.setSelectionMode(SelectionMode.NONE); } - }, "Component", "State", "Selection mode"); + }, selectionModePath); + } - grid.getElement().getStyle().setZIndex(0); - add(grid); + private void createColumnsMenu() { + + for (int i = 0; i < COLUMNS; i++) { + final int index = i; + addMenuCommand("Visible", new ScheduledCommand() { + @Override + public void execute() { + grid.getColumn(index).setVisible( + !grid.getColumn(index).isVisible()); + } + }, "Component", "Columns", "Column " + i); + } } /** |