Skip to content

Reject stale Blazor Virtualize viewport measurements - #68691

Draft
PureWeen with Copilot wants to merge 20 commits into
mainfrom
copilot/fix-stale-viewport-measurements
Draft

Reject stale Blazor Virtualize viewport measurements#68691
PureWeen with Copilot wants to merge 20 commits into
mainfrom
copilot/fix-stale-viewport-measurements

Conversation

Copilot AI commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Reject stale Blazor Virtualize viewport measurements

  • You've read the Contributor Guide and Code of Conduct.
  • You've included unit or integration tests for your change, where applicable.
  • You've included inline docs for your change, where applicable.
  • There's an open issue for the PR that you are making. If you'd like to propose a new feature or change, please open an issue to discuss the change or find an existing issue.

Reject viewport measurements owned by an older rendered window

Description

Resize exposes stale geometry produced during near-end initial alignment: an asynchronous measurement for an older item slice can recalibrate and redistribute a newer slice. This change prevents cross-render measurement use; it does not expand Virtualize’s resize-anchor contract.

  • Rendered-window ownership

    • Assigns each committed window a monotonic version alongside its rendered-item count.
    • Stamps the same version on both spacer elements.
    • Uses version identity instead of item count, avoiding equal-count ABA races.
  • All-producer propagation

    • Captures geometry and ownership atomically in JavaScript.
    • Carries ownership through synchronous and deferred alignment results and both spacer observers.
    • Rejects mismatched results before measurement accumulation, item-size recalibration, or redistribution.
    if (result.RenderedWindowVersion != _renderedWindowVersion)
    {
        return;
    }
  • Regression coverage

    • Adds focused alignment and spacer-callback tests, including equal-count ABA and current-version behavior.
    • Adds an Interactive Server Chromium scenario with 2,000 fixed 50px items, InitialItemIndex=1990, and three bidirectional resize cycles.
  • Evidence

    • Current main de8ed19463ed91e5dc00824dc1b959d19abcb252: three red runs retained item 1950 but produced corrupted scroll heights of 96,879px, 99,692px, and 99,581px instead of 100,000px.
    • Fixed commit 638248695dd8e8c55399976d6622ebb1792abc69: the identical browser assertion passed once.
    • Focused ownership tests passed. The full VirtualizeTest run passed 42/43; the existing cancellation test timed out once and passed immediately in isolation.
    • Additional repeated green runs and adjacent ItemsProvider, variable-height, and QuickGrid controls remain pending, so this PR should remain draft.

Follow-up to #67936 and the empirical report.

Original prompt

Create and open a follow-up pull request in dotnet/aspnetcore that fixes stale asynchronous viewport measurements in Blazor Virtualize after merged PR #67936.

Repository and process requirements

  • Work from the current default branch of dotnet/aspnetcore.
  • Read and follow src/Components/AGENTS.md and .github/instructions/components.instructions.md before editing.
  • Keep the change narrowly scoped to Components virtualization.
  • Do not change global.json, package.json, package-lock.json, or NuGet.config.
  • Do not add public API, InternalsVisibleTo, or UnsafeAccessor.
  • Source activate.sh from the repository root before running dotnet commands.
  • Use the existing Components build/test infrastructure. Do not add new build or test tools.
  • Open a pull request when the implementation and targeted validation are complete. If faithful browser validation is genuinely blocked, leave the PR as a draft and document the exact blocker instead of claiming success.

Background

PR #67936, "Fix InitialItemIndex viewport underfill for small items in big container or on window resize," merged as commit fde48e9 from head 944cbbd.

The merged change fixes the original near-end viewport underfill, but it exposes a deterministic stale-measurement race during initial alignment.

Frozen real-browser repro from the exact merged head:

  • Interactive Server Chromium path
  • 2,000 fixed-height items
  • ItemSize=50
  • InitialItemIndex=1990
  • default OverscanCount and MaxItemCount
  • self-scrolling container initially 2,500px tall
  • expected first visible item after near-end clamping: 1950

