-
Notifications
You must be signed in to change notification settings - Fork 10.6k
Virtualization supports variable-height items #64964
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
ilonatommy
merged 54 commits into
dotnet:main
from
ilonatommy:fix-25058-variable-height
Mar 20, 2026
Merged
Changes from all commits
Commits
Show all changes
54 commits
Select commit
Hold shift + click to select a range
e47d67c
feat(Virtualize): Support variable-height items
ilonatommy c939f0e
test(Virtualize): Add unit tests for variable-height support
ilonatommy c47f845
test(Virtualize): Add comprehensive E2E tests for variable-height items
ilonatommy f01b85c
Update src/Components/test/testassets/BasicTestApp/VirtualizationVari…
ilonatommy 83f5724
Update src/Components/test/E2ETest/Tests/VirtualizationTest.cs
ilonatommy f475bc3
Update src/Components/test/E2ETest/Tests/VirtualizationTest.cs
ilonatommy 1701a0e
Feedback: clean tests, detect backwards movements.
ilonatommy 19e573d
Feedback: 0 division and wrapper for virtualzation items so that heig…
ilonatommy 545e5ab
Fix https://github.com/dotnet/aspnetcore/issues/64029 and https://git…
ilonatommy db76114
Increase the lenght of list in jump tests to 1k + increase them to 20…
ilonatommy 2c08839
Additional optimizations.
ilonatommy e83bbb0
Simplify the comment.
ilonatommy 2f9c441
Stricker tests (do not retry jumping on failure) + scroll adjustment …
ilonatommy 587dc21
Clean up redundant comments.
ilonatommy aad16d3
Only WASM scrolling tests can be deterministic.
ilonatommy cca6a87
Try fixing the jump and scrolling.
ilonatommy 257e016
Stricter QuickGrid jump test.
ilonatommy d75e37a
Tests wait longer.
ilonatommy 7c2b442
Clean comments, refactor code.
ilonatommy d4fe9c8
Add logging to debug CI-only failures.
ilonatommy a30119b
On CI: Loaded items move spacer out of area that we check for interse…
ilonatommy a059c39
Fix CI: differenciate seeing spacer caused by scrolling and caused by…
ilonatommy 0d52ca4
Another attempt.
ilonatommy 618c4eb
Ugly fix after reproducing the CI problem with Linux.
ilonatommy aa7672b
Cleanup.
ilonatommy 033f666
More cleanup.
ilonatommy 6896fec
Rename to clarify.
ilonatommy f56eaac
Better test coverage.
ilonatommy 24b8380
Try enabling the problematic test on server.
ilonatommy bd1087b
Merge branch 'main' into fix-25058-variable-height
ilonatommy 34ccefd
Test jump to home with async load, symmetric to jump to end.
ilonatommy 8d69bb4
Increase unit tests coverage.
ilonatommy 65872fb
Merge branch 'fix-25058-variable-height' of https://github.com/ilonat…
ilonatommy d8fcfc3
More test coverage.
ilonatommy 2238e57
Feedback: document breaking change.
ilonatommy 3d6583f
Feedback: nested virtualization has better test coverage.
ilonatommy 92ecf38
Merge remote-tracking branch 'origin/main' into fix-25058-variable-he…
ilonatommy 96f43fd
Add test for measurements pollution with NaN and similar.
ilonatommy f1e8270
Feedback: update ambigious comment.
ilonatommy bea9654
Merge remote-tracking branch 'upstream/main' into fix-25058-variable-…
ilonatommy 0738909
Feedback: use `ItemsProviderCallCount` without interlocked.
ilonatommy 569c4be
Feedback: description for complex razor files.
ilonatommy 3574fad
Feedback: attribute sequence indexing starts at 0.
ilonatommy 5b12a88
Feedback: replace wrapper element with wrapper comment to avoid break…
ilonatommy 58fc1d5
Fix JS tests.
ilonatommy 5ec2177
Feedback: optimize C# -> JS communication costs.
ilonatommy c1accfd
Wrapper auto-provided `tr` but with comment approach we have to corre…
ilonatommy a1b9614
Revert https://github.com/dotnet/aspnetcore/pull/64964/commits/5b12a8…
ilonatommy 6c68072
Missing change for the revert.
ilonatommy 4aabc44
Fix `QuickGridTest.ItemsProviderCalledOnceWithVirtualize`: Avoid redu…
ilonatommy a0dd443
Fix failing Flashing tests and dual item provider call tests.
ilonatommy 4e7a9b1
Remove breaking change: use comments instead of wraper element.
ilonatommy 29b1148
Update Virtualize.test.ts for comment-based rendering
ilonatommy 8dd290c
Merge branch 'main' into fix-25058-variable-height
ilonatommy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| import { expect, test, describe, beforeEach, afterEach } from '@jest/globals'; | ||
| import { Virtualize } from '../src/Virtualize'; | ||
|
|
||
| const { measureRenderedItems } = Virtualize; | ||
|
|
||
| function createDOM(heights: number[]): { before: HTMLDivElement; after: HTMLDivElement } { | ||
| const container = document.createElement('div'); | ||
| document.body.appendChild(container); | ||
| const before = document.createElement('div'); | ||
| const after = document.createElement('div'); | ||
| container.appendChild(before); | ||
|
|
||
| // Build N+1 fence: comment, item, comment, item, ..., comment | ||
| for (const h of heights) { | ||
| const comment = document.createComment('virtualize:item'); | ||
| container.appendChild(comment); | ||
| const item = document.createElement('div'); | ||
| item.getBoundingClientRect = () => ({ | ||
| height: h, width: 100, top: 0, left: 0, bottom: h, right: 100, | ||
| x: 0, y: 0, toJSON() { return this; }, | ||
| }); | ||
| container.appendChild(item); | ||
| } | ||
| if (heights.length > 0) { | ||
| container.appendChild(document.createComment('virtualize:item')); | ||
| } | ||
|
|
||
| container.appendChild(after); | ||
|
|
||
| // jsdom doesn't implement Range.getBoundingClientRect. | ||
| // Patch createRange to return a mock that sums item heights between start and end. | ||
| const origCreateRange = document.createRange.bind(document); | ||
| document.createRange = () => { | ||
| const range = origCreateRange(); | ||
| let startNode: Node | null = null; | ||
| const origSetStartAfter = range.setStartAfter.bind(range); | ||
| const origSetEndBefore = range.setEndBefore.bind(range); | ||
| range.setStartAfter = (node: Node) => { startNode = node; origSetStartAfter(node); }; | ||
| range.setEndBefore = (node: Node) => { | ||
| origSetEndBefore(node); | ||
| // Sum heights of element children between startNode and node | ||
| let totalHeight = 0; | ||
| if (startNode) { | ||
| for (let n = startNode.nextSibling; n && n !== node; n = n.nextSibling) { | ||
| if (n instanceof HTMLElement && n.getBoundingClientRect) { | ||
| totalHeight += n.getBoundingClientRect().height; | ||
| } | ||
| } | ||
| } | ||
| range.getBoundingClientRect = () => ({ | ||
| height: totalHeight, width: 100, top: 0, left: 0, bottom: totalHeight, right: 100, | ||
| x: 0, y: 0, toJSON() { return this; }, | ||
| } as DOMRect); | ||
| }; | ||
| return range; | ||
| }; | ||
|
|
||
| return { before, after }; | ||
| } | ||
|
|
||
| describe('measureRenderedItems', () => { | ||
| test('returns aggregated sum and count for valid items', () => { | ||
| const { before, after } = createDOM([40, 60]); | ||
| const result = measureRenderedItems(before, after); | ||
| expect(result.heightSum).toBe(100); | ||
| expect(result.heightCount).toBe(2); | ||
| }); | ||
|
|
||
| test('includes zero-height items in count', () => { | ||
| const { before, after } = createDOM([50, 0, 30]); | ||
| const result = measureRenderedItems(before, after); | ||
| // Total height is 80 (50+0+30), all 3 items counted | ||
| expect(result.heightSum).toBe(80); | ||
| expect(result.heightCount).toBe(3); | ||
| }); | ||
|
|
||
| test('returns zero for empty item list', () => { | ||
| const { before, after } = createDOM([]); | ||
| const result = measureRenderedItems(before, after); | ||
| expect(result.heightSum).toBe(0); | ||
| expect(result.heightCount).toBe(0); | ||
| }); | ||
|
|
||
| test('returns zero for single item (needs at least 2 delimiters)', () => { | ||
| // Single item has 2 delimiters which is the minimum | ||
| const { before, after } = createDOM([42]); | ||
| const result = measureRenderedItems(before, after); | ||
| expect(result.heightSum).toBe(42); | ||
| expect(result.heightCount).toBe(1); | ||
| }); | ||
| }); |
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
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
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.