From 58fefe9647455cbe1976d9198bf5864f43257fb3 Mon Sep 17 00:00:00 2001 From: Javier Godoy <11554739+javier-godoy@users.noreply.github.com> Date: Tue, 18 Aug 2026 15:59:06 -0300 Subject: [PATCH 1/3] build: set version to 2.2.0-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3d44729..601340d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.vaadin.addons.flowingcode grid-helpers - 2.1.1-SNAPSHOT + 2.2.0-SNAPSHOT Grid Helpers Add-on Grid Helpers Add-on for Vaadin Flow https://www.flowingcode.com/en/open-source/ From 9d9195fc9509a42954ded5531c5701e5b9ca81c8 Mon Sep 17 00:00:00 2001 From: Javier Godoy <11554739+javier-godoy@users.noreply.github.com> Date: Tue, 18 Aug 2026 12:52:33 -0300 Subject: [PATCH 2/3] feat: move column toggle into a slot Close #177 --- .../gridhelpers/ColumnToggleHelper.java | 29 ++++++++++--------- .../vaadin/addons/gridhelpers/GridHelper.java | 19 ++++++++++-- .../frontend/fcGridHelper/connector.js | 7 ++++- .../{vaadin-menu-bar.css => styles.css} | 21 ++++++++------ .../frontend/fcGridHelper/vaadin-grid.css | 9 ------ .../addons/gridhelpers/it/ColumnToggleIT.java | 15 ++++++++-- .../gridhelpers/it/GridHelperElement.java | 20 +++++++++++++ .../gridhelpers/test/GridHelperTest.java | 6 ++-- 8 files changed, 84 insertions(+), 42 deletions(-) rename src/main/resources/META-INF/frontend/fcGridHelper/{vaadin-menu-bar.css => styles.css} (60%) diff --git a/src/main/java/com/flowingcode/vaadin/addons/gridhelpers/ColumnToggleHelper.java b/src/main/java/com/flowingcode/vaadin/addons/gridhelpers/ColumnToggleHelper.java index fc08616..28a99f6 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/gridhelpers/ColumnToggleHelper.java +++ b/src/main/java/com/flowingcode/vaadin/addons/gridhelpers/ColumnToggleHelper.java @@ -2,7 +2,7 @@ * #%L * Grid Helpers Add-on * %% - * Copyright (C) 2022 - 2024 Flowing Code + * Copyright (C) 2022 - 2026 Flowing Code * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,7 +54,7 @@ class ColumnToggleHelper implements Serializable { private final GridHelper helper; - private Column menuToggleColumn; + private MenuBar menuToggle; public void setColumnToggleVisible(boolean visible) { // https://cookbook.vaadin.com/grid-column-toggle @@ -66,7 +66,7 @@ public void setColumnToggleVisible(boolean visible) { } public boolean isColumnToggleVisible() { - return menuToggleColumn != null && menuToggleColumn.isVisible(); + return menuToggle != null; } private void showColumnToggle() { @@ -74,18 +74,20 @@ private void showColumnToggle() { .ifPresent( toggle -> { Grid grid = helper.getGrid(); - if (menuToggleColumn == null) { - menuToggleColumn = grid.addColumn(t -> "").setWidth("auto").setFlexGrow(0); - } else { - menuToggleColumn.setVisible(true); - } - grid.getHeaderRows().get(0).getCell(menuToggleColumn).setComponent(toggle); + removeMenuToggle(); + menuToggle = toggle; + grid.getElement().appendChild(toggle.getElement()); }); } private void hideColumnToggle() { - if (menuToggleColumn != null) { - menuToggleColumn.setVisible(false); + removeMenuToggle(); + } + + private void removeMenuToggle() { + if (menuToggle != null) { + menuToggle.getElement().removeFromParent(); + menuToggle = null; } } @@ -93,6 +95,7 @@ private Optional createMenuToggle() { Grid grid = helper.getGrid(); MenuBar menuBar = new MenuBar(); + menuBar.getThemeNames().add(MenuBarVariant.LUMO_TERTIARY.getVariantName()); menuBar.getThemeNames().add(MenuBarVariant.LUMO_TERTIARY_INLINE.getVariantName()); MenuItem menuItem = menuBar.addItem(VaadinIcon.ELLIPSIS_DOTS_V.create()); SubMenu subMenu = menuItem.getSubMenu(); @@ -110,6 +113,7 @@ private Optional createMenuToggle() { } menuBar.getThemeNames().add(GRID_HELPER_TOGGLE_THEME); + menuBar.getElement().setAttribute("slot", "fc-column-toggle"); return Optional.of(menuBar).filter(_menuBar -> !_menuBar.getItems().isEmpty()); } @@ -170,7 +174,4 @@ public void setHidingToggleCaption(Column column, String caption) { } } - Column getMenuToggleColumn() { - return menuToggleColumn; - } } diff --git a/src/main/java/com/flowingcode/vaadin/addons/gridhelpers/GridHelper.java b/src/main/java/com/flowingcode/vaadin/addons/gridhelpers/GridHelper.java index 8ab6408..89adad3 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/gridhelpers/GridHelper.java +++ b/src/main/java/com/flowingcode/vaadin/addons/gridhelpers/GridHelper.java @@ -2,7 +2,7 @@ * #%L * Grid Helpers Add-on * %% - * Copyright (C) 2022 - 2025 Flowing Code + * Copyright (C) 2022 - 2026 Flowing Code * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,6 +43,7 @@ import com.vaadin.flow.shared.Registration; import java.io.Serializable; import java.util.Collection; +import java.util.Objects; import lombok.AccessLevel; import lombok.Getter; import org.slf4j.Logger; @@ -50,7 +51,6 @@ @SuppressWarnings("serial") @JsModule("./fcGridHelper/connector.js") -@CssImport(value = "./fcGridHelper/vaadin-menu-bar.css", themeFor = "vaadin-menu-bar") @CssImport(value = GridHelper.GRID_STYLES, themeFor = "vaadin-grid") @CssImport( value = "./fcGridHelper/vaadin-context-menu-item.css", @@ -67,6 +67,7 @@ @CssImport( value = "./fcGridHelper/vaadin-checkbox.css", themeFor = "vaadin-checkbox") +@CssImport(value = "./fcGridHelper/styles.css") public final class GridHelper implements Serializable { private static final Logger logger = LoggerFactory.getLogger(GridHelper.class); @@ -362,8 +363,20 @@ public static String getHidingToggleCaption(Column column) { return getHelper(column.getGrid()).columnToggleHelper.getHidingToggleCaption(column); } + /** + * Returns whether the given column renders the column toggle. + * + * @param column the column to test, not {@code null} + * @return always {@code false} + * @throws NullPointerException if {@code column} is {@code null} + * @deprecated Since 2.2.0, the column toggle is rendered in the {@code fc-column-toggle} slot + * instead of in a dedicated column, so no column is a menu toggle column and this + * method always returns {@code false}. + */ + @Deprecated(forRemoval = true, since = "2.2.0") public static boolean isMenuToggleColumn(Column column) { - return column == getHelper(column).columnToggleHelper.getMenuToggleColumn(); + Objects.requireNonNull(column); + return false; } // Empty Label diff --git a/src/main/resources/META-INF/frontend/fcGridHelper/connector.js b/src/main/resources/META-INF/frontend/fcGridHelper/connector.js index 9ee5833..fdee270 100644 --- a/src/main/resources/META-INF/frontend/fcGridHelper/connector.js +++ b/src/main/resources/META-INF/frontend/fcGridHelper/connector.js @@ -29,7 +29,12 @@ import { Grid } from '@vaadin/grid/src/vaadin-grid.js'; (function () { window.Vaadin.Flow.fcGridHelperConnector = { initLazy: grid => { - + if (!grid.shadowRoot.querySelector('slot[name="fc-column-toggle"]')) { + const slot = document.createElement('slot'); + slot.setAttribute('name','fc-column-toggle'); + grid.shadowRoot.appendChild(slot); + } + //https://cookbook.vaadin.com/grid-arrow-selection grid.addEventListener('keyup', function(e) { if (e.keyCode == 32) return; diff --git a/src/main/resources/META-INF/frontend/fcGridHelper/vaadin-menu-bar.css b/src/main/resources/META-INF/frontend/fcGridHelper/styles.css similarity index 60% rename from src/main/resources/META-INF/frontend/fcGridHelper/vaadin-menu-bar.css rename to src/main/resources/META-INF/frontend/fcGridHelper/styles.css index 2a3c774..47188c9 100644 --- a/src/main/resources/META-INF/frontend/fcGridHelper/vaadin-menu-bar.css +++ b/src/main/resources/META-INF/frontend/fcGridHelper/styles.css @@ -2,7 +2,7 @@ * #%L * Grid Helpers Add-on * %% - * Copyright (C) 2022 - 2024 Flowing Code + * Copyright (C) 2022 - 2026 Flowing Code * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,15 +17,18 @@ * limitations under the License. * #L% */ - -:host([theme~="gridHelperToggle"]) { +vaadin-menu-bar[theme~="gridHelperToggle"][slot] { position: absolute; - top: 0; - right: var(--fcgh-toggle-right); - height: 100%; + top: -4px; + right: calc( 6px - var(--vaadin-aura-theme,0)*1px); +} + +vaadin-menu-bar[theme~="gridHelperToggle"][slot] vaadin-menu-bar-button::part(suffix) { + display:none; } -:host([theme~="gridHelperToggle"]) [part="container"] { - vertical-align: middle; - height: 100%; +vaadin-menu-bar[theme~="gridHelperToggle"][slot] > vaadin-menu-bar-button { + padding: calc(var(--vaadin-aura-theme, 0) * 2px); + --lumo-size-m: 0; } + diff --git a/src/main/resources/META-INF/frontend/fcGridHelper/vaadin-grid.css b/src/main/resources/META-INF/frontend/fcGridHelper/vaadin-grid.css index 675f114..91b5817 100644 --- a/src/main/resources/META-INF/frontend/fcGridHelper/vaadin-grid.css +++ b/src/main/resources/META-INF/frontend/fcGridHelper/vaadin-grid.css @@ -42,15 +42,6 @@ min-height: calc(var(--lumo-size-xxs) - var(--_lumo-grid-border-width)); } -:host { - --fcgh-toggle-right: 0; -} - -:host([overflow~="right"]), :host([overflow~="left"]), -:host([overflow~="start"]), :host([overflow~="end"]) { - --fcgh-toggle-right: 24px; -} - table[aria-multiselectable="true"] [part~="fcGh-noselect"][first-column] ::slotted(*) { opacity: var(--fcgh-noselect-opacity, 0.5); pointer-events: none; diff --git a/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/it/ColumnToggleIT.java b/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/it/ColumnToggleIT.java index 4366562..275d86d 100644 --- a/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/it/ColumnToggleIT.java +++ b/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/it/ColumnToggleIT.java @@ -52,13 +52,24 @@ public void testColumnToggleVisible() { $server.setColumnToggleVisible(true); assertNotNull("ColumnToggle should be present", grid.getColumnToggleButton()); - assertThat(grid.getVisibleColumns(), hasSize(nColumns + 1)); + assertThat(grid.getVisibleColumns(), hasSize(nColumns)); $server.setColumnToggleVisible(false); assertNull("ColumnToggle should be absent", grid.getColumnToggleButton()); assertThat(grid.getVisibleColumns(), hasSize(nColumns)); } + @Test + public void testColumnToggleSlot() { + $server.setColumnToggleVisible(true); + assertNotNull("ColumnToggle should be present", grid.getColumnToggle()); + assertEquals("fc-column-toggle", grid.getColumnToggleSlotName()); + + $server.setColumnToggleVisible(false); + assertNull("ColumnToggle should be absent", grid.getColumnToggle()); + assertNull(grid.getColumnToggleSlotName()); + } + @Test public void testColumnToggleClick() { int nColumns = grid.getVisibleColumns().size(); @@ -66,9 +77,7 @@ public void testColumnToggleClick() { $server.setColumnToggleVisible(true); grid.getColumnToggleButton().click(); - // the toggle is rendered in its own column assertThat(grid.getColumnToggleElements(), hasSize(nColumns)); - assertThat(grid.getVisibleColumns(), hasSize(++nColumns)); grid.getColumnToggleElements().get(0).setChecked(false); assertThat(grid.getVisibleColumns(), hasSize(nColumns - 1)); diff --git a/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/it/GridHelperElement.java b/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/it/GridHelperElement.java index ad44b82..0c45255 100644 --- a/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/it/GridHelperElement.java +++ b/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/it/GridHelperElement.java @@ -76,6 +76,26 @@ public TestBenchElement getColumnToggleButton() { } } + /** + * Returns the column toggle menu bar, which is slotted into the grid, or {@code null} if the + * column toggle is not visible. + */ + public TestBenchElement getColumnToggle() { + List elements = + findElements(By.cssSelector("vaadin-menu-bar[theme~='gridHelperToggle']")); + return (TestBenchElement) elements.stream().findFirst().orElse(null); + } + + /** + * Returns the name of the slot that the column toggle is assigned to, or {@code null} if the + * column toggle is not visible or is not assigned to any slot. + */ + public String getColumnToggleSlotName() { + return (String) executeScript( + "const toggle = this.querySelector('vaadin-menu-bar[theme~=\"gridHelperToggle\"]');" + + "return toggle && toggle.assignedSlot && toggle.assignedSlot.name;"); + } + public List getColumnToggleElements() { try { ElementQuery query; diff --git a/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/test/GridHelperTest.java b/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/test/GridHelperTest.java index 9dc31cc..3ac7e5b 100644 --- a/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/test/GridHelperTest.java +++ b/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/test/GridHelperTest.java @@ -184,11 +184,11 @@ public void testSelectOnClick() { } @Test - public void testMenuToggleColumn() { + public void testMenuToggleSlot() { grid.setColumnToggleVisible(true); - Column toggleColumn=grid.getColumns().get(grid.getColumns().size()-1); - assertTrue(GridHelper.isMenuToggleColumn(toggleColumn)); + assertTrue(grid.getElement().getChildren() + .anyMatch(e -> "fc-column-toggle".equals(e.getAttribute("slot")))); } @Test From ac82dbba0945dca6208aef2788910e7249ab6a5a Mon Sep 17 00:00:00 2001 From: Javier Godoy <11554739+javier-godoy@users.noreply.github.com> Date: Thu, 20 Aug 2026 08:49:16 -0300 Subject: [PATCH 3/3] feat: add API for replacing column toggle icon See #177 --- .../gridhelpers/ColumnToggleHelper.java | 20 +++++++++++++++- .../vaadin/addons/gridhelpers/GridHelper.java | 23 +++++++++++++++++++ .../gridhelpers/ColumnToggleMenuDemo.java | 2 ++ .../vaadin/addons/gridhelpers/LombokDemo.java | 3 ++- 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/flowingcode/vaadin/addons/gridhelpers/ColumnToggleHelper.java b/src/main/java/com/flowingcode/vaadin/addons/gridhelpers/ColumnToggleHelper.java index 28a99f6..ec68ebf 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/gridhelpers/ColumnToggleHelper.java +++ b/src/main/java/com/flowingcode/vaadin/addons/gridhelpers/ColumnToggleHelper.java @@ -26,6 +26,7 @@ package com.flowingcode.vaadin.addons.gridhelpers; +import com.vaadin.flow.component.Component; import com.vaadin.flow.component.ComponentEventListener; import com.vaadin.flow.component.ComponentUtil; import com.vaadin.flow.component.checkbox.Checkbox; @@ -38,6 +39,7 @@ import com.vaadin.flow.component.menubar.MenuBarVariant; import com.vaadin.flow.shared.Registration; import java.io.Serializable; +import java.util.Objects; import java.util.Optional; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -56,6 +58,8 @@ class ColumnToggleHelper implements Serializable { private MenuBar menuToggle; + private Component icon; + public void setColumnToggleVisible(boolean visible) { // https://cookbook.vaadin.com/grid-column-toggle if (visible) { @@ -69,6 +73,20 @@ public boolean isColumnToggleVisible() { return menuToggle != null; } + private Component getColumnToggleIcon() { + if (icon == null) { + icon = VaadinIcon.ELLIPSIS_DOTS_V.create(); + } + return icon; + } + + public void setColumnToggleIcon(Component icon) { + this.icon = Objects.requireNonNull(icon); + if (isColumnToggleVisible()) { + showColumnToggle(); + } + } + private void showColumnToggle() { createMenuToggle() .ifPresent( @@ -97,7 +115,7 @@ private Optional createMenuToggle() { MenuBar menuBar = new MenuBar(); menuBar.getThemeNames().add(MenuBarVariant.LUMO_TERTIARY.getVariantName()); menuBar.getThemeNames().add(MenuBarVariant.LUMO_TERTIARY_INLINE.getVariantName()); - MenuItem menuItem = menuBar.addItem(VaadinIcon.ELLIPSIS_DOTS_V.create()); + MenuItem menuItem = menuBar.addItem(getColumnToggleIcon()); SubMenu subMenu = menuItem.getSubMenu(); for (Column column : grid.getColumns()) { diff --git a/src/main/java/com/flowingcode/vaadin/addons/gridhelpers/GridHelper.java b/src/main/java/com/flowingcode/vaadin/addons/gridhelpers/GridHelper.java index 89adad3..88d57a5 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/gridhelpers/GridHelper.java +++ b/src/main/java/com/flowingcode/vaadin/addons/gridhelpers/GridHelper.java @@ -38,6 +38,7 @@ import com.vaadin.flow.component.grid.HeaderRow; import com.vaadin.flow.component.grid.HeaderRow.HeaderCell; import com.vaadin.flow.component.grid.ItemClickEvent; +import com.vaadin.flow.component.icon.IconFactory; import com.vaadin.flow.function.SerializableFunction; import com.vaadin.flow.function.SerializablePredicate; import com.vaadin.flow.shared.Registration; @@ -310,6 +311,28 @@ public static boolean isColumnToggleVisible(Grid grid) { return getHelper(grid).columnToggleHelper.isColumnToggleVisible(); } + /** + * Sets the icon of the menu that toggles the visibility of grid columns. + * + * @param grid the grid to be configured + * @param icon the component that is rendered in the column toggle + * @throws NullPointerException if {@code icon} is {@code null} + */ + public static void setColumnToggleIcon(Grid grid, Component icon) { + getHelper(grid).columnToggleHelper.setColumnToggleIcon(icon); + } + + /** + * Sets the icon of the menu that toggles the visibility of grid columns. + * + * @param grid the grid to be configured + * @param icon the factory of the icon that is rendered in the column toggle + * @throws NullPointerException if {@code icon} is {@code null} + */ + public static void setColumnToggleIcon(Grid grid, IconFactory icon) { + setColumnToggleIcon(grid, icon.create()); + } + /** * Returns whether this column can be hidden by the user. Default is {@code false}. * diff --git a/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/ColumnToggleMenuDemo.java b/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/ColumnToggleMenuDemo.java index ef0e2f0..7fc543b 100644 --- a/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/ColumnToggleMenuDemo.java +++ b/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/ColumnToggleMenuDemo.java @@ -24,6 +24,7 @@ import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.grid.Grid.Column; import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.icon.VaadinIcon; import com.vaadin.flow.component.notification.Notification; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; @@ -43,6 +44,7 @@ public ColumnToggleMenuDemo() { Column lastNameColumn = grid.addColumn(Person::getLastName).setHeader("Last name"); Column countryColumn = grid.addColumn(Person::getCountry).setHeader("Country"); + GridHelper.setColumnToggleIcon(grid, VaadinIcon.CARET_DOWN); GridHelper.setHidingToggleCaption(firstNameColumn, "First name"); GridHelper.setHidingToggleCaption(lastNameColumn, "Last name"); GridHelper.setHidingToggleCaption(countryColumn, "Country"); diff --git a/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/LombokDemo.java b/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/LombokDemo.java index 7ef7911..38005af 100644 --- a/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/LombokDemo.java +++ b/src/test/java/com/flowingcode/vaadin/addons/gridhelpers/LombokDemo.java @@ -24,9 +24,9 @@ import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.grid.Grid.SelectionMode; import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.icon.VaadinIcon; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; - import lombok.experimental.ExtensionMethod; @PageTitle("Using Lombok") @@ -55,6 +55,7 @@ public LombokDemo() { grid.getColumns().forEach(c -> c.setAutoWidth(true)); grid.setColumnToggleVisible(true); + grid.setColumnToggleIcon(VaadinIcon.CARET_DOWN); grid.setSelectionColumnFrozen(true); grid.setSelectOnClick(true); grid.setSelectionFilter(Person::isActive);