From: Teemu Suo-Anttila Date: Tue, 6 Sep 2016 12:07:37 +0000 (+0300) Subject: Add StyleGenerators for Grid and Columns X-Git-Tag: 8.0.0.alpha1~8 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=74df0fadeb7d6c2a6e15b4716602d6ab7ffb54b1;p=vaadin-framework.git Add StyleGenerators for Grid and Columns Change-Id: I5eedce6ac24381d657357ff07ca1ccedd804158d --- 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 ab0f6037f1..880de07d55 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 @@ -76,6 +76,29 @@ public class GridConnector // Default selection style is space key. spaceSelectHandler = new SpaceSelectHandler(getWidget()); getWidget().addSortHandler(this::handleSortEvent); + getWidget().setRowStyleGenerator(rowRef -> { + JsonObject json = rowRef.getRow(); + return json.hasKey(GridState.JSONKEY_ROWSTYLE) + ? json.getString(GridState.JSONKEY_ROWSTYLE) : null; + }); + getWidget().setCellStyleGenerator(cellRef -> { + JsonObject row = cellRef.getRow(); + if (!row.hasKey(GridState.JSONKEY_CELLSTYLES)) { + return null; + } + + Column column = cellRef.getColumn(); + if (columnToIdMap.containsKey(column)) { + String id = columnToIdMap.get(column); + JsonObject cellStyles = row + .getObject(GridState.JSONKEY_CELLSTYLES); + if (cellStyles.hasKey(id)) { + return cellStyles.getString(id); + } + } + + return null; + }); layout(); } diff --git a/server/src/main/java/com/vaadin/server/data/DataCommunicator.java b/server/src/main/java/com/vaadin/server/data/DataCommunicator.java index e8c96fddab..b0ade744de 100644 --- a/server/src/main/java/com/vaadin/server/data/DataCommunicator.java +++ b/server/src/main/java/com/vaadin/server/data/DataCommunicator.java @@ -357,7 +357,7 @@ public class DataCommunicator extends AbstractExtension { /** * Informs the DataProvider that the collection has changed. */ - protected void reset() { + public void reset() { if (reset) { return; } diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java index 94a3dd0ce9..96d7376816 100644 --- a/server/src/main/java/com/vaadin/ui/Grid.java +++ b/server/src/main/java/com/vaadin/ui/Grid.java @@ -32,9 +32,7 @@ import java.util.function.Function; import java.util.stream.Stream; import com.vaadin.data.selection.SingleSelection; -import com.vaadin.server.AbstractExtension; import com.vaadin.server.KeyMapper; -import com.vaadin.server.data.DataGenerator; import com.vaadin.server.data.SortOrder; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.data.DataCommunicatorConstants; @@ -64,6 +62,17 @@ import elemental.json.JsonValue; public class Grid extends AbstractListing> implements HasComponents { + /** + * A callback interface for generating style names for an item. + * + * @param + * the grid bean type + */ + @FunctionalInterface + public interface StyleGenerator + extends Function, Serializable { + } + /** * A callback interface for generating details for a particular row in Grid. * @@ -304,13 +313,13 @@ public class Grid extends AbstractListing> * @param * the column value type */ - public static class Column extends AbstractExtension - implements DataGenerator { + public static class Column extends AbstractGridExtension { private final Function valueProvider; private Function>> sortOrderProvider; private Comparator comparator; + private StyleGenerator styleGenerator; /** * Constructs a new Column configuration with given header caption, @@ -401,22 +410,41 @@ public class Grid extends AbstractListing> @SuppressWarnings("unchecked") Renderer renderer = (Renderer) state.renderer; - if (!jsonObject.hasKey(DataCommunicatorConstants.DATA)) { - jsonObject.put(DataCommunicatorConstants.DATA, - Json.createObject()); - } - JsonObject obj = jsonObject - .getObject(DataCommunicatorConstants.DATA); + JsonObject obj = getDataObject(jsonObject, + DataCommunicatorConstants.DATA); V providerValue = valueProvider.apply(data); JsonValue rendererValue = renderer.encode(providerValue); obj.put(communicationId, rendererValue); + + if (styleGenerator != null) { + String style = styleGenerator.apply(data); + if (style != null && !style.isEmpty()) { + JsonObject styleObj = getDataObject(jsonObject, + GridState.JSONKEY_CELLSTYLES); + styleObj.put(getState(false).id, style); + } + } } - @Override - public void destroyData(T data) { + /** + * Gets a data object with the given key from the given JsonObject. If + * there is no object with the key, this method creates a new + * JsonObject. + * + * @param jsonObject + * the json object + * @param key + * the key where the desired data object is stored + * @return data object for the given key + */ + private JsonObject getDataObject(JsonObject jsonObject, String key) { + if (!jsonObject.hasKey(key)) { + jsonObject.put(key, Json.createObject()); + } + return jsonObject.getObject(key); } @Override @@ -572,6 +600,33 @@ public class Grid extends AbstractListing> public Stream> getSortOrder(SortDirection direction) { return sortOrderProvider.apply(direction); } + + /** + * Sets the style generator that is used for generating styles for cells + * in this column. + * + * @param cellStyleGenerator + * the cell style generator to set, or null to + * remove a previously set generator + * @return this column + */ + public Column setStyleGenerator( + StyleGenerator cellStyleGenerator) { + this.styleGenerator = cellStyleGenerator; + getParent().getDataCommunicator().reset(); + return this; + } + + /** + * Gets the style generator that is used for generating styles for + * cells. + * + * @return the cell style generator, or null if no + * generator is set + */ + public StyleGenerator getStyleGenerator() { + return styleGenerator; + } } private KeyMapper> columnKeys = new KeyMapper<>(); @@ -579,6 +634,7 @@ public class Grid extends AbstractListing> private List>> sortOrder = new ArrayList<>(); private DetailsManager detailsManager; private Set extensionComponents = new HashSet<>(); + private StyleGenerator styleGenerator; /** * Constructor for the {@link Grid} component. @@ -589,6 +645,14 @@ public class Grid extends AbstractListing> detailsManager = new DetailsManager<>(); addExtension(detailsManager); addDataGenerator(detailsManager); + addDataGenerator((item, json) -> { + if (styleGenerator != null) { + String styleName = styleGenerator.apply(item); + if (styleName != null && !styleName.isEmpty()) { + json.put(GridState.JSONKEY_ROWSTYLE, styleName); + } + } + }); } /** @@ -836,6 +900,28 @@ public class Grid extends AbstractListing> return getState(false).heightMode; } + /** + * Sets the style generator that is used for generating styles for rows. + * + * @param styleGenerator + * the row style generator to set, or null to remove + * a previously set generator + */ + public void setStyleGenerator(StyleGenerator styleGenerator) { + this.styleGenerator = styleGenerator; + getDataCommunicator().reset(); + } + + /** + * Gets the style generator that is used for generating styles for rows. + * + * @return the row style generator, or null if no generator is + * set + */ + public StyleGenerator getStyleGenerator() { + return styleGenerator; + } + @Override protected GridState getState() { return getState(true); 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 a47d8b3e9e..5c9f03336e 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 @@ -18,6 +18,7 @@ import com.vaadin.ui.Component; import com.vaadin.ui.CssLayout; import com.vaadin.ui.Grid; import com.vaadin.ui.Grid.DetailsGenerator; +import com.vaadin.ui.Grid.StyleGenerator; import com.vaadin.ui.Label; import com.vaadin.ui.MenuBar; import com.vaadin.ui.MenuBar.MenuItem; @@ -32,6 +33,17 @@ import com.vaadin.ui.renderers.ProgressBarRenderer; @Widgetset("com.vaadin.DefaultWidgetSet") public class GridBasics extends AbstractTestUIWithLog { + 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 ROW_STYLE_GENERATOR_EMPTY = "Empty string"; + public static final String ROW_STYLE_GENERATOR_NULL = "Null"; + public static final String CELL_STYLE_GENERATOR_NONE = "None"; + public static final String CELL_STYLE_GENERATOR_PROPERTY_TO_STRING = "Property to string"; + public static final String CELL_STYLE_GENERATOR_SPECIAL = "Special for 1/4 Column 1"; + public static final String CELL_STYLE_GENERATOR_EMPTY = "Empty string"; + public static final String CELL_STYLE_GENERATOR_NULL = "Null"; + private static class DetailedDetailsGenerator implements DetailsGenerator { @@ -120,6 +132,8 @@ public class GridBasics extends AbstractTestUIWithLog { dataObj -> "(" + dataObj.getRowNumber() + ", 0)"); grid.addColumn("Column 1", dataObj -> "(" + dataObj.getRowNumber() + ", 1)"); + grid.addColumn("Column 2", + dataObj -> "(" + dataObj.getRowNumber() + ", 2)"); grid.addColumn("Row Number", DataObject::getRowNumber, new NumberRenderer()); @@ -172,6 +186,50 @@ public class GridBasics extends AbstractTestUIWithLog { addGridMethodMenu(frozenColMenu, "" + i, i, grid::setFrozenColumnCount); } + createRowStyleMenu(stateMenu.addItem("Row style generator", null)); + createCellStyleMenu(stateMenu.addItem("Cell style generator", null)); + } + + private void createRowStyleMenu(MenuItem rowStyleMenu) { + addGridMethodMenu(rowStyleMenu, ROW_STYLE_GENERATOR_NONE, null, + grid::setStyleGenerator); + addGridMethodMenu(rowStyleMenu, ROW_STYLE_GENERATOR_ROW_NUMBERS, + t -> "row" + t.getRowNumber(), grid::setStyleGenerator); + addGridMethodMenu(rowStyleMenu, + ROW_STYLE_GENERATOR_ROW_NUMBERS_FOR_3_OF_4, + t -> t.getRowNumber() % 4 != 0 ? "row" + t.getRowNumber() + : null, + grid::setStyleGenerator); + addGridMethodMenu(rowStyleMenu, ROW_STYLE_GENERATOR_EMPTY, t -> "", + grid::setStyleGenerator); + addGridMethodMenu(rowStyleMenu, ROW_STYLE_GENERATOR_NULL, t -> null, + grid::setStyleGenerator); + } + + private void createCellStyleMenu(MenuItem cellStyleMenu) { + addGridMethodMenu(cellStyleMenu, CELL_STYLE_GENERATOR_NONE, + (StyleGenerator) null, + sg -> grid.getColumns().forEach(c -> c.setStyleGenerator(sg))); + addGridMethodMenu(cellStyleMenu, CELL_STYLE_GENERATOR_EMPTY, + (StyleGenerator) t -> "", + sg -> grid.getColumns().forEach(c -> c.setStyleGenerator(sg))); + addGridMethodMenu(cellStyleMenu, + CELL_STYLE_GENERATOR_PROPERTY_TO_STRING, null, + sg -> grid.getColumns().forEach(c -> c.setStyleGenerator( + t -> c.getCaption().replaceAll(" ", "-")))); + addGridMethodMenu(cellStyleMenu, CELL_STYLE_GENERATOR_SPECIAL, null, + sg -> grid.getColumns().forEach(c -> c.setStyleGenerator(t -> { + if (t.getRowNumber() % 4 == 1) { + return null; + } else if (t.getRowNumber() % 4 == 3 + && c.getCaption().equals("Column 1")) { + return null; + } + return c.getCaption().replaceAll(" ", "_"); + }))); + addGridMethodMenu(cellStyleMenu, CELL_STYLE_GENERATOR_NULL, + (StyleGenerator) t -> null, + sg -> grid.getColumns().forEach(c -> c.setStyleGenerator(sg))); } private void addGridMethodMenu(MenuItem parent, String name, T value, diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicStyleGeneratorTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicStyleGeneratorTest.java new file mode 100644 index 0000000000..b9b93fa884 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicStyleGeneratorTest.java @@ -0,0 +1,155 @@ +/* + * 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.components.grid.basics; + +import static org.junit.Assert.assertFalse; + +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + +import com.vaadin.testbench.elements.GridElement.GridCellElement; +import com.vaadin.testbench.elements.GridElement.GridRowElement; +import com.vaadin.testbench.elements.NotificationElement; + +public class GridBasicStyleGeneratorTest extends GridBasicsTest { + @Test + public void testStyleNameGeneratorScrolling() throws Exception { + openTestURL(); + + selectRowStyleNameGenerator( + GridBasics.ROW_STYLE_GENERATOR_ROW_NUMBERS_FOR_3_OF_4); + selectCellStyleNameGenerator(GridBasics.CELL_STYLE_GENERATOR_SPECIAL); + + GridRowElement row = getGridElement().getRow(2); + GridCellElement cell = getGridElement().getCell(3, 2); + + Assert.assertTrue(hasCssClass(row, "row2")); + Assert.assertTrue(hasCssClass(cell, "Column_2")); + + // Scroll down and verify that the old elements don't have the + // stylename any more + + // Carefully chosen offset to hit an index % 4 without cell style + row = getGridElement().getRow(352); + cell = getGridElement().getCell(353, 2); + + Assert.assertFalse(hasCssClass(row, "row352")); + Assert.assertFalse(hasCssClass(cell, "Column_2")); + } + + @Test + public void testDisableStyleNameGenerator() throws Exception { + openTestURL(); + + selectRowStyleNameGenerator( + GridBasics.ROW_STYLE_GENERATOR_ROW_NUMBERS_FOR_3_OF_4); + selectCellStyleNameGenerator(GridBasics.CELL_STYLE_GENERATOR_SPECIAL); + + // Just verify that change was effective + GridRowElement row = getGridElement().getRow(2); + GridCellElement cell = getGridElement().getCell(3, 2); + + Assert.assertTrue(hasCssClass(row, "row2")); + Assert.assertTrue(hasCssClass(cell, "Column_2")); + + // Disable the generator and check again + selectRowStyleNameGenerator(GridBasics.ROW_STYLE_GENERATOR_NONE); + selectCellStyleNameGenerator(GridBasics.CELL_STYLE_GENERATOR_NONE); + + row = getGridElement().getRow(2); + cell = getGridElement().getCell(3, 2); + + Assert.assertFalse(hasCssClass(row, "row2")); + Assert.assertFalse(hasCssClass(cell, "Column_2")); + } + + @Test + public void testChangeStyleNameGenerator() throws Exception { + openTestURL(); + + selectRowStyleNameGenerator( + GridBasics.ROW_STYLE_GENERATOR_ROW_NUMBERS_FOR_3_OF_4); + selectCellStyleNameGenerator(GridBasics.CELL_STYLE_GENERATOR_SPECIAL); + + // Just verify that change was effective + GridRowElement row = getGridElement().getRow(2); + GridCellElement cell = getGridElement().getCell(3, 2); + + Assert.assertTrue(hasCssClass(row, "row2")); + Assert.assertTrue(hasCssClass(cell, "Column_2")); + + // Change the generator and check again + selectRowStyleNameGenerator(GridBasics.ROW_STYLE_GENERATOR_NONE); + selectCellStyleNameGenerator( + GridBasics.CELL_STYLE_GENERATOR_PROPERTY_TO_STRING); + + row = getGridElement().getRow(2); + cell = getGridElement().getCell(3, 2); + + // Old styles removed? + Assert.assertFalse(hasCssClass(row, "row2")); + Assert.assertFalse(hasCssClass(cell, "Column_2")); + + // New style present? + Assert.assertTrue(hasCssClass(cell, "Column-2")); + } + + @Test + @Ignore + public void testCellStyleGeneratorWithSelectionColumn() { + setDebug(true); + openTestURL(); + selectMenuPath("Component", "State", "Selection mode", "multi"); + + selectCellStyleNameGenerator(GridBasics.CELL_STYLE_GENERATOR_SPECIAL); + + assertFalse("Error notification was present", + isElementPresent(NotificationElement.class)); + } + + private void selectRowStyleNameGenerator(String name) { + selectMenuPath("Component", "State", "Row style generator", name); + } + + private void selectCellStyleNameGenerator(String name) { + selectMenuPath("Component", "State", "Cell style generator", name); + } + + @Test + public void testEmptyStringStyleGenerator() { + setDebug(true); + openTestURL(); + + selectCellStyleNameGenerator(GridBasics.CELL_STYLE_GENERATOR_EMPTY); + selectRowStyleNameGenerator(GridBasics.ROW_STYLE_GENERATOR_EMPTY); + + assertFalse("Error notification was present", + isElementPresent(NotificationElement.class)); + } + + @Test + public void testNullStringStyleGenerator() { + setDebug(true); + openTestURL(); + + selectCellStyleNameGenerator(GridBasics.CELL_STYLE_GENERATOR_NULL); + selectRowStyleNameGenerator(GridBasics.ROW_STYLE_GENERATOR_NULL); + + assertFalse("Error notification was present", + isElementPresent(NotificationElement.class)); + } +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridContentTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridContentTest.java index 88f2eb0b20..ba7187a5ec 100644 --- a/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridContentTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridContentTest.java @@ -10,8 +10,8 @@ public class GridContentTest extends GridBasicsTest { DataObject first = getTestData().findFirst().orElse(null); Assert.assertEquals("Text content should match row number", first.getRowNumber().toString(), - getGridElement().getCell(0, 4).getText()); + getGridElement().getCell(0, 5).getText()); Assert.assertEquals("HTML content did not match", first.getHtmlString(), - getGridElement().getCell(0, 4).getAttribute("innerHTML")); + getGridElement().getCell(0, 5).getAttribute("innerHTML")); } }