-
Notifications
You must be signed in to change notification settings - Fork 2.7k
test(cli): cover bottom-stuck virtualized list behavior #7652
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
Changes from 1 commit
8180d13
3c0ea5e
30c5a99
018707e
aeba884
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -490,9 +490,28 @@ function VirtualizedList<T>( | |||||||||||||||||||||||||||
| endIndexOffsetRaw >= offsets.length | ||||||||||||||||||||||||||||
| ? data.length - 1 | ||||||||||||||||||||||||||||
| : Math.min(data.length - 1, endIndexOffsetRaw); | ||||||||||||||||||||||||||||
| const renderStartIndex = (() => { | ||||||||||||||||||||||||||||
| if (!isStickingToBottom || renderStatic === true || endIndex < 0) { | ||||||||||||||||||||||||||||
| return startIndex; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] The backward walk recomputes cumulative item heights using Concrete cost: two independent code paths compute per-item heights with identical coercion. If a future change adjusts the coercion in one place but not the other (e.g., adding a minimum-height clamp to
Suggested change
— qwen3.7-max via Qwen Code /review |
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| let heightFromEnd = 0; | ||||||||||||||||||||||||||||
| for (let i = endIndex; i >= 0; i--) { | ||||||||||||||||||||||||||||
| const item = data[i]; | ||||||||||||||||||||||||||||
| const key = item ? keyExtractor(item, i) : ''; | ||||||||||||||||||||||||||||
| const raw = heights[key] ?? estimatedItemHeight(i); | ||||||||||||||||||||||||||||
| const height = Number.isFinite(raw) && raw > 0 ? raw : 0; | ||||||||||||||||||||||||||||
| heightFromEnd += height; | ||||||||||||||||||||||||||||
| if (heightFromEnd >= viewHeightForEndIndex) { | ||||||||||||||||||||||||||||
| return Math.min(startIndex, Math.max(0, i - 1)); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return 0; | ||||||||||||||||||||||||||||
| })(); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const topSpacerHeight = | ||||||||||||||||||||||||||||
| renderStatic === true ? 0 : (offsets[startIndex] ?? 0); | ||||||||||||||||||||||||||||
| renderStatic === true ? 0 : (offsets[renderStartIndex] ?? 0); | ||||||||||||||||||||||||||||
| const bottomSpacerHeight = renderStatic | ||||||||||||||||||||||||||||
| ? 0 | ||||||||||||||||||||||||||||
| : totalHeight - (offsets[endIndex + 1] ?? totalHeight); | ||||||||||||||||||||||||||||
|
|
@@ -512,8 +531,24 @@ function VirtualizedList<T>( | |||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const renderRangeStart = renderStatic ? 0 : startIndex; | ||||||||||||||||||||||||||||
| const renderRangeStart = renderStatic ? 0 : renderStartIndex; | ||||||||||||||||||||||||||||
| const renderRangeEnd = renderStatic ? data.length - 1 : endIndex; | ||||||||||||||||||||||||||||
| const measuredRenderedHeight = (() => { | ||||||||||||||||||||||||||||
| if (!isStickingToBottom || renderStatic === true || renderRangeEnd < 0) { | ||||||||||||||||||||||||||||
| return undefined; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| let total = 0; | ||||||||||||||||||||||||||||
| for (let i = renderRangeStart; i <= renderRangeEnd; i++) { | ||||||||||||||||||||||||||||
| const item = data[i]; | ||||||||||||||||||||||||||||
| if (!item) return undefined; | ||||||||||||||||||||||||||||
| const measured = heights[keyExtractor(item, i)]; | ||||||||||||||||||||||||||||
| if (measured === undefined) return undefined; | ||||||||||||||||||||||||||||
| total += measured; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return total; | ||||||||||||||||||||||||||||
| })(); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const renderedItems = useMemo(() => { | ||||||||||||||||||||||||||||
| if (!isReady) { | ||||||||||||||||||||||||||||
|
|
@@ -883,7 +918,13 @@ function VirtualizedList<T>( | |||||||||||||||||||||||||||
| // the full `containerHeight`, so scrolling is unaffected. | ||||||||||||||||||||||||||||
| const rootHeight = | ||||||||||||||||||||||||||||
| props.containerHeight !== undefined | ||||||||||||||||||||||||||||
| ? Math.min(props.containerHeight, totalHeight) | ||||||||||||||||||||||||||||
| ? Math.min( | ||||||||||||||||||||||||||||
| props.containerHeight, | ||||||||||||||||||||||||||||
| measuredRenderedHeight !== undefined && | ||||||||||||||||||||||||||||
| measuredRenderedHeight < props.containerHeight | ||||||||||||||||||||||||||||
| ? measuredRenderedHeight | ||||||||||||||||||||||||||||
| : totalHeight, | ||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] The In the new test, after convergence Failure scenario: a bug in this branch (e.g. collapsing when it shouldn't, or computing wrong height) goes undetected — the user-visible result would be either a blank gap below short content (the original bug regressing) or content clipped too short. Consider adding a test with few items in a large container — e.g. 5 items with — qwen3.7-max via Qwen Code /review
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] This PR links Concrete cost: The — qwen3.7-max via Qwen Code /review
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Updated the PR description based on the latest review feedback: removed the \Fixes https://github.com/QwenLM/qwen-code/issues/7485\ auto-closing linkage, reframed the change as a defensive viewport-collapse improvement for the virtualized path, and explicitly noted that this PR does not claim a current-main runtime reproduction for the originally related report. |
||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||
| : '100%'; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Suggestion] The regression test passes without the PR's source changes — verified by running the full test suite against the pre-PR
VirtualizedList.tsx. With 20 items,tallEstimate = () => 4,containerHeight = 20, and 10 rerenders, the natural iterative measurement convergence completes fully (~5–6 iterations), so all items render regardless of whether therenderStartIndex/measuredRenderedHeightcollapse mechanism exists.Concrete cost: a future regression in the collapse logic (e.g., an off-by-one in
i - 1, or a wrong early-return condition) would ship undetected because this test provides no regression safety net for the code paths it claims to exercise.Consider reducing the rerender count to 2–3 and asserting
frame.endsWith('item-19')at that point (which requires the collapse mechanism since convergence hasn't finished), or adding a separate test with 50+ items where the virtualization window never renders all items and the collapse path is the only way to pass.— qwen3.7-max via Qwen Code /review