Skip to content

GridElement Specification

For the authoritative method list and signatures, see the auto-generated api-reference.md. This file focuses on behaviour and usage that signatures alone don't convey.

Overview

GridElement is a Playwright element wrapper for the <vaadin-grid> web component. It provides helpers for querying rows and cells, accessing cell content by row/column index or header text, and interacting with the grid's selection, sorting, and row details features. Row and cell access methods auto-scroll the grid as needed to bring virtualized rows into view.

Results are returned as Optional wrappers or inner-class elements (RowElement, CellElement, HeaderCellElement) so callers can interact with rows and cells directly rather than going back through the grid. Please note, that you should interact with these elements before searching for another row which may lead to scrolling and possibly hiding / removing element that is referenced.

Tag Name

vaadin-grid

Class Hierarchy

VaadinElement
    └── GridElement

Implemented Interfaces

Interface Description
FocusableElement Focus operations
HasStyleElement CSS class support
HasThemeElement Theme attribute support
HasEnabledElement Enabled/disabled state

Constants

Constant Value Description
FIELD_TAG_NAME "vaadin-grid" HTML tag name

API Methods

Constructor

GridElement(Locator locator)

Creates a new GridElement from a Playwright locator.

Static Factory Methods

get(Page page)

Get the first GridElement on the page.

GridElement grid = GridElement.get(page);

get(Locator parent)

Get the first GridElement within a parent locator.

GridElement grid = GridElement.get(page.locator("#container"));

getById(Page page, String id)

Get a GridElement by its id attribute.

GridElement grid = GridElement.getById(page, "my-grid");

Properties / Getters

Method Description
getTotalRowCount() Total number of data items
getRenderedRowCount() Number of rows currently rendered in the DOM
getColumnCount() Number of visible (non-hidden) columns
isAllRowsVisible() Whether allRowsVisible is enabled (no vertical scroll)
isMultiSort() Whether multi-sort is enabled
isColumnReorderingAllowed() Whether column reordering is allowed
findColumnIndexById(String id) OptionalInt index of the visible column with that id; empty if absent or hidden
getColumnIndexById(String id) Same, throwing IllegalArgumentException when no visible column has that id

Column Indexes

Every columnIndex / colIndex parameter counts the visible columns in display order, starting from 0 on the left:

  • hidden columns (setVisible(false)) are skipped;
  • a selection column (multi-select) is a column and counts;
  • after the user drags a column to a new place, the indexes follow the new order — the same order as TestBench's getVisibleColumns().

This matches TestBench, whose getCell(row, colIndex) and getHeaderCell(colIndex) count the selection column too. Flow's Grid.getColumns() and browserless tests (GridTester) do not list the selection column, so in a multi-select grid a Flow column index is one less than the GridElement index (the selection column is first unless the columns were reordered). To stay independent of the selection mode, look a column up by header text (findCell(row, "Email")) or by id (getColumnIndexById) instead of hard-coding its index.

A column with no header text cannot be found with findHeaderCellByText; give it an id in Flow (Column.setId("email"), which lands on the <vaadin-grid-column>) and look it up with getColumnIndexById("email"). Column.setKey(..) stays on the server and is not visible to the browser.

int email = grid.getColumnIndexById("email");
grid.assertCellContent(0, email, "person1@example.com");

Header Access Methods

All findHeaderCell* methods return Optional<HeaderCellElement>.

Method Description
findHeaderCell(int colIndex) Header cell by column index in the first header row
findHeaderCell(int headerRowIndex, int colIndex) Header cell by header row index and column index
findHeaderCellByText(String text) Header cell by text content in the first header row
findHeaderCellByText(int headerRowIndex, String text) Header cell by header row index and text content
getHeaderCellContents() Text content of all visible header cells
getHeaderCells(int colIndex) List<HeaderCellElement>: the column's header cells in every header row, top row first

getHeaderCells returns the cell of the column itself and those of the column groups around it, which is how the web component renders Flow's extra HeaderRows: a joined cell above (returned for each column it spans) or a filter field in an appended row below. Scope a component lookup to a cell's content:

// Header rows: joined "Name" | "Last Name" with sorter | filter field
// The column has Column.setId("last-name") in Flow
var cells = grid.getHeaderCells(grid.getColumnIndexById("last-name"));
TextFieldElement filter = TextFieldElement.getById(cells.get(2).getCellContentLocator(), "last-filter");
filter.setValue("Smith");

All findFooterCell* methods return Optional<FooterCellElement>.