Observed on exact head 944cbbd:

  • Initial first-visible item is 1950, but scrollHeight is only 58,790px rather than approximately 100,000px.
  • On the first 2,500px to 2,000px resize, first-visible moves from 1950 to 1362 and scrollHeight changes to 83,175px.
  • The incorrect position and geometry persist through three 2,000px to 2,500px resize cycles.
  • The browser console contains no errors.
  • A paired InitialItemIndex=1950 control remains at 1950 on the current head, demonstrating that the trigger is the near-end initial-alignment growth path rather than every resize.

Instrumented root cause

  1. JavaScript measures a committed 25-item rendered window with 1,250px spacer separation.
  2. The measurement is dispatched asynchronously.
  3. Before .NET consumes it, pending near-end viewport growth commits a newer 62-item rendered window.
  4. Managed code consumes the old 1,250px geometry while _lastRenderedItemCount is already 62.
  5. Recalibration changes the estimated item size from 50px to approximately 20.161291px.
  6. That corrupts the spacer geometry used by the next redistribution. Resize is only the stimulus that makes the earlier corruption visible.

This is not a request to establish a new general contract that an exact scroll anchor must survive arbitrary container resizing. The correctness requirement is that geometry measured for an older rendered slice must never recalibrate or redistribute a newer rendered slice.

Required implementation invariant

Implement explicit all-producer rendered-window ownership:

  1. Give each committed rendered window a monotonic identity/version.
  2. Assign that identity and _lastRenderedItemCount as part of the same render/window commit.
  3. Expose the same committed identity on both spacer DOM elements.
  4. Capture geometry and rendered-window identity atomically in JavaScript.
  5. Carry the identity through every asynchronous geometry producer:
    • synchronous alignToItem results,
    • deferred/pending alignment completion callbacks,
    • before-spacer IntersectionObserver callbacks,
    • after-spacer IntersectionObserver callbacks.
  6. On the managed side, reject a result before item-size recalibration, current-item-count use, or redistribution when its identity does not equal the latest committed rendered-window identity.
  7. A resize that occurs without a component rerender must retain the same identity and remain accepted.
  8. Keep all new types and interop details internal.

A locally validated candidate on the pre-merge head used this approximate shape. Treat this as design evidence, not a patch to apply blindly. Reconcile it with current main and improve it if needed:

  • Virtualize.ts defined an internal AlignmentResult payload containing fillDirection, spacerSeparation, containerSize, and renderedWindowVersion.
  • Both spacer elements carried data-blazor-virtualize-rendered-window-version.
  • A JS measurement helper read the version from the committed spacer DOM and returned spacer separation, container size, and version in one payload.
  • alignToItem and alignToItemAt returned that payload instead of only ViewportFillDirection.
  • If pending alignment completed after the original JS call returned, JS invoked an internal OnAlignmentCompleted callback with the same payload.
  • Both IntersectionObserver callbacks sent...

cincuranet and others added 4 commits August 21, 2026 17:00
)

