]> source.dussan.org Git - vaadin-framework.git/commitdiff
Add StyleGenerators for Grid and Columns
authorTeemu Suo-Anttila <teemusa@vaadin.com>
Tue, 6 Sep 2016 12:07:37 +0000 (15:07 +0300)
committerArtur Signell <artur@vaadin.com>
Thu, 8 Sep 2016 11:33:14 +0000 (11:33 +0000)
Change-Id: I5eedce6ac24381d657357ff07ca1ccedd804158d

client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java
server/src/main/java/com/vaadin/server/data/DataCommunicator.java
server/src/main/java/com/vaadin/ui/Grid.java
uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java
uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicStyleGeneratorTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridContentTest.java

index ab0f6037f178ac45f4a93e916fb124de75ddfe33..880de07d555318eecc5d3d86b2bb8798b53cf00e 100644 (file)
@@ -76,6 +76,29 @@ public class GridConnector
         // Default selection style is space key.
         spaceSelectHandler = new SpaceSelectHandler<JsonObject>(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<?, JsonObject> 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();
     }
index e8c96fddabec03d67dd6e6f6539a42aeb19ba005..b0ade744def81407d833657bfd59158198f3468e 100644 (file)
@@ -357,7 +357,7 @@ public class DataCommunicator<T> extends AbstractExtension {
     /**
      * Informs the DataProvider that the collection has changed.
      */
-    protected void reset() {
+    public void reset() {
         if (reset) {
             return;
         }
index 94a3dd0ce94866e22576d02e1c712fb3879b4ca5..96d73768162c6036f2ed8f8d0db86895efec8c0d 100644 (file)
@@ -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<T> extends AbstractListing<T, SelectionModel<T>>
         implements HasComponents {
 
+    /**
+     * A callback interface for generating style names for an item.
+     *
+     * @param <T>
+     *            the grid bean type
+     */
+    @FunctionalInterface
+    public interface StyleGenerator<T>
+            extends Function<T, String>, Serializable {
+    }
+
     /**
      * A callback interface for generating details for a particular row in Grid.
      *
@@ -304,13 +313,13 @@ public class Grid<T> extends AbstractListing<T, SelectionModel<T>>
      * @param <V>
      *            the column value type
      */
-    public static class Column<T, V> extends AbstractExtension
-            implements DataGenerator<T> {
+    public static class Column<T, V> extends AbstractGridExtension<T> {
 
         private final Function<T, ? extends V> valueProvider;
 
         private Function<SortDirection, Stream<SortOrder<String>>> sortOrderProvider;
         private Comparator<T> comparator;
+        private StyleGenerator<T> styleGenerator;
 
         /**
          * Constructs a new Column configuration with given header caption,
@@ -401,22 +410,41 @@ public class Grid<T> extends AbstractListing<T, SelectionModel<T>>
             @SuppressWarnings("unchecked")
             Renderer<V> renderer = (Renderer<V>) 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<T> extends AbstractListing<T, SelectionModel<T>>
         public Stream<SortOrder<String>> 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 <code>null</code> to
+         *            remove a previously set generator
+         * @return this column
+         */
+        public Column<T, V> setStyleGenerator(
+                StyleGenerator<T> 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 <code>null</code> if no
+         *         generator is set
+         */
+        public StyleGenerator<T> getStyleGenerator() {
+            return styleGenerator;
+        }
     }
 
     private KeyMapper<Column<T, ?>> columnKeys = new KeyMapper<>();
@@ -579,6 +634,7 @@ public class Grid<T> extends AbstractListing<T, SelectionModel<T>>
     private List<SortOrder<Column<T, ?>>> sortOrder = new ArrayList<>();
     private DetailsManager<T> detailsManager;
     private Set<Component> extensionComponents = new HashSet<>();
+    private StyleGenerator<T> styleGenerator;
 
     /**
      * Constructor for the {@link Grid} component.
@@ -589,6 +645,14 @@ public class Grid<T> extends AbstractListing<T, SelectionModel<T>>
         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<T> extends AbstractListing<T, SelectionModel<T>>
         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 <code>null</code> to remove
+     *            a previously set generator
+     */
+    public void setStyleGenerator(StyleGenerator<T> styleGenerator) {
+        this.styleGenerator = styleGenerator;
+        getDataCommunicator().reset();
+    }
+
+    /**
+     * Gets the style generator that is used for generating styles for rows.
+     *
+     * @return the row style generator, or <code>null</code> if no generator is
+     *         set
+     */
+    public StyleGenerator<T> getStyleGenerator() {
+        return styleGenerator;
+    }
+
     @Override
     protected GridState getState() {
         return getState(true);
index a47d8b3e9e0c94ec86b61da0f9bbe9d881e505fe..5c9f03336e812e64797d3c1ead229c86f1f07e68 100644 (file)
@@ -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<DataObject> {
 
@@ -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<DataObject>) null,
+                sg -> grid.getColumns().forEach(c -> c.setStyleGenerator(sg)));
+        addGridMethodMenu(cellStyleMenu, CELL_STYLE_GENERATOR_EMPTY,
+                (StyleGenerator<DataObject>) 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<DataObject>) t -> null,
+                sg -> grid.getColumns().forEach(c -> c.setStyleGenerator(sg)));
     }
 
     private <T> 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 (file)
index 0000000..b9b93fa
--- /dev/null
@@ -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));
+    }
+}
index 88f2eb0b205dfa25120b8c2fc845bbbf68dbfad4..ba7187a5ec796adf6450e1ad0684f04bcdbaf2d6 100644 (file)
@@ -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"));
     }
 }