summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java22
-rw-r--r--server/src/main/java/com/vaadin/ui/Grid.java189
-rw-r--r--server/src/main/java/com/vaadin/ui/components/grid/Footer.java70
-rw-r--r--shared/src/main/java/com/vaadin/shared/ui/grid/GridState.java3
-rw-r--r--uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java31
-rw-r--r--uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridHeaderFooterTest.java60
6 files changed, 367 insertions, 8 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 4686b3bd21..a19c955fc4 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
@@ -53,6 +53,7 @@ import com.vaadin.client.widget.grid.sort.SortEvent;
import com.vaadin.client.widget.grid.sort.SortOrder;
import com.vaadin.client.widgets.Grid;
import com.vaadin.client.widgets.Grid.Column;
+import com.vaadin.client.widgets.Grid.FooterRow;
import com.vaadin.client.widgets.Grid.HeaderRow;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.shared.data.DataCommunicatorConstants;
@@ -264,6 +265,27 @@ public class GridConnector
}
}
+ /**
+ * Updates the grid footer section on state change.
+ */
+ @OnStateChange("footer")
+ void updateFooter() {
+ final Grid<JsonObject> grid = getWidget();
+ final SectionState state = getState().footer;
+
+ while (grid.getFooterRowCount() > 0) {
+ grid.removeFooterRow(0);
+ }
+
+ for (RowState rowState : state.rows) {
+ FooterRow row = grid.appendFooterRow();
+
+ rowState.cells.forEach((columnId, cellState) -> {
+ row.getCell(getColumn(columnId)).setText(cellState.text);
+ });
+ }
+ }
+
@Override
public void setDataSource(DataSource<JsonObject> dataSource) {
super.setDataSource(dataSource);
diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java
index 843db35e16..b904565a64 100644
--- a/server/src/main/java/com/vaadin/ui/Grid.java
+++ b/server/src/main/java/com/vaadin/ui/Grid.java
@@ -54,6 +54,8 @@ import com.vaadin.shared.ui.grid.GridState;
import com.vaadin.shared.ui.grid.HeightMode;
import com.vaadin.shared.ui.grid.SectionState;
import com.vaadin.shared.util.SharedUtil;
+import com.vaadin.ui.Grid.FooterRow;
+import com.vaadin.ui.components.grid.Footer;
import com.vaadin.ui.components.grid.Header;
import com.vaadin.ui.components.grid.Header.Row;
import com.vaadin.ui.renderers.AbstractRenderer;
@@ -1562,6 +1564,57 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
public void setText(String text);
}
+ /**
+ * A footer row in a Grid.
+ */
+ public interface FooterRow extends Serializable {
+
+ /**
+ * Returns the cell on this row corresponding to the given column id.
+ *
+ * @param columnId
+ * the id of the column whose footer cell to get, not null
+ * @return the footer cell
+ * @throws IllegalArgumentException
+ * if there is no such column in the grid
+ */
+ public FooterCell getCell(String columnId);
+
+ /**
+ * Returns the cell on this row corresponding to the given column.
+ *
+ * @param column
+ * the column whose footer cell to get, not null
+ * @return the footer cell
+ * @throws IllegalArgumentException
+ * if there is no such column in the grid
+ */
+ public default FooterCell getCell(Column<?, ?> column) {
+ return getCell(column.getId());
+ }
+ }
+
+ /**
+ * An individual cell on a Grid footer row.
+ */
+ public interface FooterCell extends Serializable {
+
+ /**
+ * Returns the textual caption of this cell.
+ *
+ * @return the footer caption
+ */
+ public String getText();
+
+ /**
+ * Sets the textual caption of this cell.
+ *
+ * @param text
+ * the footer caption to set, not null
+ */
+ public void setText(String text);
+ }
+
private class HeaderImpl extends Header {
@Override
@@ -1575,6 +1628,19 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
}
};
+ private class FooterImpl extends Footer {
+
+ @Override
+ protected SectionState getState(boolean markAsDirty) {
+ return Grid.this.getState(markAsDirty).footer;
+ }
+
+ @Override
+ protected Collection<Column<T, ?>> getColumns() {
+ return Grid.this.getColumns();
+ }
+ };
+
private Set<Column<T, ?>> columnSet = new LinkedHashSet<>();
private Map<String, Column<T, ?>> columnKeys = new HashMap<>();
@@ -1585,6 +1651,7 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
private DescriptionGenerator<T> descriptionGenerator;
private Header header = new HeaderImpl();
+ private Footer footer = new FooterImpl();
private int counter = 0;
@@ -2156,6 +2223,128 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
}
/**
+ * Returns the footer row at the given index.
+ *
+ * @param index
+ * the index of the row, where the topmost row has index zero
+ * @return the footer row at the index
+ * @throws IndexOutOfBoundsException
+ * if {@code rowIndex < 0 || rowIndex >= getFooterRowCount()}
+ */
+ public FooterRow getFooterRow(int index) {
+ return getFooter().getRow(index);
+ }
+
+ /**
+ * Gets the number of rows in the footer section.
+ *
+ * @return the number of footer rows
+ */
+ public int getFooterRowCount() {
+ return getFooter().getRowCount();
+ }
+
+ /**
+ * Inserts a new row at the given position to the footer section. Shifts the
+ * row currently at that position and any subsequent rows down (adds one to
+ * their indices). Inserting at {@link #getFooterRowCount()} appends the row
+ * at the bottom of the footer.
+ *
+ * @param index
+ * the index at which to insert the row, where the topmost row
+ * has index zero
+ * @return the inserted footer row
+ *
+ * @throws IndexOutOfBoundsException
+ * if {@code rowIndex < 0 || rowIndex > getFooterRowCount()}
+ *
+ * @see #appendFooterRow()
+ * @see #prependFooterRow()
+ * @see #removeFooterRow(FooterRow)
+ * @see #removeFooterRow(int)
+ */
+ public FooterRow addFooterRowAt(int index) {
+ return getFooter().addRowAt(index);
+ }
+
+ /**
+ * Adds a new row at the bottom of the footer section.
+ *
+ * @return the appended footer row
+ *
+ * @see #prependFooterRow()
+ * @see #addFooterRowAt(int)
+ * @see #removeFooterRow(FooterRow)
+ * @see #removeFooterRow(int)
+ */
+ public FooterRow appendFooterRow() {
+ return addFooterRowAt(getFooterRowCount());
+ }
+
+ /**
+ * Adds a new row at the top of the footer section.
+ *
+ * @return the prepended footer row
+ *
+ * @see #appendFooterRow()
+ * @see #addFooterRowAt(int)
+ * @see #removeFooterRow(FooterRow)
+ * @see #removeFooterRow(int)
+ */
+ public FooterRow prependFooterRow() {
+ return addFooterRowAt(0);
+ }
+
+ /**
+ * Removes the given row from the footer section. Removing a default row
+ * sets the Grid to have no default row.
+ *
+ * @param row
+ * the footer row to be removed, not null
+ *
+ * @throws IllegalArgumentException
+ * if the footer does not contain the row
+ *
+ * @see #removeFooterRow(int)
+ * @see #addFooterRowAt(int)
+ * @see #appendFooterRow()
+ * @see #prependFooterRow()
+ */
+ public void removeFooterRow(FooterRow row) {
+ getFooter().removeRow(row);
+ }
+
+ /**
+ * Removes the row at the given position from the footer section.
+ *
+ * @param index
+ * the index of the row to remove, where the topmost row has
+ * index zero
+ *
+ * @throws IndexOutOfBoundsException
+ * if {@code index < 0 || index >= getFooterRowCount()}
+ *
+ * @see #removeFooterRow(FooterRow)
+ * @see #addFooterRowAt(int)
+ * @see #appendFooterRow()
+ * @see #prependFooterRow()
+ */
+ public void removeFooterRow(int index) {
+ getFooter().removeRow(index);
+ }
+
+ /**
+ * Returns the footer section of this grid. The default footer contains a
+ * single row, set as the {@linkplain #setDefaultFooterRow(FooterRow)
+ * default row}.
+ *
+ * @return the footer section
+ */
+ protected Footer getFooter() {
+ return footer;
+ }
+
+ /**
* Registers a new column reorder listener.
*
* @param listener
diff --git a/server/src/main/java/com/vaadin/ui/components/grid/Footer.java b/server/src/main/java/com/vaadin/ui/components/grid/Footer.java
new file mode 100644
index 0000000000..9423e2d842
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/components/grid/Footer.java
@@ -0,0 +1,70 @@
+/*
+ * 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.ui.components.grid;
+
+import com.vaadin.ui.Grid;
+
+/**
+ * Represents the footer section of a Grid.
+ *
+ * @author Vaadin Ltd.
+ *
+ * @since 8.0
+ */
+public abstract class Footer extends StaticSection<Footer.Row> {
+
+ /**
+ * A row in a Grid Footer.
+ */
+ public class Row extends StaticSection.StaticRow<Row.Cell>
+ implements Grid.FooterRow {
+
+ /**
+ * A cell in a Grid footer row.
+ */
+ public class Cell extends StaticSection.StaticCell
+ implements Grid.FooterCell {
+ /**
+ * Creates a new footer cell.
+ */
+ protected Cell() {
+ super(Row.this);
+ }
+ }
+
+ /**
+ * Creates a new footer row.
+ */
+ protected Row() {
+ super(Footer.this);
+ }
+
+ @Override
+ protected Cell createCell() {
+ return new Cell();
+ }
+
+ @Override
+ protected String getCellTagName() {
+ return "td";
+ }
+ }
+
+ @Override
+ public Row createRow() {
+ return new Row();
+ }
+}
diff --git a/shared/src/main/java/com/vaadin/shared/ui/grid/GridState.java b/shared/src/main/java/com/vaadin/shared/ui/grid/GridState.java
index 6c9631b598..a8abf82c75 100644
--- a/shared/src/main/java/com/vaadin/shared/ui/grid/GridState.java
+++ b/shared/src/main/java/com/vaadin/shared/ui/grid/GridState.java
@@ -102,6 +102,9 @@ public class GridState extends AbstractSingleSelectState {
/** The state of the header section. */
public SectionState header = new SectionState();
+ /** The state of the footer section. */
+ public SectionState footer = new SectionState();
+
/**
* Column order in grid.
*/
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 9fce271c6d..f56767e3d3 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
@@ -20,6 +20,7 @@ import com.vaadin.ui.Component;
import com.vaadin.ui.Grid;
import com.vaadin.ui.Grid.Column;
import com.vaadin.ui.Grid.DetailsGenerator;
+import com.vaadin.ui.Grid.FooterRow;
import com.vaadin.ui.Grid.HeaderRow;
import com.vaadin.ui.Label;
import com.vaadin.ui.MenuBar;
@@ -199,6 +200,7 @@ public class GridBasics extends AbstractReindeerTestUIWithLog {
createDetailsMenu(componentMenu.addItem("Details", null));
createBodyMenu(componentMenu.addItem("Body rows", null));
createHeaderMenu(componentMenu.addItem("Header", null));
+ createFooterMenu(componentMenu.addItem("Footer", null));
createColumnsMenu(componentMenu.addItem("Columns", null));
return menu;
}
@@ -409,6 +411,35 @@ public class GridBasics extends AbstractReindeerTestUIWithLog {
});
}
+ private void createFooterMenu(MenuItem footerMenu) {
+ footerMenu.addItem("Add default footer row", menuItem -> {
+ FooterRow defaultFooter = grid.appendFooterRow();
+ grid.getColumns().forEach(
+ column -> defaultFooter.getCell(column).setText(grid
+ .getDefaultHeaderRow().getCell(column).getText()));
+ footerMenu.removeChild(menuItem);
+ });
+ footerMenu.addItem("Append footer row", menuItem -> {
+ FooterRow row = grid.appendFooterRow();
+
+ int i = 0;
+ for (Column<?, ?> column : grid.getColumns()) {
+ row.getCell(column).setText("Footer cell " + i++);
+ }
+ });
+ footerMenu.addItem("Prepend footer row", menuItem -> {
+ FooterRow row = grid.prependFooterRow();
+
+ int i = 0;
+ for (Column<?, ?> column : grid.getColumns()) {
+ row.getCell(column).setText("Footer cell " + i++);
+ }
+ });
+ footerMenu.addItem("Remove first footer row", menuItem -> {
+ grid.removeFooterRow(0);
+ });
+ }
+
/* DetailsGenerator related things */
private void createDetailsMenu(MenuItem detailsMenu) {
diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridHeaderFooterTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridHeaderFooterTest.java
index 5b594a980d..681da7443f 100644
--- a/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridHeaderFooterTest.java
+++ b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridHeaderFooterTest.java
@@ -31,8 +31,18 @@ public class GridHeaderFooterTest extends GridBasicsTest {
protected static final String[] HEADER_TEXTS = IntStream
.range(0, GridBasics.COLUMN_CAPTIONS.length)
- .mapToObj(i -> "Header cell " + i)
- .toArray(String[]::new);
+ .mapToObj(i -> "Header cell " + i).toArray(String[]::new);
+
+ protected static final String[] FOOTER_TEXTS = IntStream
+ .range(0, GridBasics.COLUMN_CAPTIONS.length)
+ .mapToObj(i -> "Footer cell " + i).toArray(String[]::new);
+
+ @Override
+ public void setUp() {
+ super.setUp();
+
+ selectMenuPath("Component", "Footer", "Add default footer row");
+ }
@Test
public void initialState_defaultHeaderPresent() {
@@ -103,16 +113,40 @@ public class GridHeaderFooterTest extends GridBasicsTest {
assertNoSortIndicator(headerCell, "sort-desc");
}
+ @Test
+ public void initialState_defaultFooterPresent() {
+ assertEquals(1, getGridElement().getFooterCount());
+ assertFooterTexts(0, GridBasics.COLUMN_CAPTIONS);
+ }
+
+ @Test
+ public void appendFooterRow_addedToBottom() {
+ selectMenuPath("Component", "Footer", "Append footer row");
+
+ assertEquals(2, getGridElement().getFooterCount());
+ assertFooterTexts(0, GridBasics.COLUMN_CAPTIONS);
+ assertFooterTexts(1, FOOTER_TEXTS);
+ }
+
+ @Test
+ public void prependFooterRow_addedToTop() {
+ selectMenuPath("Component", "Footer", "Prepend footer row");
+
+ assertEquals(2, getGridElement().getFooterCount());
+ assertFooterTexts(0, FOOTER_TEXTS);
+ assertFooterTexts(1, GridBasics.COLUMN_CAPTIONS);
+ }
+
protected static void assertText(String expected, GridCellElement e) {
// TBE.getText returns "" if the element is scrolled out of view
- String actual = e.findElement(By.tagName("div")).getAttribute(
- "innerHTML");
+ String actual = e.findElement(By.tagName("div"))
+ .getAttribute("innerHTML");
assertEquals(expected, actual);
}
protected void assertHeaderTexts(int rowIndex, String[] texts) {
- List<GridCellElement> headerCells = getGridElement().getHeaderCells(
- rowIndex);
+ List<GridCellElement> headerCells = getGridElement()
+ .getHeaderCells(rowIndex);
assertEquals(texts.length, headerCells.size());
for (int i = 0; i < headerCells.size(); i++) {
@@ -120,9 +154,19 @@ public class GridHeaderFooterTest extends GridBasicsTest {
}
}
+ protected void assertFooterTexts(int rowIndex, String[] texts) {
+ List<GridCellElement> footerCells = getGridElement()
+ .getFooterCells(rowIndex);
+
+ assertEquals(texts.length, footerCells.size());
+ for (int i = 0; i < footerCells.size(); i++) {
+ assertText(texts[i], footerCells.get(i));
+ }
+ }
+
protected void assertSortIndicator(GridCellElement cell, String classname) {
- assertTrue("Header cell should have sort indicator " + classname, cell
- .getAttribute("class").contains(classname));
+ assertTrue("Header cell should have sort indicator " + classname,
+ cell.getAttribute("class").contains(classname));
}
protected void assertNoSortIndicator(GridCellElement cell,