* Fix h3 connection-level and stream-level abort locking

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix stale asynchronous viewport measurements in Blazor Virtualize Reject stale Blazor Virtualize viewport measurements Aug 21, 2026
Copilot AI requested a review from PureWeen August 21, 2026 15:48
dependabot Bot added 2 commits August 21, 2026 09:59
…e-base.yml (#68685)

Bumps [dotnet/arcade/.github/workflows/inter-branch-merge-base.yml](https://github.com/dotnet/arcade) from acdb3e708ba600e766667825c84f9fa4a49e6c8f to 1353cab671305cff0ae5afc0d96ff3d03f239e0c.
- [Commits](dotnet/arcade@acdb3e7...1353cab)

---
updated-dependencies:
- dependency-name: dotnet/arcade/.github/workflows/inter-branch-merge-base.yml
  dependency-version: 1353cab671305cff0ae5afc0d96ff3d03f239e0c
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…#68686)

Bumps [dotnet/arcade/.github/workflows/backport-base.yml](https://github.com/dotnet/arcade) from acdb3e708ba600e766667825c84f9fa4a49e6c8f to 1353cab671305cff0ae5afc0d96ff3d03f239e0c.
- [Commits](dotnet/arcade@acdb3e7...1353cab)

---
updated-dependencies:
- dependency-name: dotnet/arcade/.github/workflows/backport-base.yml
  dependency-version: 1353cab671305cff0ae5afc0d96ff3d03f239e0c
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
@PureWeen

Copy link
Copy Markdown
Member

@copilot please keep working on this PR. The rendered-window ownership direction looks right, but keep it draft until these are addressed:

  1. Guard deferred OnAlignmentCompleted so it only acts while _initialIndex.Phase == InitialIndexPhase.Pending. A user scroll can abort initial alignment while JS still has pendingAlignLocalIndex; a later callback with the current rendered-window version must not redistribute after that cancellation. Add a focused test for this abort-then-deferred-callback case.

  2. Remove RenderedWindowVersion from IVirtualizeJsCallbacks and remove VirtualizeJsCallbacksTestExtensions.cs unless you find a production reason for that property. It appears to exist only for test convenience. The tests can pass virtualize._renderedWindowVersion explicitly.

  3. Complete the browser proof promised in the original prompt. Run the identical fixed-size assertion green at least three times, then run the InitialItemIndex=1950 control, ItemsProvider, variable-height, and QuickGrid shared-path controls. Preserve exact commands and observables in the PR body. Do not weaken the item-950 QuickGrid assertion.

  4. Investigate the failed CoreCLR E2E leg. It is currently the existing QuickGrid_InitialIndex_TallContainer_NearEnd_FillsViewportWithoutUserScroll(useProvider: True) item-950 failure. It may be the known flake, but because this PR changes the same path, rerun it and distinguish a baseline flake from a regression before calling the PR ready.

  5. Update the branch against current main so GitHub recalculates the diff without merged Kestrel PR Fix h3 connection-level and stream-level abort locking #68102. The Components-only diff against live main is the intended change set.

Please retain the all-producer ownership invariant: capture geometry plus version at measurement time, carry it through synchronous/deferred alignment and both spacer observers, and reject stale results before recalibration or redistribution. Keep the PR draft if any required control or repeated green run remains incomplete.

Copilot AI and others added 6 commits August 21, 2026 17:23
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>

Copilot AI commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please keep working on this PR. The rendered-window ownership direction looks right, but keep it draft until these are addressed:

  1. Guard deferred OnAlignmentCompleted so it only acts whi...

Addressed in 27d516f29b and e9127395a2; the branch is updated to current main (62c4132682) and the diff is Components-only.

Validation:

  • source activate.sh && dotnet test src/Components/Web/test/Microsoft.AspNetCore.Components.Web.Tests.csproj --no-restore --filter "FullyQualifiedName~VirtualizeTest" -v:minimal — 44/44 passed. The new abort/deferred-callback test fails without the guard (state redistributes from items-before 60/capacity 40/version 3 to 20/80/version 4) and passes with it.
  • source activate.sh && for run in 1 2 3; do dotnet test src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj -c Release --no-build --filter "FullyQualifiedName~InitialItemIndex_NearEndResize_DoesNotApplyStaleRenderedWindowMeasurement" /p:UseIisNativeAssets=false -v:minimal || exit 1; done — 3/3 passed with the unchanged item-1950/100,000px assertions.
  • Direct Chromium/Puppeteer query controls ran the same three 2500↔2000px cycles: InitialItemIndex=1950 stayed at first-visible 1950, scrollTop 97,500, scrollHeight 100,000; ItemsProvider did the same; variable-height stayed first-visible 1978, scrollTop 236,432, scrollHeight 238,932. Every state covered both viewport edges and had no page errors.
  • source activate.sh && dotnet test src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj -c Release --no-build --filter "FullyQualifiedName~QuickGrid_InitialIndex_TallContainer_NearEnd_FillsViewportWithoutUserScroll" /p:UseIisNativeAssets=false -v:minimal — the unchanged item-950 assertion reproduced top rendered index=-1, scrollTop=0 in both modes. Main build 1562310 at pre-fix commit fde48e9521 failed the provider case with the identical observable, so this is baseline flakiness rather than a new failure signature. The PR remains draft because this required control is not green.

@PureWeen

Copy link
Copy Markdown
Member

@copilot please keep working on this. I reproduced the current PR head e9127395a2fe5faf525d03f592218346225c66f7 locally with the unchanged filter covering:

  • QuickGrid_InitialIndex_TallContainer_NearEnd_FillsViewportWithoutUserScroll for both provider values
  • InitialIndex_FirstDownwardRedistributionDoesNotMoveBackward

The filter runs both execution classes, so there are six assertions. All six fail on this head. Both QuickGrid cases end at top rendered index=-1, scrollTop=0, and both redistribution cases move backward 509 -> 416.

The baseline evidence is materially different. Build 1562310 at fde48e9521e3db0b49253095ad51d07e385ab47b has the same Virtualize/Web.JS/E2E surfaces as this branch's actual baseline 62c4132682f492ae086e7e5fa0b0a3f2f3b9c58a:

  • CoreCLR: both QuickGrid cases passed and redistribution passed.
  • Mono: five of six passed. Only the already-known Server/provider QuickGrid case failed with -1 / scrollTop=0; both redistribution cases and the non-provider QuickGrid cases passed.

So the current result is a PR regression, not just the existing QuickGrid flake. Please do not weaken, quarantine, or remove these assertions.

The strongest mechanism is in the observer producer. IntersectionObserverEntry geometry is snapshotted by the browser, but measureIntersectionEntry reads the spacer version and spacer separation from the live DOM when the JS callback runs. If a render commits between browser observation and callback delivery, old entry.boundingClientRect / entry.intersectionRect data is combined with the latest DOM version and separation. The managed version equality then accepts a mixed-epoch measurement. That defeats the ownership invariant and is consistent with the 509 -> 416 jump.

Please rework the IntersectionObserver path so every accepted tuple is truly from one committed window. My preferred correction is to treat the native entry as a notification only: in one synchronous block, read both current spacer versions and remeasure the current target rect, current viewport intersection, spacer separation, spacer size, and container size from the live DOM. Use only that current geometry with the current version, and ignore the notification if the target is disconnected or no longer intersects. Do not combine snapshotted entry rects with live DOM ownership. If you choose another mechanism, it must prove that the version was captured at the browser's observation epoch, not merely at callback delivery.

Keep the cancellation-phase guard and all-producer managed checks. Add deterministic coverage for a render occurring between observer sampling and processing; the current managed unit tests cannot catch live-DOM relabeling of an old browser entry.

Before calling this ready:

  1. Run the unchanged three-test filter green across both execution classes. Run the CoreCLR set at least three consecutive times.
  2. Re-run the primary fixed-size near-end resize assertion green 3/3.
  3. Run all focused Virtualize unit tests.
  4. Preserve the known Mono/provider QuickGrid baseline limitation separately rather than expanding this PR to fix it.
  5. Update the PR body with the exact red baseline/current comparison, corrected-head green evidence, and final head SHA. Keep the PR draft until the redistribution regression is gone.

Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
@PureWeen

Copy link
Copy Markdown
Member

@copilot the stale observer correction is the right shape, and the original focused controls are now green locally on d9886e01952450575de3ee2968ffc9f22931b6a5 across three consecutive runs (18/18). But the new live remeasurement introduced a separate window-scroll regression that needs to be fixed before this is ready.

For a window-scrolled Virtualize where scrollContainer === null, scrollElement is document.documentElement. The corrected path calls measureRenderedWindow() without an extent, so containerSize falls back to document.documentElement.getBoundingClientRect().height. With the normal html { height: auto } setup, that is the full document height rather than the viewport height.

I froze one identical Jest assertion and ran it against both heads with document.documentElement.clientHeight = 900 and getBoundingClientRect().height = 31000:

  • Prior head e9127395a2fe5faf525d03f592218346225c66f7: PASS, callback reports containerSize=900.
  • Current head d9886e01952450575de3ee2968ffc9f22931b6a5: FAIL, callback reports containerSize=31000.
  • Current head with a viewport-height correction: PASS, callback returns to containerSize=900.

The identical final assertion SHA-256 was 19f6ad8574b6b3ef18355d1944166133686fc2ea2360200101ed7144b0c626d2. At 50px items with defaults, the bad value changes the source-derived capacity from roughly 48 visible / 0 unused to the 130-item cap / 520 unused, which also activates the loop-breaker transform.

Can you keep the notification-only/live-DOM ownership fix, but derive containerSize from viewport geometry when the scroll root is the window instead of using the document element's content rectangle? Please add a checked-in regression test for the scrollContainer === null case. Preserve the intended root-margin and scale semantics rather than simply reusing the full document rect.

After that, rerun the unchanged redistribution/QuickGrid filter three times and include the window-root test in the focused Jest run. Please also update the PR body with the corrected head and exact evidence before taking it out of draft.

Copilot AI and others added 2 commits August 21, 2026 21:09
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>

Copilot AI commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

@copilot the stale observer correction is the right shape, and the original focused controls are now green locally on d9886e01952450575de3ee2968ffc9f22931b6a5 across three consecutive runs (18/18). ...

Fixed at 2bca9918. Window-root observer measurements now use the current viewport extent (document.documentElement.clientHeight) while preserving the existing scale and root-margin calculations; the checked-in Jest regression reproduces 31000 before the fix and reports 900 after it. Focused Jest is 3/3, Virtualize units are 44/44, the unchanged redistribution/QuickGrid filter is 18/18 across three runs, and the fixed-size near-end assertion is 3/3. The PR body includes the exact commands and final head.

@PureWeen

Copy link
Copy Markdown
Member

@copilot this head is not ready. Components E2E build 1563063 failed on both CoreCLR and Mono, and the failures point to three concrete gaps in the observer rewrite.

First, I need to correct part of my previous guidance. The 31000 document-height result was definitely wrong, but using the raw 900 viewport height also drops the observer root margin. The existing unchanged test makes this deterministic on 2bca99187a8ff2b2cbd10477fb8f4f8a1834b1be:

InitialRender_DispatchesSingleSpacerCallback

  • Expected containerSize: 400
  • Actual: 300

The container is 300px and the observer has 50px root margin on each side. The old entry.rootBounds.height represented the effective root extent, including that margin. Please derive containerSize from the current live viewport plus both root margins, with the existing scale normalization, rather than using either the full document rectangle or the raw viewport extent. Update the new window-root test to assert the equivalent effective root extent, not the raw clientHeight value. Sorry, our previous 900 assertion caught the document-height regression but missed this compatibility requirement.

Second, apply the same effective viewport extent to the alignment producer. alignToItemAt still calls measureRenderedWindow() with no extent. For a window scroll root that falls back to document.documentElement.getBoundingClientRect().height, and ProcessAlignmentResult now uses the returned capacity. Please pass the same current effective root extent through alignToItemAt and add a direct alignToItem window-root regression test.

Third, don't skip the observer state machine when current remeasurement produces no intersecting targets. flushPendingCallbacks currently returns early for an empty measurement array, which bypasses consumeScroll, anchor snapshot bookkeeping, clearing ScrollSource.AlignToItem, and spacer re-observation. Please still run the bookkeeping path with an empty measurement set, and add deterministic coverage for an alignment notification whose targets are no longer intersecting when processed.

These are not theoretical-only failures. Build 1563063 regressed unchanged tests across both runtimes, including:

  • InitialRender_DispatchesSingleSpacerCallback (400 expected, 300 actual)
  • all three AnchorMode_WindowScroll_*_MidList_ViewportStable cases
  • AnchorMode_None/Start/End_MidList_ViewportStable
  • DynamicContent_PrependItemsWhileScrolledToMiddle_VisibleItemsStayInPlace (item 80 moved from Y 234 to 5869)
  • RerendersWhenItemSizeShrinks_Async
  • CancelsOutdatedRefreshes_Async

Please keep the notification-only same-epoch measurement fix, but address all three paths above. Then run the full Components E2E virtualization coverage, not only the six-test filter. The unchanged InitialRender_DispatchesSingleSpacerCallback must return to 400, the window-scroll anchor cases must be green on both CoreCLR and Mono, and the PR body still needs the actual final SHA and red/green evidence before leaving draft.

Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
@PureWeen

Copy link
Copy Markdown
Member

@copilot the latest head fixed the three issues from the last round, but one real Virtualize regression remains. Please keep working on this rather than retrying CI.

On f1c525d67babcb35d879d8a4cca0a4b9d80d2f44, Mono build 1563245 fails only:

VirtualizationTest.CancelsOutdatedRefreshes_Async

It times out after ~39 seconds with cancellation-count still 0. This is not baseline behavior:

  • baseline fde48e9521: passed
  • baseline 62c4132682: passed in 1.15s
  • earlier PR head e9127395: passed in 1.02s
  • 2bca9918: failed with the same timeout/signature
  • f1c525d6: failed with the same timeout/signature
  • the Server variant still passes

The regression starts with the notification-only rewrite. The callback stores only target identities in pendingCallbacks, and throttled callbacks are remeasured up to 50ms later in flushPendingCallbacks. During the test's rapid 1000→5000 scroll sequence, WASM can move past the queued spacer before that flush. measureIntersectionTargets then drops the target because it is no longer intersecting, so no second provider request is issued and the original request is never cancelled.

Please keep the same-epoch fix, but move the live remeasurement to IntersectionObserver callback delivery time rather than the later throttled dispatch. In other words, treat each native entry as a notification only, synchronously read current spacer versions and all current geometry in that callback, then queue the resulting coherent versioned measurement for throttled delivery. Do not use entry.boundingClientRect, entry.intersectionRect, or relabel old entry geometry. If the DOM rerenders after measurement but before dispatch, the managed rendered-window version check should reject it.

This preserves the existing throttle without dropping a notification because the viewport moved again before the timer fired. Please add deterministic coverage where a second observer callback arrives during the throttle window, its target moves before dispatch, and the measurement captured at callback delivery is still dispatched with its captured version. Also rerun CancelsOutdatedRefreshes_Async on Mono repeatedly and the full Virtualize E2E set on both runtimes.

The other red checks on this head are unrelated: the quarantined NavigationLock alert failure is tracked by #66041, and the aspnetcore-ci failure came from an HTTP/3 interop test followed by Helix upload-path noise. The cancellation failure is the one that still blocks this PR.

Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>

Copilot AI commented Aug 22, 2026

Copy link
Copy Markdown
Contributor Author

@copilot the latest head fixed the three issues from the last round, but one real Virtualize regression remains. Please keep working on this rather than retrying CI.

On `f1c525d67babcb35d879d8a4cca0a...

Addressed in d5c11ad6. Observer callbacks now synchronously capture coherent live-DOM measurements and queue those measurements across throttling. The deterministic throttle-window Jest regression passes, CancelsOutdatedRefreshes_Async is green 3/3 on Mono, Web.JS is 253/253, and managed Virtualize units are 44/44. Full Mono Virtualization coverage was 793 passed with one unrelated table-scenario setup failure.

Comment thread src/Components/test/E2ETest/ServerRenderingTests/VirtualizationRenderModesTest.cs Outdated
Comment thread src/Components/test/E2ETest/ServerRenderingTests/VirtualizationRenderModesTest.cs Outdated
@ilonatommy

ilonatommy commented Aug 24, 2026

Copy link
Copy Markdown
Member

The role of AlignToItem return value was before served by RenderedContentMeasurement reason on spacer callbacks. If we are introducing VirtualizeAlignmentResult then RenderedContentMeasurement is becoming redundant.

Edit:
resolved in 5221098.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants