feat!: return the component the grid rendered from GridTester.getCellComponent - #250
Merged
Merged
Conversation
GridTester.getCellComponent rendered the cell again on every call and appended the fresh copy to the grid, so reading the same cell twice left two components behind, find() reported both, and the component a test acted on was never the one the grid shows. It now returns the component the grid itself rendered for the row and column: reading a cell twice gives the same instance, nothing is appended, and the instance is replaced when the row is rendered anew. A row the client has not asked for yet is scrolled into view first, the way a user reaches it. The previous behaviour is available as renderCellComponent, for the cells the grid does not render, such as those of a hidden column. Fixes #235
Pins the failure a data provider whose items are not equal across fetches now produces, including the advice to render the cell instead, and turns the null-render case into an assertion about the component rather than a second copy of the text assertion. `RenderedComponentSupport` reads the rendered components straight from the generator's map, and reaches the private fields with `ReflectTools` instead of hand-rolling the lookup. The `getCellText` and `renderCellComponent` javadocs say that reading a cell can scroll the grid and fail, and that a column index never addresses a hidden column.
mcollovati
force-pushed
the
fix/235-grid-cell-component-live-instance
branch
from
September 19, 2026 05:52
4f79556 to
3fb151c
Compare
mcollovati
approved these changes
Sep 19, 2026
mcollovati
pushed a commit
that referenced
this pull request
Sep 21, 2026
…Component (#250) (CP: 25.3) (#257) This PR cherry-picks changes from the original PR #250 to branch 25.3. --- #### Original PR description > ## Summary > > `GridTester.getCellComponent` used to render the cell again on every call and attach the fresh copy to the grid, so a test never acted on the component the grid actually shows. It now returns the component the grid rendered for that row and column. The old behaviour is still available as the new `renderCellComponent`. > > Fixes #235 > > ## What changed > > **Breaking:** `getCellComponent(int, int)` and `getCellComponent(int, String)` behave differently. This affects every test that reads a component cell. > > - It returns the component the grid rendered for the client, which is the one the browser shows. Reading the same cell twice gives the same instance, and the instance is replaced when the row is rendered anew (for example after `refreshItem(...)`). > - Nothing is attached to the grid any more, so a later `find(...)` no longer reports leftover copies. Tests that counted those copies will see fewer components. > - A row the client has not asked for yet is scrolled into view first, the way a user reaches it. Reading a cell can therefore scroll the grid. > - It now throws `IllegalStateException` for a cell the grid renders nothing for: a hidden column, or a data provider whose items are not equal across fetches. The message points at `renderCellComponent`. > - `getCellText` on a `ComponentRenderer` column reads the same rendered component. For a renderer that returns no component it now returns `""` instead of `null`, because the grid renders an empty text node in that place. It can also throw for the cases listed above. > > New, additive: > > - `renderCellComponent(int, int)` and `renderCellComponent(int, String)` keep the old behaviour — render the cell on its own and attach the copy to the grid. Use them for cells the grid does not render, such as those of a hidden column, and to keep older tests working. > - An internal helper class reads the rendered components from the column's data generator. > > A column index addresses only visible columns, so a hidden column's cells are reachable only by key, through `renderCellComponent(int, String)`. > > README and the `find(...)` javadocs were updated to match. > > ## Use case > > You have a grid with a component column of "Approve" buttons. A test clicks the button on the second row and checks that the row's item was really approved. With the old behaviour the click went to a throwaway copy, so nothing in the grid reacted. > > ```java > var grid_ = test(view.requestGrid); > > // the button the grid shows on row 1 > var approve = (Button) grid_.getCellComponent(1, "approve"); > test(approve).click(); > > assertTrue(view.requests.get(1).isApproved()); > > // after the row is refreshed, read the cell again to get the new button > view.requestGrid.getListDataView().refreshItem(view.requests.get(1)); > var updated = (Button) grid_.getCellComponent(1, "approve"); > assertEquals("Approved", updated.getText()); > ``` > > For a column that is hidden on purpose, render the cell instead: > > ```java > Button hidden = (Button) grid_.renderCellComponent(1, "hiddenAction"); > ``` > > ## API Changes > > ### com.vaadin.flow.component.grid.GridTester > > ```java > // Added > public Component renderCellComponent(int row, int column) // former getCellComponent behaviour: renders a fresh copy and attaches it to the grid > public Component renderCellComponent(int row, String columnName) // former getCellComponent behaviour; the only way to reach a hidden column's cell > > // Changed > - public Component getCellComponent(int row, int column) // rendered a new component per call and appended it to the grid > + public Component getCellComponent(int row, int column) // returns the component the grid rendered; scrolls the row into view; throws IllegalStateException when the grid rendered none > - public Component getCellComponent(int row, String columnName) // rendered a new component per call and appended it to the grid > + public Component getCellComponent(int row, String columnName) // returns the component the grid rendered; throws IllegalStateException for a hidden column > - public String getCellText(int row, int column) // returned null for a ComponentRenderer cell that rendered no component > + public String getCellText(int row, int column) // reads the grid-rendered component; returns "" instead of null; may scroll or throw IllegalStateException > ``` > > ### com.vaadin.browserless.internal.RenderedComponentSupport > > ```java > // Added > public final class RenderedComponentSupport // for internal use only > public static Component getRenderedComponent(Grid.Column<?> column, String itemKey, BiFunction<Class<?>, String, Field> fieldGetter) > ``` > > ## Test summary > > - Reading the same component cell twice returns one and the same instance, and leaves nothing attached to the grid for `find(...)` to report. > - Refreshing an item replaces the cell's component: the old one is detached and the new one is the attached, rendered instance. > - A row the client has not asked for yet is scrolled into view and stays rendered on later reads. > - A hidden column and a data provider whose items differ across fetches both fail with a clear message that points at `renderCellComponent`, which then returns a component for those cells. > - `renderCellComponent` still renders a new copy per call, attaches it to the grid, and that copy is not the component the grid shows. > - A renderer that returns no component yields the empty text node the grid renders, so the cell text is `""`. Co-authored-by: totally-not-ai[bot] <290682512+totally-not-ai[bot]@users.noreply.github.com>
This was referenced Sep 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
GridTester.getCellComponentused to render the cell again on every call and attach the fresh copy to the grid, so a test never acted on the component the grid actually shows. It now returns the component the grid rendered for that row and column. The old behaviour is still available as the newrenderCellComponent.Fixes #235
What changed
Breaking:
getCellComponent(int, int)andgetCellComponent(int, String)behave differently. This affects every test that reads a component cell.refreshItem(...)).find(...)no longer reports leftover copies. Tests that counted those copies will see fewer components.IllegalStateExceptionfor a cell the grid renders nothing for: a hidden column, or a data provider whose items are not equal across fetches. The message points atrenderCellComponent.getCellTexton aComponentRenderercolumn reads the same rendered component. For a renderer that returns no component it now returns""instead ofnull, because the grid renders an empty text node in that place. It can also throw for the cases listed above.New, additive:
renderCellComponent(int, int)andrenderCellComponent(int, String)keep the old behaviour — render the cell on its own and attach the copy to the grid. Use them for cells the grid does not render, such as those of a hidden column, and to keep older tests working.A column index addresses only visible columns, so a hidden column's cells are reachable only by key, through
renderCellComponent(int, String).README and the
find(...)javadocs were updated to match.Use case
You have a grid with a component column of "Approve" buttons. A test clicks the button on the second row and checks that the row's item was really approved. With the old behaviour the click went to a throwaway copy, so nothing in the grid reacted.
For a column that is hidden on purpose, render the cell instead:
API Changes
com.vaadin.flow.component.grid.GridTester
com.vaadin.browserless.internal.RenderedComponentSupport
Test summary
find(...)to report.renderCellComponent, which then returns a component for those cells.renderCellComponentstill renders a new copy per call, attaches it to the grid, and that copy is not the component the grid shows."".