From da8394e5adeda85047a5e167bdd0904dc3387a87 Mon Sep 17 00:00:00 2001 From: Teemu Suo-Anttila Date: Tue, 30 Aug 2016 13:01:46 +0300 Subject: [PATCH] Reintroduce frozen columns and height by rows to Grid Change-Id: I5fecfabd023b39dc252e47a6aa403a79034b0f3d --- .../client/connectors/grid/GridConnector.java | 6 + server/src/main/java/com/vaadin/ui/Grid.java | 148 ++++++++++++++++++ .../component}/grid/GridDetailsTest.java | 2 +- .../tests/server/component/grid/GridTest.java | 53 +++++++ .../components/grid/basics/GridBasics.java | 38 +++++ .../grid/basics/GridBasicStructureTest.java | 87 ++++++++++ 6 files changed, 333 insertions(+), 1 deletion(-) rename server/src/test/java/com/vaadin/tests/{components => server/component}/grid/GridDetailsTest.java (98%) create mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/GridTest.java create mode 100644 uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicStructureTest.java 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 3f22f419e7..9f2b8971c1 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 @@ -38,6 +38,7 @@ import com.vaadin.client.widgets.Grid.Column; import com.vaadin.shared.data.sort.SortDirection; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.grid.GridServerRpc; +import com.vaadin.shared.ui.grid.GridState; import elemental.json.JsonObject; @@ -171,4 +172,9 @@ public class GridConnector extends AbstractListingConnector return ensureHandlerManager() .addHandler(ConnectorHierarchyChangeEvent.TYPE, handler); } + + @Override + public GridState getState() { + return (GridState) super.getState(); + } } diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java index c236a9c5f6..ae7c550172 100644 --- a/server/src/main/java/com/vaadin/ui/Grid.java +++ b/server/src/main/java/com/vaadin/ui/Grid.java @@ -45,6 +45,7 @@ import com.vaadin.shared.ui.grid.ColumnState; import com.vaadin.shared.ui.grid.GridConstants.Section; import com.vaadin.shared.ui.grid.GridServerRpc; import com.vaadin.shared.ui.grid.GridState; +import com.vaadin.shared.ui.grid.HeightMode; import elemental.json.Json; import elemental.json.JsonObject; @@ -628,6 +629,153 @@ public class Grid extends AbstractListing> return Collections.unmodifiableSet(extensionComponents).iterator(); } + /** + * Sets the number of frozen columns in this grid. Setting the count to 0 + * means that no data columns will be frozen, but the built-in selection + * checkbox column will still be frozen if it's in use. Setting the count to + * -1 will also disable the selection column. + *

+ * The default value is 0. + * + * @param numberOfColumns + * the number of columns that should be frozen + * + * @throws IllegalArgumentException + * if the column count is less than -1 or greater than the + * number of visible columns + */ + public void setFrozenColumnCount(int numberOfColumns) { + if (numberOfColumns < -1 || numberOfColumns > columnSet.size()) { + throw new IllegalArgumentException( + "count must be between -1 and the current number of columns (" + + columnSet.size() + "): " + numberOfColumns); + } + + getState().frozenColumnCount = numberOfColumns; + } + + /** + * Gets the number of frozen columns in this grid. 0 means that no data + * columns will be frozen, but the built-in selection checkbox column will + * still be frozen if it's in use. -1 means that not even the selection + * column is frozen. + *

+ * NOTE: this count includes {@link Column#isHidden() hidden + * columns} in the count. + * + * @see #setFrozenColumnCount(int) + * + * @return the number of frozen columns + */ + public int getFrozenColumnCount() { + return getState(false).frozenColumnCount; + } + + /** + * Sets the number of rows that should be visible in Grid's body. This + * method will set the height mode to be {@link HeightMode#ROW}. + * + * @param rows + * The height in terms of number of rows displayed in Grid's + * body. If Grid doesn't contain enough rows, white space is + * displayed instead. If null is given, then Grid's + * height is undefined + * @throws IllegalArgumentException + * if {@code rows} is zero or less + * @throws IllegalArgumentException + * if {@code rows} is {@link Double#isInfinite(double) infinite} + * @throws IllegalArgumentException + * if {@code rows} is {@link Double#isNaN(double) NaN} + */ + public void setHeightByRows(double rows) { + if (rows <= 0.0d) { + throw new IllegalArgumentException( + "More than zero rows must be shown."); + } else if (Double.isInfinite(rows)) { + throw new IllegalArgumentException( + "Grid doesn't support infinite heights"); + } else if (Double.isNaN(rows)) { + throw new IllegalArgumentException("NaN is not a valid row count"); + } + getState().heightMode = HeightMode.ROW; + getState().heightByRows = rows; + } + + /** + * Gets the amount of rows in Grid's body that are shown, while + * {@link #getHeightMode()} is {@link HeightMode#ROW}. + * + * @return the amount of rows that are being shown in Grid's body + * @see #setHeightByRows(double) + */ + public double getHeightByRows() { + return getState(false).heightByRows; + } + + /** + * {@inheritDoc} + *

+ * Note: This method will set the height mode to be + * {@link HeightMode#CSS}. + * + * @see #setHeightMode(HeightMode) + */ + @Override + public void setHeight(float height, Unit unit) { + getState().heightMode = HeightMode.CSS; + super.setHeight(height, unit); + } + + /** + * Defines the mode in which the Grid widget's height is calculated. + *

+ * If {@link HeightMode#CSS} is given, Grid will respect the values given + * via a {@code setHeight}-method, and behave as a traditional Component. + *

+ * If {@link HeightMode#ROW} is given, Grid will make sure that the body + * will display as many rows as {@link #getHeightByRows()} defines. + * Note: If headers/footers are inserted or removed, the widget + * will resize itself to still display the required amount of rows in its + * body. It also takes the horizontal scrollbar into account. + * + * @param heightMode + * the mode in to which Grid should be set + */ + public void setHeightMode(HeightMode heightMode) { + /* + * This method is a workaround for the fact that Vaadin re-applies + * widget dimensions (height/width) on each state change event. The + * original design was to have setHeight and setHeightByRow be equals, + * and whichever was called the latest was considered in effect. + * + * But, because of Vaadin always calling setHeight on the widget, this + * approach doesn't work. + */ + + getState().heightMode = heightMode; + } + + /** + * Returns the current {@link HeightMode} the Grid is in. + *

+ * Defaults to {@link HeightMode#CSS}. + * + * @return the current HeightMode + */ + public HeightMode getHeightMode() { + return getState(false).heightMode; + } + + @Override + protected GridState getState() { + return getState(true); + } + + @Override + protected GridState getState(boolean markAsDirty) { + return (GridState) super.getState(markAsDirty); + } + private void addExtensionComponent(Component c) { if (extensionComponents.add(c)) { c.setParent(this); diff --git a/server/src/test/java/com/vaadin/tests/components/grid/GridDetailsTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/GridDetailsTest.java similarity index 98% rename from server/src/test/java/com/vaadin/tests/components/grid/GridDetailsTest.java rename to server/src/test/java/com/vaadin/tests/server/component/grid/GridDetailsTest.java index 7590efccbb..c7f1fbd778 100644 --- a/server/src/test/java/com/vaadin/tests/components/grid/GridDetailsTest.java +++ b/server/src/test/java/com/vaadin/tests/server/component/grid/GridDetailsTest.java @@ -1,4 +1,4 @@ -package com.vaadin.tests.components.grid; +package com.vaadin.tests.server.component.grid; import java.util.ArrayList; import java.util.Arrays; diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/GridTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/GridTest.java new file mode 100644 index 0000000000..c426ffe4f6 --- /dev/null +++ b/server/src/test/java/com/vaadin/tests/server/component/grid/GridTest.java @@ -0,0 +1,53 @@ +package com.vaadin.tests.server.component.grid; + +import static org.junit.Assert.assertEquals; + +import java.util.function.Function; + +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.shared.ui.grid.HeightMode; +import com.vaadin.ui.Grid; + +public class GridTest { + + private Grid grid; + + @Before + public void setUp() { + grid = new Grid<>(); + grid.addColumn("foo", String.class, Function.identity()); + } + + @Test + public void testGridHeightModeChange() { + assertEquals("Initial height mode was not CSS", HeightMode.CSS, + grid.getHeightMode()); + grid.setHeightByRows(13.24); + assertEquals("Setting height by rows did not change height mode", + HeightMode.ROW, grid.getHeightMode()); + grid.setHeight("100px"); + assertEquals("Setting height did not change height mode.", + HeightMode.CSS, grid.getHeightMode()); + } + + @Test(expected = IllegalArgumentException.class) + public void testFrozenColumnCountTooBig() { + grid.setFrozenColumnCount(2); + } + + @Test(expected = IllegalArgumentException.class) + public void testFrozenColumnCountTooSmall() { + grid.setFrozenColumnCount(-2); + } + + @Test() + public void testSetFrozenColumnCount() { + for (int i = -1; i < 2; ++i) { + grid.setFrozenColumnCount(i); + assertEquals("Frozen column count not updated", i, + grid.getFrozenColumnCount()); + } + } +} 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 fa032dac48..3445a3e3f3 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 @@ -1,12 +1,17 @@ package com.vaadin.tests.components.grid.basics; +import java.text.DecimalFormat; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Stream; +import com.vaadin.annotations.Widgetset; import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.tests.components.AbstractTestUIWithLog; import com.vaadin.ui.Button; import com.vaadin.ui.Component; @@ -20,6 +25,7 @@ import com.vaadin.ui.Notification; import com.vaadin.ui.Panel; import com.vaadin.ui.VerticalLayout; +@Widgetset("com.vaadin.DefaultWidgetSet") public class GridBasics extends AbstractTestUIWithLog { private static class DetailedDetailsGenerator @@ -121,10 +127,42 @@ public class GridBasics extends AbstractTestUIWithLog { private Component createMenu() { MenuBar menu = new MenuBar(); MenuItem componentMenu = menu.addItem("Component", null); + createStateMenu(componentMenu.addItem("State", null)); + createSizeMenu(componentMenu.addItem("Size", null)); createDetailsMenu(componentMenu.addItem("Details", null)); return menu; } + private void createSizeMenu(MenuItem sizeMenu) { + MenuItem heightByRows = sizeMenu.addItem("Height by Rows", null); + DecimalFormat df = new DecimalFormat("0.00"); + Stream.of(0.33, 0.67, 1.00, 1.33, 1.67, 2.00, 2.33, 2.67, 3.00, 3.33, + 3.67, 4.00, 4.33, 4.67) + .forEach(d -> addGridMethodMenu(heightByRows, + df.format(d) + " rows", d, grid::setHeightByRows)); + sizeMenu.addItem("HeightMode Row", item -> { + grid.setHeightMode( + item.isChecked() ? HeightMode.ROW : HeightMode.CSS); + }).setCheckable(true); + + MenuItem heightMenu = sizeMenu.addItem("Height", null); + Stream.of(50, 100, 200, 400).map(i -> i + "px").forEach( + i -> addGridMethodMenu(heightMenu, i, i, grid::setHeight)); + } + + private void createStateMenu(MenuItem stateMenu) { + MenuItem frozenColMenu = stateMenu.addItem("Frozen column count", null); + for (int i = -1; i < 3; ++i) { + addGridMethodMenu(frozenColMenu, "" + i, i, + grid::setFrozenColumnCount); + } + } + + private void addGridMethodMenu(MenuItem parent, String name, T value, + Consumer method) { + parent.addItem(name, menuItem -> method.accept(value)); + } + /* DetailsGenerator related things */ private void createDetailsMenu(MenuItem detailsMenu) { diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicStructureTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicStructureTest.java new file mode 100644 index 0000000000..9493463ddb --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicStructureTest.java @@ -0,0 +1,87 @@ +/* + * 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.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.openqa.selenium.WebElement; + +import com.vaadin.testbench.TestBenchElement; + +public class GridBasicStructureTest extends GridBasicsTest { + + @Test + public void testFreezingColumn() throws Exception { + // Freeze column 1 + selectMenuPath("Component", "State", "Frozen column count", "1"); + + WebElement cell = getGridElement().getCell(0, 0); + assertTrue("First cell on a row should be frozen", + cell.getAttribute("class").contains("frozen")); + + assertFalse("Second cell on a row should not be frozen", + getGridElement().getCell(0, 1).getAttribute("class") + .contains("frozen")); + + int cellX = cell.getLocation().getX(); + scrollGridHorizontallyTo(100); + assertEquals("First cell should not move when scrolling", cellX, + cell.getLocation().getX()); + } + + @Test + public void testHeightByRows() throws Exception { + int initialHeight = getGridElement().getSize().getHeight(); + + selectMenuPath("Component", "Size", "HeightMode Row"); + selectMenuPath("Component", "Size", "Height by Rows", "2.00 rows"); + + TestBenchElement tableWrapper = getGridElement().getTableWrapper(); + int rowHeight = getGridElement().getRow(0).getSize().getHeight(); + + assertTrue("Grid height was not 3 rows", Math + .abs(rowHeight * 3 - tableWrapper.getSize().getHeight()) < 2); + + selectMenuPath("Component", "Size", "Height by Rows", "3.33 rows"); + + assertTrue("Grid height was not 4.33 rows", Math.abs( + rowHeight * 4.33 - tableWrapper.getSize().getHeight()) < 2); + + selectMenuPath("Component", "Size", "HeightMode Row"); + assertEquals("Grid should have returned to its original size", + initialHeight, getGridElement().getSize().getHeight()); + } + + @Test + public void testHeightModeChanges() throws Exception { + selectMenuPath("Component", "Size", "Height by Rows", "2.00 rows"); + + TestBenchElement tableWrapper = getGridElement().getTableWrapper(); + int rowHeight = getGridElement().getRow(0).getSize().getHeight(); + + assertTrue("Grid height mode did not become ROW", Math + .abs(rowHeight * 3 - tableWrapper.getSize().getHeight()) < 2); + + selectMenuPath("Component", "Size", "Height", "200px"); + + assertEquals("Grid height mode did not become CSS", 200, + getGridElement().getSize().getHeight()); + + } +} -- 2.39.5