Method Description
findFooterCell(int colIndex) Footer cell by column index in the first footer row
findFooterCell(int footerRowIndex, int colIndex) Footer cell by footer row index and column index
findFooterCellByText(String text) Footer cell by text content in the first footer row
findFooterCellByText(int footerRowIndex, String text) Footer cell by footer row index and text content
getFooterCellContents() Text content of all visible footer cells

Row Access Methods

Method Return Type Description
findRow(int rowIndex) Optional<RowElement> Find row by index; auto-scrolls if the row is not yet in view
findRowIndexesWithColumnText(int columnIndex, String text) List<Integer> Row indexes where the given column cell matches the given text (reads the column a page at a time)
getColumnTexts(int columnIndex) List<String> Text of every body cell in the column, indexed by row

getColumnTexts (and findRowIndexesWithColumnText, built on it) reads a lazy grid a page at a time: one script reads every rendered row, then the grid jumps to the first unread row and waits for it to load. That is one scroll per page instead of several round trips per row, so prefer it over looping on findCell(row, column). To customise how a cell matches, override matchesCellText(String cellText, String text).

Cell Access Methods

Both methods return Optional<CellElement> and auto-scroll the grid if the target row is not yet in view.

Method Description
findCell(int row, int column) Body cell by row and column index
findCell(int row, String columnHeaderText) Body cell by row index and column header text

Selection Methods

Method Description
getSelectedItemCount() Number of currently selected items
select(int rowIndex) Select row by index
deselect(int rowIndex) Deselect row by index
isSelectAllChecked() Whether the select-all checkbox is checked
isSelectAllIndeterminate() Whether the select-all checkbox is indeterminate
isSelectAllUnchecked() Whether the select-all checkbox is unchecked
checkSelectAll() Check the select-all checkbox (no-op if already checked)
uncheckSelectAll() Uncheck the select-all checkbox (no-op if already unchecked)

select and deselect click the row like a user would. In a multi-select grid they click the selection checkbox. Otherwise they click the first cell that has a spot where the click reaches the grid rather than a component rendered in the cell, usually the cell padding: the grid ignores clicks on links, buttons and other focusable content, so clicking the centre of a cell holding a link would follow the link instead of selecting the row. A cell holding a link or a button therefore still selects the row through its padding. Only when interactive content covers every cell, padding included (for example a component with negative margins, or a theme without cell padding), do they throw an IllegalStateException; override the protected clickRowForSelection(RowElement) or clickCellForSelection(CellElement) to select such a row another way.

They also check the outcome. select(int) and deselect(int) throw an IllegalArgumentException when there is no row at the index, and every select and deselect fails with an AssertionError when the row's selected state didn't change after the click: a grid with no selection, a single-select grid that doesn't allow deselecting, or a server that rejected the selection.

Utility Methods

Method Description
waitForGridToStopLoading() Wait for the grid to finish loading after a scroll or action that triggers row loading

Inner Classes

CellElement

Wraps a single grid cell (<td>).

Method Description
getTableCell() Locator for the table cell element (<td>)
getColumnIndex() 0-based column index; -1 for details cells
getCellContent() Locator for the <vaadin-grid-cell-content> slot element
getContentSlotName() Name of the slot used for cell content
click() Click the cell content

HeaderCellElement extends CellElement

Wraps a single grid header cell (<th>).

Extends CellElement with sorting support.

Method Description
isSortable() Whether the column supports sorting
clickSort() Click the sorter to cycle sort direction
sort(SortDirection direction) Click the sorter until it shows direction (ASCENDING, DESCENDING, NONE); no-op if already there
getSortDirection() Current GridElement.SortDirection; NONE when unsorted
assertSortDirection(SortDirection expected) Assert the direction (retrying); NONE asserts unsorted
isSortAscending() Whether the column is sorted ascending
isSortDescending() Whether the column is sorted descending
isNotSorted() Whether the column is not sorted

FooterCellElement extends CellElement

Wraps a single grid footer cell (<td> in <tfoot>). Adds no methods beyond CellElement; exists as a distinct type returned by the footer access methods.

RowElement

Wraps a single grid row (<tr>).

Method Description
getRow() Locator for the row element <tr>
getRowIndex() 0-based row index
getCell(int columnIndex) Cell for the given column index
getCell(String columnHeaderText) Cell for the given header text
getDetailsCell() Cell element for the details column
isSelected() Whether the row is selected
select() Select this row
deselect() Deselect this row
click() Click the row, firing the grid's item click
doubleClick() Double-click the row, firing the grid's item double click
openDetails() Open this row's details panel by clicking the row
closeDetails() Close this row's details panel by clicking the row
isDetailsOpen() Whether this row's details panel is open

