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: Ic85051b16ef77791f1fdae78fca47a0729e1c43dtags/7.4.0.beta1
@@ -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); | |||
@@ -1991,6 +1997,25 @@ public class Grid<T> extends Composite implements | |||
return null; | |||
} | |||
/** | |||
* 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> |
@@ -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(); | |||
} | |||
} |
@@ -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()); | |||
} | |||
} |
@@ -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(); | |||
} |
@@ -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) { |
@@ -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()); | |||
} | |||
} |
@@ -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")); | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
/** |