diff options
7 files changed, 458 insertions, 8 deletions
diff --git a/client/src/main/java/com/vaadin/client/connectors/grid/ColumnConnector.java b/client/src/main/java/com/vaadin/client/connectors/grid/ColumnConnector.java index 3805e01370..d88a50b7e5 100644 --- a/client/src/main/java/com/vaadin/client/connectors/grid/ColumnConnector.java +++ b/client/src/main/java/com/vaadin/client/connectors/grid/ColumnConnector.java @@ -80,6 +80,21 @@ public class ColumnConnector extends AbstractExtensionConnector { column.setSortable(getState().sortable); } + @OnStateChange("hidingToggleCaption") + void updateHidingToggleCaption() { + column.setHidingToggleCaption(getState().hidingToggleCaption); + } + + @OnStateChange("hidden") + void updateHidden() { + column.setHidden(getState().hidden); + } + + @OnStateChange("hidable") + void updateHidable() { + column.setHidable(getState().hidable); + } + @Override public void onUnregister() { super.onUnregister(); 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 58a5ea8262..fd31622cfe 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 @@ -157,6 +157,13 @@ public class GridConnector return null; }); + getWidget().addColumnVisibilityChangeHandler(event -> { + if (event.isUserOriginated()) { + getRpcProxy(GridServerRpc.class).columnVisibilityChanged( + getColumnId(event.getColumn()), event.isHidden()); + } + }); + /* Item click events */ getWidget().addBodyClickHandler(itemClickHandler); getWidget().addBodyDoubleClickHandler(itemClickHandler); diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java index 806f0d270f..b77e2ca7f7 100644 --- a/server/src/main/java/com/vaadin/ui/Grid.java +++ b/server/src/main/java/com/vaadin/ui/Grid.java @@ -73,6 +73,12 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { private static final Method ITEM_CLICK_METHOD = ReflectTools .findMethod(ItemClickListener.class, "accept", ItemClick.class); + @Deprecated + private static final Method COLUMN_VISIBILITY_METHOD = ReflectTools + .findMethod(ColumnVisibilityChangeListener.class, + "columnVisibilityChanged", + ColumnVisibilityChangeEvent.class); + /** * An event fired when an item in the Grid has been clicked. * @@ -243,6 +249,85 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { } /** + * An event listener for column visibility change events in the Grid. + * + * @since 7.5.0 + */ + public interface ColumnVisibilityChangeListener extends Serializable { + + /** + * Called when a column has become hidden or unhidden. + * + * @param event + */ + void columnVisibilityChanged(ColumnVisibilityChangeEvent event); + } + + /** + * An event that is fired when a column's visibility changes. + * + * @since 7.5.0 + */ + public static class ColumnVisibilityChangeEvent extends Component.Event { + + private final Column<?, ?> column; + private final boolean userOriginated; + private final boolean hidden; + + /** + * Constructor for a column visibility change event. + * + * @param source + * the grid from which this event originates + * @param column + * the column that changed its visibility + * @param hidden + * <code>true</code> if the column was hidden, + * <code>false</code> if it became visible + * @param isUserOriginated + * <code>true</code> iff the event was triggered by an UI + * interaction + */ + public ColumnVisibilityChangeEvent(Grid<?> source, Column<?, ?> column, + boolean hidden, boolean isUserOriginated) { + super(source); + this.column = column; + this.hidden = hidden; + userOriginated = isUserOriginated; + } + + /** + * Gets the column that became hidden or visible. + * + * @return the column that became hidden or visible. + * @see Column#isHidden() + */ + public Column<?, ?> getColumn() { + return column; + } + + /** + * Was the column set hidden or visible. + * + * @return <code>true</code> if the column was hidden <code>false</code> + * if it was set visible + */ + public boolean isHidden() { + return hidden; + } + + /** + * Returns <code>true</code> if the column reorder was done by the user, + * <code>false</code> if not and it was triggered by server side code. + * + * @return <code>true</code> if event is a result of user interaction + */ + public boolean isUserOriginated() { + return userOriginated; + } + } + + /** * A callback interface for generating description texts for an item. * * @param <T> @@ -370,9 +455,13 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { } @Override - public void columnVisibilityChanged(String id, boolean hidden, - boolean userOriginated) { - // TODO Auto-generated method stub + public void columnVisibilityChanged(String id, boolean hidden) { + Column<T, ?> column = getColumn(id); + ColumnState columnState = column.getState(false); + if (columnState.hidden != hidden) { + columnState.hidden = hidden; + fireColumnVisibilityChangeEvent(column, hidden, true); + } } @Override @@ -852,6 +941,119 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { public DescriptionGenerator<T> getDescriptionGenerator() { return descriptionGenerator; } + + /** + * Sets the caption of the hiding toggle for this column. Shown in the + * toggle for this column in the grid's sidebar when the column is + * {@link #isHidable() hidable}. + * <p> + * The default value is <code>null</code>, and in that case the column's + * {@link #getHeaderCaption() header caption} is used. + * <p> + * <em>NOTE:</em> setting this to empty string might cause the hiding + * toggle to not render correctly. + * + * @since 7.5.0 + * @param hidingToggleCaption + * the text to show in the column hiding toggle + * @return the column itself + */ + public Column<T, V> setHidingToggleCaption(String hidingToggleCaption) { + if (hidingToggleCaption != getHidingToggleCaption()) { + getState().hidingToggleCaption = hidingToggleCaption; + } + return this; + } + + /** + * Gets the caption of the hiding toggle for this column. + * + * @since 7.5.0 + * @see #setHidingToggleCaption(String) + * @return the caption for the hiding toggle for this column + */ + public String getHidingToggleCaption() { + return getState(false).hidingToggleCaption; + } + + /** + * Hides or shows the column. By default columns are visible before + * explicitly hiding them. + * + * @since 7.5.0 + * @param hidden + * <code>true</code> to hide the column, <code>false</code> + * to show + * @return this column + * @throws IllegalStateException + * if the column is no longer attached to any grid + */ + public Column<T, V> setHidden(boolean hidden) { + checkColumnIsAttached(); + if (hidden != isHidden()) { + getState().hidden = hidden; + getParent().fireColumnVisibilityChangeEvent(this, hidden, + false); + } + return this; + } + + /** + * Returns whether this column is hidden. Default is {@code false}. + * + * @since 7.5.0 + * @return <code>true</code> if the column is currently hidden, + * <code>false</code> otherwise + */ + public boolean isHidden() { + return getState(false).hidden; + } + + /** + * Sets whether this column can be hidden by the user. Hidable columns + * can be hidden and shown via the sidebar menu. + * + * @since 7.5.0 + * @param hidable + * <code>true</code> iff the column may be hidable by the + * user via UI interaction + * @return this column + */ + public Column<T, V> setHidable(boolean hidable) { + if (hidable != isHidable()) { + getState().hidable = hidable; + } + return this; + } + + /** + * Returns whether this column can be hidden by the user. Default is + * {@code false}. + * <p> + * <em>Note:</em> the column can be programmatically hidden using + * {@link #setHidden(boolean)} regardless of the returned value. + * + * @since 7.5.0 + * @return <code>true</code> if the user can hide the column, + * <code>false</code> if not + */ + public boolean isHidable() { + return getState(false).hidable; + } + + /** + * Checks whether this column is attached and throws an + * {@link IllegalStateException} if it is not. + * + * @throws IllegalStateException + * if the column is no longer attached to any grid + */ + protected void checkColumnIsAttached() throws IllegalStateException { + if (getParent() == null) { + throw new IllegalStateException( + "Column is no longer attached to a grid."); + } + } } private final class SingleSelection extends AbstractSingleSelection { @@ -923,6 +1125,12 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { }); } + public <V> void fireColumnVisibilityChangeEvent(Column<T, V> column, + boolean hidden, boolean userOriginated) { + fireEvent(new ColumnVisibilityChangeEvent(this, column, hidden, + userOriginated)); + } + /** * Adds a new column to this {@link Grid} with given header caption, typed * renderer and value provider. @@ -1256,6 +1464,22 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents { return () -> removeListener(ItemClick.class, listener); } + /** + * Registers a new column visibility change listener + * + * @param listener + * the listener to register, not null + * @return a registration for the listener + */ + public Registration addColumnVisibilityChangeListener( + ColumnVisibilityChangeListener listener) { + Objects.requireNonNull(listener, "listener cannot be null"); + addListener(ColumnVisibilityChangeEvent.class, listener, + COLUMN_VISIBILITY_METHOD); + return () -> removeListener(ColumnVisibilityChangeEvent.class, listener, + COLUMN_VISIBILITY_METHOD); + } + @Override protected GridState getState() { return getState(true); diff --git a/shared/src/main/java/com/vaadin/shared/ui/grid/ColumnState.java b/shared/src/main/java/com/vaadin/shared/ui/grid/ColumnState.java index 63a93cac15..c789988cb5 100644 --- a/shared/src/main/java/com/vaadin/shared/ui/grid/ColumnState.java +++ b/shared/src/main/java/com/vaadin/shared/ui/grid/ColumnState.java @@ -24,5 +24,14 @@ public class ColumnState extends SharedState { public String id; public boolean sortable; + /** The caption for the column hiding toggle. */ + public String hidingToggleCaption; + + /** Whether this column is currently hidden. */ + public boolean hidden = false; + + /** Whether the column can be hidden by the user. */ + public boolean hidable = false; + public Connector renderer; } diff --git a/shared/src/main/java/com/vaadin/shared/ui/grid/GridServerRpc.java b/shared/src/main/java/com/vaadin/shared/ui/grid/GridServerRpc.java index 7c9eb42b2c..08417b4b33 100644 --- a/shared/src/main/java/com/vaadin/shared/ui/grid/GridServerRpc.java +++ b/shared/src/main/java/com/vaadin/shared/ui/grid/GridServerRpc.java @@ -83,12 +83,8 @@ public interface GridServerRpc extends ServerRpc { * the id of the column * @param hidden * <code>true</code> if hidden, <code>false</code> if unhidden - * @param userOriginated - * <code>true</code> if triggered by user, <code>false</code> if - * by code */ - void columnVisibilityChanged(String id, boolean hidden, - boolean userOriginated); + void columnVisibilityChanged(String id, boolean hidden); /** * Informs the server that a column has been resized by the user. diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/GridColumnHiding.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridColumnHiding.java new file mode 100644 index 0000000000..7fa86b6522 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridColumnHiding.java @@ -0,0 +1,50 @@ +package com.vaadin.tests.components.grid; + +import java.util.Arrays; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.tests.data.bean.Person; +import com.vaadin.ui.Button; +import com.vaadin.ui.Grid; +import com.vaadin.ui.Grid.Column; +import com.vaadin.ui.renderers.NumberRenderer; +import com.vaadin.v7.ui.Label; + +public class GridColumnHiding extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + Grid<Person> grid = new Grid<>(); + Column<Person, String> nameColumn = grid + .addColumn("Name", Person::getFirstName).setHidable(true); + Column<Person, Number> ageColumn = grid + .addColumn("Age", Person::getAge, new NumberRenderer()) + .setHidable(true) + .setHidingToggleCaption("custom age column caption"); + Column<Person, String> emailColumn = grid.addColumn("Email", + Person::getEmail); + + Button toggleNameColumn = new Button("server side toggle name column"); + Button toggleAgeColumn = new Button("server side toggle age column"); + Button toggleEmailColumn = new Button( + "server side toggle email column"); + + toggleNameColumn.addClickListener( + event -> nameColumn.setHidden(!nameColumn.isHidden())); + toggleAgeColumn.addClickListener( + event -> ageColumn.setHidden(!ageColumn.isHidden())); + toggleEmailColumn.addClickListener( + event -> emailColumn.setHidden(!emailColumn.isHidden())); + + Label visibilityChangeLabel = new Label("visibility change label"); + grid.addColumnVisibilityChangeListener(event -> visibilityChangeLabel + .setValue(event.getColumn().isHidden() + "")); + + grid.setItems(Arrays.asList(Person.createTestPerson1(), + Person.createTestPerson2())); + + addComponents(grid, toggleNameColumn, toggleAgeColumn, + toggleEmailColumn, visibilityChangeLabel); + } +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/GridColumnHidingTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/GridColumnHidingTest.java new file mode 100644 index 0000000000..e0ec3edbd8 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/GridColumnHidingTest.java @@ -0,0 +1,149 @@ +/* + * 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; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.WebElement; + +import com.vaadin.testbench.By; +import com.vaadin.testbench.customelements.GridElement; +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.LabelElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class GridColumnHidingTest extends MultiBrowserTest { + + @Test + public void serverHideColumns() { + openTestURL(); + GridElement grid = $(GridElement.class).first(); + ButtonElement toggleNameColumn = $(ButtonElement.class).get(0); + ButtonElement toggleAgeColumn = $(ButtonElement.class).get(1); + ButtonElement toggleEmailColumn = $(ButtonElement.class).get(2); + + Assert.assertEquals("Foo", grid.getCell(0, 0).getText()); + Assert.assertEquals("Maya", grid.getCell(1, 0).getText()); + Assert.assertEquals("46", grid.getCell(0, 1).getText()); + Assert.assertEquals("yeah@cool.com", grid.getCell(0, 2).getText()); + + toggleAgeColumn.click(); + Assert.assertEquals("Foo", grid.getCell(0, 0).getText()); + Assert.assertEquals("Maya", grid.getCell(1, 0).getText()); + Assert.assertEquals("yeah@cool.com", grid.getCell(0, 1).getText()); + + toggleNameColumn.click(); + Assert.assertEquals("yeah@cool.com", grid.getCell(0, 0).getText()); + + toggleEmailColumn.click(); + Assert.assertFalse(isElementPresent(By.className("v-grid-cell"))); + + toggleAgeColumn.click(); + toggleNameColumn.click(); + toggleEmailColumn.click(); + Assert.assertEquals("Foo", grid.getCell(0, 0).getText()); + Assert.assertEquals("46", grid.getCell(0, 1).getText()); + Assert.assertEquals("yeah@cool.com", grid.getCell(0, 2).getText()); + } + + @Test + public void clientHideColumns() { + openTestURL(); + GridElement grid = $(GridElement.class).first(); + + getSidebarOpenButton(grid).click(); + getColumnHidingToggle(grid, "custom age column caption").click(); + Assert.assertEquals("Foo", grid.getCell(0, 0).getText()); + Assert.assertEquals("Maya", grid.getCell(1, 0).getText()); + Assert.assertEquals("yeah@cool.com", grid.getCell(0, 1).getText()); + Assert.assertEquals("maya@foo.bar", grid.getCell(1, 1).getText()); + + getColumnHidingToggle(grid, "Name").click(); + Assert.assertEquals("yeah@cool.com", grid.getCell(0, 0).getText()); + + getColumnHidingToggle(grid, "custom age column caption").click(); + Assert.assertEquals("46", grid.getCell(0, 0).getText()); + Assert.assertEquals("18", grid.getCell(1, 0).getText()); + Assert.assertEquals("yeah@cool.com", grid.getCell(0, 1).getText()); + Assert.assertEquals("maya@foo.bar", grid.getCell(1, 1).getText()); + } + + @Test + public void columnVisibilityChangeListener() { + openTestURL(); + GridElement grid = $(GridElement.class).first(); + LabelElement isHiddenLabel = $(LabelElement.class).get(1); + ButtonElement toggleNameColumn = $(ButtonElement.class).get(0); + ButtonElement toggleAgeColumn = $(ButtonElement.class).get(1); + + Assert.assertEquals("visibility change label", isHiddenLabel.getText()); + toggleNameColumn.click(); + Assert.assertEquals("true", isHiddenLabel.getText()); + toggleAgeColumn.click(); + Assert.assertEquals("true", isHiddenLabel.getText()); + toggleAgeColumn.click(); + Assert.assertEquals("false", isHiddenLabel.getText()); + + getSidebarOpenButton(grid).click(); + getColumnHidingToggle(grid, "Name").click(); + Assert.assertEquals("false", isHiddenLabel.getText()); + getColumnHidingToggle(grid, "custom age column caption").click(); + Assert.assertEquals("true", isHiddenLabel.getText()); + getSidebarOpenButton(grid).click(); + } + + @Test + public void columnTogglesVisibility() { + openTestURL(); + GridElement grid = $(GridElement.class).first(); + getSidebarOpenButton(grid).click(); + List<WebElement> elements = getColumnHidingToggles(grid); + Assert.assertEquals(2, elements.size()); + Assert.assertTrue("Name".equals(elements.get(0).getText())); + Assert.assertTrue( + "custom age column caption".equals(elements.get(1).getText())); + } + + protected WebElement getSidebarOpenButton(GridElement grid) { + List<WebElement> elements = grid + .findElements(By.className("v-grid-sidebar-button")); + return elements.isEmpty() ? null : elements.get(0); + } + + protected List<WebElement> getColumnHidingToggles(GridElement grid) { + WebElement sidebar = getSidebar(grid); + return sidebar.findElements(By.className("column-hiding-toggle")); + } + + protected WebElement getColumnHidingToggle(GridElement grid, + String caption) { + List<WebElement> elements = getColumnHidingToggles(grid); + for (WebElement e : elements) { + if (caption.equalsIgnoreCase(e.getText())) { + return e; + } + } + return null; + } + + protected WebElement getSidebar(GridElement grid) { + List<WebElement> elements = findElements( + By.className("v-grid-sidebar-popup")); + return elements.isEmpty() ? null : elements.get(0); + } +} |