layout();
}
+ /**
+ * Updates the grid header section on state change.
+ */
@OnStateChange("header")
void updateHeader() {
- final SectionState state = getState().header;
final Grid<JsonObject> grid = getWidget();
+ final SectionState state = getState().header;
while (grid.getHeaderRowCount() > 0) {
grid.removeHeaderRow(0);
for (RowState rowState : state.rows) {
HeaderRow row = grid.appendHeaderRow();
+
rowState.cells.forEach((columnId, cellState) -> {
row.getCell(getColumn(columnId)).setText(cellState.text);
});
- }
- if (grid.getHeaderRowCount() > 0) {
- // TODO Default header handling to be added in a later patch
- grid.setDefaultHeaderRow(grid.getHeaderRow(0));
+ if (rowState.defaultHeader) {
+ grid.setDefaultHeaderRow(row);
+ }
}
}
import com.vaadin.shared.ui.grid.HeightMode;
import com.vaadin.shared.ui.grid.SectionState;
import com.vaadin.ui.components.grid.Header;
+import com.vaadin.ui.components.grid.Header.Row;
import com.vaadin.ui.renderers.AbstractRenderer;
import com.vaadin.ui.renderers.Renderer;
import com.vaadin.ui.renderers.TextRenderer;
* Sets the header caption for this column.
*
* @param caption
- * the header caption
+ * the header caption, not null
*
* @return this column
*/
public Column<T, V> setCaption(String caption) {
Objects.requireNonNull(caption, "Header caption can't be null");
getState().caption = caption;
+
+ HeaderRow row = getParent().getDefaultHeaderRow();
+ if (row != null) {
+ row.getCell(getId()).setText(caption);
+ }
+
return this;
}
* Returns the cell on this row corresponding to the given column id.
*
* @param columnId
- * the id of the column whose header cell to get
+ * the id of the column whose header cell to get, not null
* @return the header cell
+ * @throws IllegalArgumentException
+ * if there is no such column in the grid
*/
public HeaderCell getCell(String columnId);
* Returns the cell on this row corresponding to the given column.
*
* @param column
- * the column whose header cell to get
+ * the column whose header cell to get, not null
* @return the header cell
+ * @throws IllegalArgumentException
+ * if there is no such column in the grid
*/
public default HeaderCell getCell(Column<?, ?> column) {
return getCell(column.getId());
* Sets the textual caption of this cell.
*
* @param text
- * the header caption to set
+ * the header caption to set, not null
*/
public void setText(String text);
}
+ private class HeaderImpl extends Header {
+
+ @Override
+ protected SectionState getState(boolean markAsDirty) {
+ return Grid.this.getState(markAsDirty).header;
+ }
+
+ @Override
+ protected Collection<Column<T, ?>> getColumns() {
+ return Grid.this.getColumns();
+ }
+ };
+
private KeyMapper<Column<T, ?>> columnKeys = new KeyMapper<>();
private Set<Column<T, ?>> columnSet = new LinkedHashSet<>();
private List<SortOrder<Column<T, ?>>> sortOrder = new ArrayList<>();
private StyleGenerator<T> styleGenerator = item -> null;
private DescriptionGenerator<T> descriptionGenerator;
- private Header header = new Header() {
- @Override
- protected SectionState getState(boolean markAsDirty) {
- return Grid.this.getState(markAsDirty).header;
- }
- };
+ private Header header = new HeaderImpl();
/**
* Constructor for the {@link Grid} component.
setSelectionModel(new SingleSelection());
registerRpc(new GridServerRpcImpl());
- appendHeaderRow();
+ setDefaultHeaderRow(appendHeaderRow());
detailsManager = new DetailsManager<>();
addExtension(detailsManager);
getHeader().addColumn(columnId);
- if (getHeaderRowCount() > 0) {
- // TODO Default header API to be added in a later patch
- HeaderRow defaultHeader = getHeaderRow(0);
- defaultHeader.getCell(columnId).setText(caption);
+ if (getDefaultHeaderRow() != null) {
+ getDefaultHeaderRow().getCell(columnId).setText(caption);
}
return column;
if (columnSet.remove(column)) {
columnKeys.remove(column);
removeDataGenerator(column);
+ getHeader().removeColumn(column.getId());
column.remove();
+
}
}
/**
* Returns the header row at the given index.
*
- * @param rowIndex
+ * @param index
* the index of the row, where the topmost row has index zero
* @return the header row at the index
* @throws IndexOutOfBoundsException
* if {@code rowIndex < 0 || rowIndex >= getHeaderRowCount()}
*/
- public HeaderRow getHeaderRow(int rowIndex) {
- return getHeader().getRow(rowIndex);
+ public HeaderRow getHeaderRow(int index) {
+ return getHeader().getRow(index);
}
/**
}
/**
- * Removes the given row from the header section.
+ * Removes the given row from the header section. Removing a default row
+ * sets the Grid to have no default row.
*
* @param row
* the header row to be removed, not null
/**
* Removes the row at the given position from the header section.
*
- * @param rowIndex
+ * @param index
* the index of the row to remove, where the topmost row has
* index zero
*
* @throws IndexOutOfBoundsException
- * if {@code rowIndex < 0 || rowIndex >= getHeaderRowCount()}
+ * if {@code index < 0 || index >= getHeaderRowCount()}
*
* @see #removeHeaderRow(HeaderRow)
* @see #addHeaderRowAt(int)
* @see #appendHeaderRow()
* @see #prependHeaderRow()
*/
- public void removeHeaderRow(int rowIndex) {
- getHeader().removeRow(rowIndex);
+ public void removeHeaderRow(int index) {
+ getHeader().removeRow(index);
+ }
+
+ /**
+ * 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.getDefaultRow();
+ }
+
+ /**
+ * Sets the default row of the header. The default row is a special header
+ * row that displays column captions and sort indicators. By default Grid
+ * has a single row which is also the default row. When a header row is set
+ * as the default row, any existing cell content is replaced by the column
+ * captions.
+ *
+ * @param row
+ * the new default row, or null for no default row
+ *
+ * @throws IllegalArgumentException
+ * if the header does not contain the row
+ */
+ public void setDefaultHeaderRow(HeaderRow row) {
+ header.setDefaultRow((Row) row);
}
/**
* Returns the header section of this grid. The default header contains a
- * single row displaying the column captions.
+ * single row, set as the {@linkplain #setDefaultHeaderRow(HeaderRow)
+ * default row}.
*
* @return the header section
*/
}
/**
- * Registers a new column visibility change listener
+ * Registers a new column visibility change listener.
*
* @param listener
* the listener to register, not null
/**
* Represents the header section of a Grid.
+ *
+ * @author Vaadin Ltd.
+ *
+ * @since 8.0
*/
public abstract class Header extends StaticSection<Header.Row> {
+ /**
+ * A row in a Grid header.
+ */
public class Row extends StaticSection.StaticRow<Row.Cell>
implements Grid.HeaderRow {
+ /**
+ * A cell in a Grid header row.
+ */
public class Cell extends StaticSection.StaticCell implements
Grid.HeaderCell {
+ /**
+ * Creates a new header cell.
+ */
protected Cell() {
super(Row.this);
}
}
/**
- * @param section
+ * Creates a new header row.
*/
protected Row() {
super(Header.this);
protected String getCellTagName() {
return "th";
}
+
+ /**
+ * Returns whether this row is the default header row.
+ *
+ * @return {@code true} if this row is the default row, {@code false}
+ * otherwise.
+ */
+ protected boolean isDefault() {
+ return getRowState().defaultHeader;
+ }
+
+ /**
+ * Sets whether this row is the default header row.
+ *
+ * @param defaultHeader
+ * {@code true} to set to default, {@code false} otherwise.
+ */
+ protected void setDefault(boolean defaultHeader) {
+ getRowState().defaultHeader = defaultHeader;
+ }
}
@Override
public Row createRow() {
return new Row();
}
+
+ @Override
+ public void removeRow(int index) {
+ if (getRow(index).isDefault()) {
+ setDefaultRow(null);
+ }
+ super.removeRow(index);
+ }
+
+ /**
+ * Returns the default row of this header. The default row displays column
+ * captions and sort indicators.
+ *
+ * @return the default row, or {@code null} if there is no default row
+ */
+ public Row getDefaultRow() {
+ return getRows().stream()
+ .filter(Row::isDefault)
+ .findAny().orElse(null);
+ }
+
+ /**
+ * Sets the default row of this header. The default row displays column
+ * captions and sort indicators.
+ *
+ * @param defaultRow
+ * the new default row, or null for no default row
+ *
+ * @throws IllegalArgumentException
+ * if the header does not contain the row
+ */
+ public void setDefaultRow(Row defaultRow) {
+ if (defaultRow != null) {
+ if (!getRows().contains(defaultRow)) {
+ throw new IllegalArgumentException(
+ "The section does not contain the row");
+ }
+ if (defaultRow.isDefault()) {
+ return;
+ }
+ }
+ getRows().forEach(row -> row.setDefault(row == defaultRow));
+
+ markAsDirty();
+ }
}
import java.io.Serializable;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.vaadin.shared.ui.grid.SectionState;
import com.vaadin.shared.ui.grid.SectionState.CellState;
import com.vaadin.shared.ui.grid.SectionState.RowState;
+import com.vaadin.ui.Grid.Column;
/**
* Represents the header or footer section of a Grid.
public abstract static class StaticRow<CELL extends StaticCell>
implements Serializable {
- private RowState rowState = new RowState();
- private StaticSection<?> section;
- private Map<Object, CELL> cells = new LinkedHashMap<>();
+ private final RowState rowState = new RowState();
+ private final StaticSection<?> section;
+ private final Map<Object, CELL> cells = new LinkedHashMap<>();
/**
* Creates a new row belonging to the given section.
*
* @param columnId
* the id of the column
- * @return the cell for the given column or null if not found
+ * @return the cell for the given column
+ *
+ * @throws IllegalArgumentException
+ * if no cell was found for the column id
*/
public CELL getCell(String columnId) {
CELL cell = cells.get(columnId);
+ if (cell == null) {
+ throw new IllegalArgumentException(
+ "No cell found for column id " + columnId);
+ }
return cell;
}
}
}
}
- private List<ROW> rows = new ArrayList<>();
+ private final List<ROW> rows = new ArrayList<>();
/**
* Creates a new row instance.
*/
protected abstract SectionState getState(boolean markAsDirty);
+ protected abstract Collection<? extends Column<?, ?>> getColumns();
+
/**
* Marks the state of this section as modified.
*/
ROW row = createRow();
rows.add(index, row);
getState(true).rows.add(index, row.getRowState());
+
+ getColumns().stream().forEach(column -> row.addCell(column.getId()));
+
return row;
}
row.removeCell(columnId);
}
}
+
+ /**
+ * Returns an unmodifiable list of the rows in this section.
+ *
+ * @return the rows in this section
+ */
+ protected List<ROW> getRows() {
+ return Collections.unmodifiableList(rows);
+ }
}
--- /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.server.component.grid;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import java.util.function.Function;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.vaadin.ui.Grid;
+import com.vaadin.ui.Grid.Column;
+import com.vaadin.ui.Grid.HeaderRow;
+
+public class GridDefaultHeaderTest {
+ private Grid<String> grid;
+ private Column<?, ?> column1, column2;
+
+ @Before
+ public void setUp() {
+ grid = new Grid<>();
+
+ column1 = grid.addColumn("First", Function.identity());
+ column2 = grid.addColumn("Second", Function.identity());
+ }
+
+ @Test
+ public void initialState_hasDefaultHeader() {
+ HeaderRow defaultHeader = grid.getDefaultHeaderRow();
+
+ assertEquals(1, grid.getHeaderRowCount());
+ assertSame(grid.getHeaderRow(0), defaultHeader);
+ assertEquals("First", defaultHeader.getCell(column1).getText());
+ assertEquals("Second", defaultHeader.getCell(column2).getText());
+ }
+
+ @Test
+ public void initialState_defaultHeaderRemovable() {
+ grid.removeHeaderRow(0);
+
+ assertEquals(0, grid.getHeaderRowCount());
+ assertNull(grid.getDefaultHeaderRow());
+ }
+
+ @Test
+ public void initialState_updateColumnCaption_defaultHeaderUpdated() {
+ column1.setCaption("1st");
+
+ assertEquals("1st", grid.getDefaultHeaderRow().getCell(column1)
+ .getText());
+ }
+
+ @Test
+ public void customDefaultHeader_updateColumnCaption_defaultHeaderUpdated() {
+ grid.setDefaultHeaderRow(grid.appendHeaderRow());
+ column1.setCaption("1st");
+
+ assertEquals("1st", grid.getDefaultHeaderRow().getCell(column1)
+ .getText());
+ assertEquals("First", grid.getHeaderRow(0).getCell(column1).getText());
+ }
+
+ @Test
+ public void noDefaultRow_updateColumnCaption_headerNotUpdated() {
+ grid.setDefaultHeaderRow(null);
+ column1.setCaption("1st");
+
+ assertEquals("First", grid.getHeaderRow(0).getCell(column1)
+ .getText());
+ }
+}
package com.vaadin.tests.server.component.grid;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;
+import java.util.function.Function;
+
import org.junit.Before;
import org.junit.Test;
public class GridHeaderFooterTest {
private Grid<String> grid;
- private Column<?, ?> column1, column2;
@Before
public void setUp() {
grid = new Grid<>();
-
- column1 = grid.addColumn("First", s -> s.substring(0, 1));
- column2 = grid.addColumn("Rest", s -> s.substring(1));
- }
-
- @Test
- public void initialState_hasDefaultHeader() {
- assertEquals(1, grid.getHeaderRowCount());
- HeaderRow defaultHeader = grid.getHeaderRow(0);
- assertEquals("First", defaultHeader.getCell(column1).getText());
- assertEquals("Rest", defaultHeader.getCell(column2).getText());
- }
-
- @Test
- public void initialState_defaultHeaderRemovable() {
- grid.removeHeaderRow(0);
- assertEquals(0, grid.getHeaderRowCount());
}
@Test
}
grid.removeHeaderRow(row);
}
+
+ @Test
+ public void addColumn_headerCellAdded() {
+ Column<?, ?> column = grid.addColumn("Col", Function.identity());
+
+ assertNotNull(grid.getHeaderRow(0).getCell(column));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void removeColumn_headerCellRemoved() {
+ Column<String, ?> column = grid.addColumn("Col", Function.identity());
+ grid.removeColumn(column);
+
+ grid.getHeaderRow(0).getCell(column);
+ }
}
import com.vaadin.ui.Button;
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.HeaderRow;
import com.vaadin.ui.Label;
import com.vaadin.ui.MenuBar;
import com.vaadin.ui.MenuBar.Command;
@Widgetset("com.vaadin.DefaultWidgetSet")
public class GridBasics extends AbstractTestUIWithLog {
- public static final String[] COLUMN_CAPTIONS = { "Column 0", "Column 1", "Column 2", "Row Number", "Date", "HTML String", "Big Random", "Small Random" };
-
public static final String ROW_STYLE_GENERATOR_ROW_NUMBERS_FOR_3_OF_4 = "Row numbers for 3/4";
public static final String ROW_STYLE_GENERATOR_NONE = "None";
public static final String ROW_STYLE_GENERATOR_ROW_NUMBERS = "Row numbers";
public static final String CELL_STYLE_GENERATOR_EMPTY = "Empty string";
public static final String CELL_STYLE_GENERATOR_NULL = "Null";
+ public static final String[] COLUMN_CAPTIONS = { "Column 0", "Column 1",
+ "Column 2", "Row Number", "Date", "HTML String", "Big Random",
+ "Small Random" };
+
private static class DetailedDetailsGenerator
implements DetailsGenerator<DataObject> {
grid.addColumn(COLUMN_CAPTIONS[3], DataObject::getRowNumber,
new NumberRenderer());
- grid.addColumn(COLUMN_CAPTIONS[4], DataObject::getDate, new DateRenderer());
+ grid.addColumn(COLUMN_CAPTIONS[4], DataObject::getDate,
+ new DateRenderer());
grid.addColumn(COLUMN_CAPTIONS[5], DataObject::getHtmlString,
new HtmlRenderer());
grid.addColumn(COLUMN_CAPTIONS[6], DataObject::getBigRandom,
private void createHeaderMenu(MenuItem headerMenu) {
headerMenu.addItem("Append header row", menuItem -> {
- grid.appendHeaderRow();
+ HeaderRow row = grid.appendHeaderRow();
+
+ int i = 0;
+ for (Column<?, ?> column : grid.getColumns()) {
+ row.getCell(column).setText("Header cell " + i++);
+ }
});
headerMenu.addItem("Prepend header row", menuItem -> {
- grid.prependHeaderRow();
+ HeaderRow row = grid.prependHeaderRow();
+
+ int i = 0;
+ for (Column<?, ?> column : grid.getColumns()) {
+ row.getCell(column).setText("Header cell " + i++);
+ }
});
headerMenu.addItem("Remove first header row", menuItem -> {
grid.removeHeaderRow(0);
});
+ headerMenu.addItem("Set first row as default", menuItem -> {
+ grid.setDefaultHeaderRow(grid.getHeaderRow(0));
+ });
+ headerMenu.addItem("Set no default row", menuItem -> {
+ grid.setDefaultHeaderRow(null);
+ });
}
/* DetailsGenerator related things */
package com.vaadin.tests.components.grid.basics;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import java.util.List;
+import java.util.stream.IntStream;
import org.junit.Test;
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);
+
@Test
public void initialState_defaultHeaderPresent() {
assertEquals(1, getGridElement().getHeaderCount());
-
- final String[] captions = GridBasics.COLUMN_CAPTIONS;
- List<GridCellElement> headerCells = getGridElement().getHeaderCells(0);
-
- assertEquals(captions.length, headerCells.size());
- for (int i = 0; i < headerCells.size(); i++) {
- assertText(captions[i], headerCells.get(i));
- }
+ assertHeaderTexts(0, GridBasics.COLUMN_CAPTIONS);
}
@Test
selectMenuPath("Component", "Header", "Append header row");
assertEquals(2, getGridElement().getHeaderCount());
+ assertHeaderTexts(0, GridBasics.COLUMN_CAPTIONS);
+ assertHeaderTexts(1, HEADER_TEXTS);
}
@Test
- public void prependHeaderRow_addedToBottom() {
+ public void prependHeaderRow_addedToTop() {
selectMenuPath("Component", "Header", "Prepend header row");
assertEquals(2, getGridElement().getHeaderCount());
+ assertHeaderTexts(0, HEADER_TEXTS);
+ assertHeaderTexts(1, GridBasics.COLUMN_CAPTIONS);
}
@Test
assertEquals(0, getGridElement().getHeaderCount());
}
+ @Test
+ public void setDefaultRow_headerCaptionsUpdated() {
+ selectMenuPath("Component", "Header", "Prepend header row");
+ selectMenuPath("Component", "Header", "Set first row as default");
+
+ assertHeaderTexts(0, GridBasics.COLUMN_CAPTIONS);
+ }
+
+ @Test
+ public void clickDefaultHeaderCell_sortIndicatorPresent() {
+ GridCellElement headerCell = getGridElement().getHeaderCell(0, 2);
+ headerCell.click();
+
+ assertSortIndicator(headerCell, "sort-asc");
+
+ headerCell.click();
+ assertNoSortIndicator(headerCell, "sort-asc");
+ assertSortIndicator(headerCell, "sort-desc");
+
+ GridCellElement anotherCell = getGridElement().getHeaderCell(0, 3);
+ anotherCell.click();
+
+ assertNoSortIndicator(headerCell, "sort-asc");
+ assertNoSortIndicator(headerCell, "sort-desc");
+ assertSortIndicator(anotherCell, "sort-asc");
+ }
+
+ @Test
+ public void noDefaultRow_clickHeaderCell_sortIndicatorsNotPresent() {
+ selectMenuPath("Component", "Header", "Set no default row");
+
+ GridCellElement headerCell = getGridElement().getHeaderCell(0, 2);
+ headerCell.click();
+
+ assertNoSortIndicator(headerCell, "sort-asc");
+ assertNoSortIndicator(headerCell, "sort-desc");
+ }
+
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");
assertEquals(expected, actual);
}
+
+ protected void assertHeaderTexts(int rowIndex, String[] texts) {
+ List<GridCellElement> headerCells = getGridElement().getHeaderCells(
+ rowIndex);
+
+ assertEquals(texts.length, headerCells.size());
+ for (int i = 0; i < headerCells.size(); i++) {
+ assertText(texts[i], headerCells.get(i));
+ }
+ }
+
+ protected void assertSortIndicator(GridCellElement cell, String classname) {
+ assertTrue("Header cell should have sort indicator " + classname, cell
+ .getAttribute("class").contains(classname));
+ }
+
+ protected void assertNoSortIndicator(GridCellElement cell,
+ String classname) {
+ assertFalse("Header cell should not have sort indicator " + classname,
+ cell.getAttribute("class").contains(classname));
+ }
}