Add InitialIndex parameter and ScrollToIndexAsync API to Virtualize<TItem>#66753
Add InitialIndex parameter and ScrollToIndexAsync API to Virtualize<TItem>#66753ilonatommy wants to merge 11 commits into
Virtualize<TItem>#66753Conversation
…to an arbitrary item index, plus a parameter for opening the list at a target index on first render.
There was a problem hiding this comment.
Pull request overview
This PR extends the Blazor Virtualize<TItem> component with two new public APIs—InitialIndex and ScrollToIndexAsync—to allow consumers to control the initial and subsequent scroll position of a virtualized list, including updated JS interop support and E2E coverage.
Changes:
- Adds
InitialIndexparameter andScrollToIndexAsync(int, CancellationToken)API toVirtualize<TItem>, including render-commit coordination and “last call wins” cancellation semantics. - Extends Virtualize JS interop with an
alignToItemfunction and corresponding C# interop wrapper. - Adds unit tests, BasicTestApp UI hooks, and E2E tests covering initial index and programmatic scrolling scenarios.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Components/Web/src/Virtualization/Virtualize.cs | Implements InitialIndex and ScrollToIndexAsync, including window movement, render rendezvous, and anchor/IO suppression during active scroll. |
| src/Components/Web/src/Virtualization/VirtualizeJsInterop.cs | Adds C# JS interop entry point for aligning to an item. |
| src/Components/Web.JS/src/Virtualize.ts | Adds JS alignToItem implementation and refactors anchor-restore measurement helper. |
| src/Components/Web/src/PublicAPI.Unshipped.txt | Declares the new public API surface for Virtualize<TItem>. |
| src/Components/Web/test/Virtualization/VirtualizeTest.cs | Adds unit tests for pre-init behavior, clamping, and cancellation behavior. |
| src/Components/test/testassets/BasicTestApp/VirtualizationAnchorMode.razor | Adds UI controls and handlers to exercise InitialIndex/ScrollToIndexAsync in the test app. |
| src/Components/test/testassets/BasicTestApp/VirtualizationAnchorModeWindowScroll.razor | Adds UI controls/handlers for window-scroll scenarios. |
| src/Components/test/E2ETest/Tests/VirtualizationTest.cs | Adds E2E coverage for ScrollToIndexAsync and InitialIndex behaviors (fixed/variable height, clamping, rapid calls, cancellation, window scroll). |
Comments suppressed due to low confidence (1)
src/Components/Web/test/Virtualization/VirtualizeTest.cs:1000
- This test only asserts the returned Task is non-null, but it doesn't await it. If
ScrollToIndexAsyncfaults asynchronously, the test would still pass and may leave unobserved exceptions. Consider awaiting the Task (optionally with a timeout) to validate successful completion.
Task task = null;
await renderer.Dispatcher.InvokeAsync(() => { task = virtualize.ScrollToIndexAsync(99_999); });
Assert.NotNull(task);
}
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/Components/Web/src/Virtualization/Virtualize.cs:629
- Same issue as
OnBeforeSpacerVisible: ignoringOnAfterSpacerVisiblewhile_currentScrollCtsis non-null blocks virtualization window updates during a long-runningScrollToIndexAsync, which can conflict with the documented behavior that user scroll should win. Prefer canceling the scroll operation or allowing these callbacks through (and treating them as user override) rather than returning early for the whole scroll duration.
void IVirtualizeJsCallbacks.OnAfterSpacerVisible(float spacerSize, float spacerSeparation, float containerSize)
{
if (_pendingAnchorRestore || _currentScrollCts is not null)
{
return;
}
…after first render.
dariatiurina
left a comment
There was a problem hiding this comment.
I have only one question, but other than that and Ondrej's notes, everything looks good.
Adds two pieces of public API to
Virtualize<TItem>that let consumers control which item is visible at the top of the list:InitialIndexparameter — opens the list at a given item on first interactive render.ScrollToIndexAsync(int itemIndex, CancellationToken cancellationToken = default)— programmatically scrolls to an item at any time after first render.Out-of-range values are silently clamped to the valid range.
Public API
Behavior
InitialIndex = 500set before first renderInitialIndex<OverscanCount(e.g., 1, 5)InitialIndex = 0(default)InitialIndexout of rangeInitialIndexchanged at runtimeInitialIndexis a one-shot, not a live bindingScrollToIndexAsync(itemIndex)Taskthat completes when the target is aligned to the start of the viewportTaskfaults withOperationCanceledExceptionScrollToIndexAsynccall while first is in flightTaskcompletes normally (silently superseded); only the last call's target is honored ("last call wins")InvalidOperationExceptionsynchronously (not on the returned Task), with a message pointing atInitialIndexFixes #26943 ,
Fixes #66328