click() and doubleClick() click the first data cell, skipping the selection column, at a spot free of interactive content (usually the padding), so a button or link rendered in the cell is not triggered; when interactive content covers a cell entirely they move on to the next one, and throw an IllegalStateException when no cell has a free spot.

openDetails() and closeDetails() click the row the same way and rely on the grid toggling details on click, Flow's Grid.setDetailsVisibleOnClick(true) (the default). They fail with an AssertionError naming that setting when the details didn't open or close; override the protected openDetails(RowElement) / closeDetails(RowElement) when the details are toggled another way.

Usage Examples

Basic Cell Access

GridElement grid = GridElement.getById(page, "my-grid");

// Row and column counts
int rows = grid.getTotalRowCount();
int cols = grid.getColumnCount();

// Headers
List<String> headers = grid.getHeaderCellContents();

// Cell content by index (auto-scrolls if needed)
var valueCellO = grid.findCell(0, 0);
assertTrue(valueCellO.isPresent());
assertThat(valueCellO.get().getCellContent()).hasText("My name");

var emailCellO = grid.findCell(0, "Email");
assertTrue(emailCellO.isPresent());
assertThat(emailCellO.get().getCellContent()).hasText("my@email.com");

Single Selection

GridElement grid = GridElement.getById(page, "single-select-grid");

grid.select(0);
var row0 = grid.findRow(0);
assertTrue(row0.isPresent());
assertTrue(row0.get().isSelected());

grid.select(1);
assertFalse(row0.get().isSelected());

Multi Selection

GridElement grid = GridElement.getById(page, "multi-select-grid");

grid.select(0);
grid.select(1);
assertEquals(2, grid.getSelectedItemCount());

grid.checkSelectAll();
assertTrue(grid.isSelectAllChecked());

grid.uncheckSelectAll();
assertEquals(0, grid.getSelectedItemCount());

Sorting

GridElement grid = GridElement.getById(page, "sortable-grid");

var headerCell = grid.findHeaderCellByText("Name");
assertTrue(headerCell.isPresent());
headerCell.get().clickSort();
assertTrue(headerCell.get().isSortAscending());

headerCell.get().clickSort();
assertTrue(headerCell.get().isSortDescending());

headerCell.get().clickSort();
assertTrue(headerCell.get().isNotSorted());

// Or go straight to a direction (at most two clicks, waits for the grid to load)
headerCell.get().sort(GridElement.SortDirection.DESCENDING);
headerCell.get().assertSortDirection(GridElement.SortDirection.DESCENDING);

sort, getSortDirection and clickSort throw IllegalStateException on a header cell without a sorter, such as a joined group cell.

Row Details

GridElement grid = GridElement.getById(page, "details-grid");

var row = grid.findRow(0);
assertTrue(row.isPresent());
row.get().openDetails();
assertTrue(row.get().isDetailsOpen());

row.get().closeDetails();
assertFalse(row.get().isDetailsOpen());

Component Renderer

GridElement grid = GridElement.getById(page, "component-grid");

var actionCell = grid.findCell(0, "Action");
assertTrue(actionCell.isPresent());
Locator button = actionCell.get().getCellContent().locator("vaadin-button");
assertThat(button).hasText("Click First1");
button.click();

LitRenderer

GridElement grid = GridElement.getById(page, "lit-renderer-grid");

// Read text from a LitRenderer cell (auto-scrolls if needed)
var firstNameCell = grid.findCell(0, "First Name");
assertTrue(firstNameCell.isPresent());
assertThat(firstNameCell.get().getCellContent().locator(".badge")).hasText("First1");

// Click a LitRenderer button
var actionCell = grid.findCell(0, "Action");
assertTrue(actionCell.isPresent());
actionCell.get().getCellContent().locator("vaadin-button").click();

Lazy Loading

GridElement grid = GridElement.getById(page, "lazy-grid");

assertEquals(10000, grid.getTotalRowCount());

// findRow auto-scrolls to row 9000 before returning
var row = grid.findRow(9000);
assertTrue(row.isPresent());
assertThat(row.get().getCell(0).getCellContent()).hasText("First9001");

Searching Across Rows

GridElement grid = GridElement.getById(page, "my-grid");

// Find all row indexes where the "Status" column contains "Active"
List<Integer> activeRows = grid.findRowIndexesWithColumnText(2, "Active");
  • VirtualListElement - Virtualized scrollable list (simpler, no columns)