From 2f8403fe1d791514689eea630a7d6daa793e2e12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20I=C3=9Fbr=C3=BCcker?= Date: Tue, 20 Jan 2026 20:25:06 +0200 Subject: [PATCH] feat: scroll to column --- .../grid/it/GridScrollToColumnPage.java | 55 +++++++++++ .../grid/it/GridScrollToColumnIT.java | 98 +++++++++++++++++++ .../com/vaadin/flow/component/grid/Grid.java | 21 ++++ .../component/grid/testbench/GridElement.java | 30 ++++++ 4 files changed, 204 insertions(+) create mode 100644 vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/main/java/com/vaadin/flow/component/grid/it/GridScrollToColumnPage.java create mode 100644 vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/test/java/com/vaadin/flow/component/grid/it/GridScrollToColumnIT.java diff --git a/vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/main/java/com/vaadin/flow/component/grid/it/GridScrollToColumnPage.java b/vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/main/java/com/vaadin/flow/component/grid/it/GridScrollToColumnPage.java new file mode 100644 index 00000000000..6c3867fbd2f --- /dev/null +++ b/vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/main/java/com/vaadin/flow/component/grid/it/GridScrollToColumnPage.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2026 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.flow.component.grid.it; + +import com.vaadin.flow.component.grid.Grid; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.html.NativeButton; +import com.vaadin.flow.router.Route; + +@Route("vaadin-grid/grid-scroll-to-column") +public class GridScrollToColumnPage extends Div { + public GridScrollToColumnPage() { + Grid grid = new Grid<>(); + grid.setWidth("400px"); + + var items = java.util.stream.IntStream.range(0, 10).boxed().toList(); + grid.setItems(items); + + final int columnCount = 20; + for (int i = 0; i < columnCount; i++) { + final int columnIndex = i; + grid.addColumn(item -> "Cell " + columnIndex) + .setHeader("Column " + columnIndex) + .setKey("column-" + columnIndex); + } + + NativeButton scrollByIndex = new NativeButton( + "Scroll to column 10 by index", e -> grid.scrollToColumn(10)); + scrollByIndex.setId("scroll-by-index"); + add(scrollByIndex); + + NativeButton scrollByReference = new NativeButton( + "Scroll to column 10 by reference", e -> { + var column = grid.getColumnByKey("column-10"); + grid.scrollToColumn(column); + }); + scrollByReference.setId("scroll-by-reference"); + add(scrollByReference); + + add(grid); + } +} diff --git a/vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/test/java/com/vaadin/flow/component/grid/it/GridScrollToColumnIT.java b/vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/test/java/com/vaadin/flow/component/grid/it/GridScrollToColumnIT.java new file mode 100644 index 00000000000..bb7b2ba1bff --- /dev/null +++ b/vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/test/java/com/vaadin/flow/component/grid/it/GridScrollToColumnIT.java @@ -0,0 +1,98 @@ +/* + * Copyright 2000-2026 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.flow.component.grid.it; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.flow.component.grid.testbench.GridElement; +import com.vaadin.flow.component.grid.testbench.GridTHTDElement; +import com.vaadin.flow.testutil.TestPath; +import com.vaadin.tests.AbstractComponentIT; + +@TestPath("vaadin-grid/grid-scroll-to-column") +public class GridScrollToColumnIT extends AbstractComponentIT { + private GridElement grid; + + @Before + public void setUp() { + open(); + grid = $(GridElement.class).waitForFirst(); + } + + @Test + public void scrollToColumnByIndex_columnIsVisible() { + var headerCell = grid.getHeaderCell(0, 10); + isCellVisible(headerCell); + + Assert.assertFalse(isCellVisible(headerCell)); + + clickElementWithJs("scroll-by-index"); + + Assert.assertTrue(isCellVisible(headerCell)); + } + + @Test + public void scrollToColumnByReference_columnIsVisible() { + var headerCell = grid.getHeaderCell(0, 10); + isCellVisible(headerCell); + + Assert.assertFalse(isCellVisible(headerCell)); + + clickElementWithJs("scroll-by-reference"); + + Assert.assertTrue(isCellVisible(headerCell)); + } + + @Test + public void scrollToColumnByIndexWithTestbench_columnIsVisible() { + var headerCell = grid.getHeaderCell(0, 10); + isCellVisible(headerCell); + + Assert.assertFalse(isCellVisible(headerCell)); + + grid.scrollToColumn(10); + + Assert.assertTrue(isCellVisible(headerCell)); + } + + @Test + public void scrollToColumnByReferenceWithTestbench_columnIsVisible() { + var headerCell = grid.getHeaderCell(0, 10); + isCellVisible(headerCell); + + Assert.assertFalse(isCellVisible(headerCell)); + + var column = grid.getColumn("Column 10"); + grid.scrollToColumn(column); + + Assert.assertTrue(isCellVisible(headerCell)); + } + + private Boolean isCellVisible(GridTHTDElement cell) { + return (Boolean) (cell.getCommandExecutor()).executeScript( + """ + const cell = arguments[0]; + const bounds = cell.getBoundingClientRect(); + const centerX = bounds.left + bounds.width / 2; + const centerY = bounds.top + bounds.height / 2; + const cellContent = document.elementFromPoint(centerX, centerY); + return cell.querySelector('slot').assignedElements().includes(cellContent); + """, + cell); + } +} diff --git a/vaadin-grid-flow-parent/vaadin-grid-flow/src/main/java/com/vaadin/flow/component/grid/Grid.java b/vaadin-grid-flow-parent/vaadin-grid-flow/src/main/java/com/vaadin/flow/component/grid/Grid.java index 2707498ba5d..953b61f45d3 100755 --- a/vaadin-grid-flow-parent/vaadin-grid-flow/src/main/java/com/vaadin/flow/component/grid/Grid.java +++ b/vaadin-grid-flow-parent/vaadin-grid-flow/src/main/java/com/vaadin/flow/component/grid/Grid.java @@ -5054,6 +5054,27 @@ public void scrollToEnd() { .executeJs("this.scrollToIndex(this._flatSize)"))); } + /** + * Scrolls the grid horizontally to make the column with the given index + * visible. The index refers to visible columns, in their visual order. + * + * @param columnIndex + * the index of the column to scroll to + */ + public void scrollToColumn(int columnIndex) { + getElement().callJsFunction("scrollToColumn", columnIndex); + } + + /** + * Scrolls the grid horizontally to make the given column visible. + * + * @param column + * the column to scroll to + */ + public void scrollToColumn(Column column) { + getElement().callJsFunction("scrollToColumn", column.getElement()); + } + private void onDragStart(GridDragStartEvent event) { ComponentUtil.setData(this, DRAG_SOURCE_DATA_KEY, event.getDraggedItems()); diff --git a/vaadin-grid-flow-parent/vaadin-grid-testbench/src/main/java/com/vaadin/flow/component/grid/testbench/GridElement.java b/vaadin-grid-flow-parent/vaadin-grid-testbench/src/main/java/com/vaadin/flow/component/grid/testbench/GridElement.java index fb0750e0db4..e7c8c1ec207 100644 --- a/vaadin-grid-flow-parent/vaadin-grid-testbench/src/main/java/com/vaadin/flow/component/grid/testbench/GridElement.java +++ b/vaadin-grid-flow-parent/vaadin-grid-testbench/src/main/java/com/vaadin/flow/component/grid/testbench/GridElement.java @@ -60,6 +60,36 @@ void scrollToRowByFlatIndex(int rowFlatIndex) { waitUntilLoadingFinished(); } + /** + * Scrolls the grid horizontally to make the column with the given index + * visible. The index refers to visible columns, in their visual order. + * + * @param columnIndex + * the index of the column to scroll to + */ + public void scrollToColumn(int columnIndex) { + callFunction("scrollToColumn", columnIndex); + } + + /** + * Scrolls the grid horizontally to make the given column visible. + * + * @param column + * the column to scroll to + */ + public void scrollToColumn(GridColumnElement column) { + executeScript(""" + const grid = arguments[0]; + const columnId = arguments[1]; + const column = grid._getColumns().find((col) => { + return col.__generatedTbId === columnId; + }); + if (column) { + grid.scrollToColumn(column); + } + """, this, column.get__generatedId()); + } + /** * Gets the page size used when fetching data. *