Skip to content

Fix QuickGrid Start-mode async-provider prepend viewport drift. - #67935

Merged
ilonatommy merged 1 commit into
dotnet:mainfrom
ilonatommy:fix-quick-grid-start-anchoring-tests
Jul 21, 2026
Merged

Fix QuickGrid Start-mode async-provider prepend viewport drift.#67935
ilonatommy merged 1 commit into
dotnet:mainfrom
ilonatommy:fix-quick-grid-start-anchoring-tests

Conversation

@ilonatommy

@ilonatommy ilonatommy commented Jul 21, 2026

Copy link
Copy Markdown
Member

Follow-up to #67931 (which fixed the same problem in  AnchorMode.None ). Extends the fix to AnchorMode.Start when items are prepended to QuickGrid with async ItemsProvider. Contributes to #67865.

The problem

QuickGrid renders as a <table>, and browser-native scroll anchoring is unreliable inside table layout, so Virtualize falls back to manual JS scroll compensation. On Blazor Server, restoring that anchor is an async round-trip. During the latency after a prepend, two things went wrong in Start mode:

  1. An anchor was captured while data was still loading. The topmost rendered row was sitting in a placeholder gap below the container top, so the restore computed the wrong scroll offset.
  2. Premature scroll callback undid the shift.. The reserved height had already grown but the compensating scroll had not landed yet, so a stray spacer-before observer callback recomputed a smaller window and undid the shift (the visible row jumped, e.g. index 90 -> 80).

WASM was unaffected because the restore there is synchronous.

The fix

Extend the anchor-restore deferral (already used for None) to Start mode (but only when the prepend did not happen at the very top):

•  Virtualize.cs : keep _pendingAnchorRestore set across the RestoreAnchorAsync round-trip for a Start-mode prepend that started away from the top, so the premature callback can't revert it. A new flag (_deferPrependAnchorClear) carries the "was at top?" signal from prepend time to render time, where it's otherwise no longer available.
•  Virtualize.ts : skip the transient loading-gap anchor snapshot for Start too, guarded by !convergingToTop.

Why "not at top" matters: at the very top, Start mode must converge back to the new first item (show the freshly prepended rows), so that path is deliberately left untouched. Only once the viewport has left the top does Start need to anchor the current row (which is where the drift occurred).

New QuickGrid AnchorMode test coverage summary, updated after #67931
Op · Position · Source None Start End
⬆️ · Exact top · Items V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5
⬆️ · Exact top · Prov V ✅ · QG ✅ 0/5 (fixed) V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ❌ 1/5
⬆️ · Exact top · Prov+delay V n/a · QG ✅ 0/5 (fixed) V n/a · QG 🆕 ✅ 0/5 V n/a · QG 🆕 ❌ 4/5
⬆️ · Near-top ~200px · Items V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5
⬆️ · Near-top ~200px · Prov V ✅ · QG ✅ 0/5 (fixed) V ✅ · QG ✅ 0/5 (fixed†) V ✅ · QG 🆕 ❌ 1/5
⬆️ · Near-top ~200px · Prov+delay V n/a · QG ✅ 0/5 (fixed) V n/a · QG ✅ 0/5 (fixed) V n/a · QG 🆕 ❌ 4/5
⬆️ · Mid-list · Items V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5
⬆️ · Mid-list · Prov V ✅ · QG ✅ 0/5 (fixed) V ✅ · QG ✅ 0/5 (fixed) V ✅ · QG 🆕 ❌ 4/5
⬆️ · Mid-list · Prov+delay V ✅ · QG ✅ 0/5 (fixed, None_AsyncProvider [Fact])
⬆️ · Bottom · Items V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5
⬆️ · Bottom · Prov V ✅ · QG ✅ 0/5 (fixed) V ✅ · QG ✅ 0/5 (fixed) V ✅ · QG 🆕 ❌ 1/5
⬆️ · After leaving top · Items V ✅ · QG 🆕 ✅ 0/5
⬆️ · After leaving top · Prov V ✅ · QG 🆕 ✅ 0/5
⬇️ · Near-top · Items V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5
⬇️ · Near-top · Prov V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5
⬇️ · Top · Items V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5
⬇️ · Top · Prov V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ❌ 1/6
⬇️ · Mid-list · Items V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5
⬇️ · Mid-list · Prov V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5
⬇️ · Bottom · Items V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5
⬇️ · Bottom · Prov V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5
⬇️ · After leaving bottom · Items V ✅ · QG 🆕 ✅ 0/5
⬇️ · After leaving bottom · Prov V ✅ · QG 🆕 ✅ 0/5
⬇️ LARGE (100) · Bottom · Items V ✅ · QG 🆕 ❌ WASM V ✅ · QG 🆕 ✅ 0/5
⬇️ LARGE (100) · Bottom · Prov V ✅ · QG 🆕 ✅ 0/5 V ✅ · QG 🆕 ✅ 0/5
⌨️ Home key → top · Items/Prov — · QG 🆕 ✅ 0/5 — · QG 🆕 ✅ 0/5 — · QG 🆕 ✅ 0/5
⌨️ End key → bottom · Items/Prov — · QG 🆕 ✅ 0/5 — · QG 🆕 ✅ 0/5 — · QG 🆕 ❌ 1/6 (CI/Mono)

@ilonatommy ilonatommy added this to the 11.0-preview7 milestone Jul 21, 2026
@ilonatommy ilonatommy self-assigned this Jul 21, 2026
Copilot AI review requested due to automatic review settings July 21, 2026 11:55
@ilonatommy ilonatommy added area-blazor Includes: Blazor, Razor Components feature-blazor-virtualization This issue is related to the Blazor Virtualize component labels Jul 21, 2026
@ilonatommy
ilonatommy requested a review from a team as a code owner July 21, 2026 11:55
@ilonatommy
ilonatommy temporarily deployed to copilot-pat-pool July 21, 2026 11:55 — with GitHub Actions Inactive
@ilonatommy
ilonatommy temporarily deployed to copilot-pat-pool July 21, 2026 11:55 — with GitHub Actions Inactive

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Extends the prior QuickGrid/Virtalize async-provider prepend anchoring fix (previously scoped to AnchorMode.None) to also cover AnchorMode.Start when the viewport is not at the very top, preventing viewport “drift” during the async anchor-restore round-trip (notably on Blazor Server).

Changes:

  • Virtualize.cs: defers clearing _pendingAnchorRestore across RestoreAnchorAsync for Start-mode prepends that began away from the top (via _deferPrependAnchorClear).
  • Virtualize.ts: avoids overwriting a stable anchor snapshot with a transient “loading gap” snapshot for Start mode when not converging to top.
  • VirtualizationTest.cs: re-enables previously-disabled Start-mode async-provider E2E permutations now covered by the fix.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/Components/Web/src/Virtualization/Virtualize.cs Holds anchor-restore suppression across the async restore for Start-mode prepends away from top, preventing premature spacer callbacks from undoing the shift.
src/Components/Web.JS/src/Virtualize.ts Skips transient mid-load anchor snapshots for Start mode (when not converging), matching the earlier None-mode behavior.
src/Components/test/E2ETest/Tests/VirtualizationTest.cs Re-enables Start-mode async-provider test cases that were disabled due to the previously observed drift.

@ilonatommy
ilonatommy merged commit 06ed003 into dotnet:main Jul 21, 2026
39 of 42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-blazor Includes: Blazor, Razor Components feature-blazor-quickgrid feature-blazor-virtualization This issue is related to the Blazor Virtualize component

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants