Skip to content

feat(dropdown): 添加滚动容器内锁定功能#7069

Open
Passing-of-A-Dream wants to merge 3 commits into
ant-design:masterfrom
Passing-of-A-Dream:fix/dropdown-scroll-container
Open

feat(dropdown): 添加滚动容器内锁定功能#7069
Passing-of-A-Dream wants to merge 3 commits into
ant-design:masterfrom
Passing-of-A-Dream:fix/dropdown-scroll-container

Conversation

@Passing-of-A-Dream

@Passing-of-A-Dream Passing-of-A-Dream commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

fixed #6606

  • 当 Dropdown 打开时锁定父级滚动容器的滚动

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • 改进下拉菜单的滚动干扰处理:打开时会临时锁定真实滚动父容器的 overflowY,关闭后恢复;多个下拉共享同一滚动父容器时不会提前解锁。
  • Tests

    • 新增/完善滚动锁定测试:覆盖嵌套滚动容器、window 场景(不修改 body 溢出样式),以及多个实例共享时的引用计数解锁与卸载恢复。

- 当 Dropdown 打开时锁定父级滚动容器的滚动
- 添加相关测试用例
@dosubot dosubot Bot added size:S This PR changes 10-29 lines, ignoring generated files. feature labels Jun 23, 2026
@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 597c7d2e-b897-4aa3-b220-215c5d4e8e39

📥 Commits

Reviewing files that changed from the base of the PR and between 7e243c4 and 279b826.

📒 Files selected for processing (2)
  • src/components/dropdown/dropdown.tsx
  • src/components/dropdown/tests/dropdown.test.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/dropdown/tests/dropdown.test.tsx

📝 Walkthrough

Walkthrough

Dropdown 组件中新增滚动容器溢出锁定逻辑:value 变化时,通过 getScrollParent 找到最近的可滚动父容器(排除 window/body/documentElement),使用 WeakMap 维护引用计数式的锁定状态,临时将其 overflow 设为 hidden,清理时计数归零才恢复原始值。新增三组测试用例覆盖单实例、window 排除和多实例共享场景。

Changes

Dropdown 滚动容器 overflow 锁定

Layer / File(s) Summary
导入和数据结构初始化
src/components/dropdown/dropdown.tsx
新增 getScrollParent 导入;创建 scrollLockState WeakMap 记录每个滚动父容器的锁定计数(count)和原始 overflow 值(originalOverflow);实现 getLockTarget 函数,优先查找祖先链中已缓存的锁目标,未命中则调用 getScrollParent 并过滤不应锁定的目标类型。
overflow 锁定与恢复实现
src/components/dropdown/dropdown.tsx
useEffect 中调用 getLockTarget 获取容器,若符合条件则从 WeakMap 获取或初始化锁定状态,递增计数,首次锁定时保存 originalOverflow 后设为 hidden;清理阶段递减计数,计数归零时恢复 overflow 并删除缓存条目。
基础测试框架和单实例场景
src/components/dropdown/tests/dropdown.test.tsx
通过 jest.mock 控制 getScrollParent 返回值;验证嵌套可滚动元素打开下拉时 overflowauto 变为 hidden 并关闭后恢复;验证滚动父容器为 window 时不修改 document.body.style.overflow
多实例共享容器引用计数测试
src/components/dropdown/tests/dropdown.test.tsx
验证两个 Dropdown 共享同一滚动父容器时:打开两个下拉后 overflow 保持 hidden,关闭其中一个不会提前恢复,仅在最后一个关闭时才恢复为原始值;测试末尾完成卸载并恢复 mock。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • zombieJ

Poem

🐇 小兔翻山滚呀滚,
下拉框里藏乾坤。
引用计数锁一锁,
标题面板共凝聚。
多兔共享无分离!✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning 代码实现了滚动锁定的核心逻辑,但评审意见指出关键问题导致PR无法合并:引用计数机制在多个Dropdown共享滚动容器时失效,且溢出样式恢复逻辑不完整。 修复引用计数机制以正确处理共享容器场景;保存和恢复overflowX/overflowY而非简写overflow属性;使用真实DOM测试多Dropdown共享容器场景。
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR标题准确描述了主要变更——为下拉菜单组件添加滚动容器锁定功能,与代码改动直接相关。
Out of Scope Changes check ✅ Passed 所有改动均与滚动容器锁定功能相关,没有发现超出范围的改动,代码和测试都专注于解决issue #6606的问题
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai
coderabbitai Bot requested a review from zombieJ June 23, 2026 07:53
@pkg-pr-new

pkg-pr-new Bot commented Jun 23, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/antd-mobile@7069

commit: 279b826

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request introduces scroll locking for the scroll parent of a Dropdown component when it is opened, restoring the original overflow style upon closing or unmounting, and adds corresponding unit tests. The reviewer identified a potential issue where multiple Dropdown components sharing the same scroll parent could cause state conflicts (such as premature unlocking). To resolve this, the reviewer suggested implementing a module-level WeakMap to keep track of the scroll lock count for each scroll parent, ensuring the overflow style is only restored when all dropdowns sharing that parent are closed.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/components/dropdown/dropdown.tsx
Comment thread src/components/dropdown/dropdown.tsx
Comment thread src/components/dropdown/dropdown.tsx
@codecov

codecov Bot commented Jun 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.14286% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 93.35%. Comparing base (d8a7e82) to head (279b826).

Files with missing lines Patch % Lines
src/components/dropdown/dropdown.tsx 97.14% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master    #7069   +/-   ##
=======================================
  Coverage   93.35%   93.35%           
=======================================
  Files         337      337           
  Lines        7416     7451   +35     
  Branches     1859     1910   +51     
=======================================
+ Hits         6923     6956   +33     
- Misses        485      487    +2     
  Partials        8        8           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot added lgtm This PR has been approved by a maintainer size:M This PR changes 30-99 lines, ignoring generated files. labels Jun 23, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/dropdown/dropdown.tsx`:
- Around line 108-123: The current implementation saves and restores the
overflow style on a per-instance basis, which causes issues when multiple
Dropdown components share the same scrollParent. When the first Dropdown closes,
it restores the overflow style prematurely, and the second Dropdown may then set
it incorrectly. Implement reference counting per scrollParent container:
maintain a map or counter that tracks how many Dropdown instances are currently
using each scrollParent, save the original overflow only when the first instance
starts using it, and only restore the original overflow when the counter reaches
zero (when the last Dropdown instance closes). This applies to both the code
section around containerRef.current and getScrollParent(container) where
overflow is being set (lines 108-123) and where it is being restored (lines
135-137).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 656a74cf-a65a-4c1e-acd5-190c9a4f2eea

📥 Commits

Reviewing files that changed from the base of the PR and between d8a7e82 and bb2b739.

📒 Files selected for processing (2)
  • src/components/dropdown/dropdown.tsx
  • src/components/dropdown/tests/dropdown.test.tsx

Comment thread src/components/dropdown/dropdown.tsx
- 使用 WeakMap 维护滚动容器的锁计数状态
- 只有当所有 Dropdown 都关闭时才恢复原始 overflow 样式
- 添加测试用例验证多 Dropdown 场景
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. and removed size:S This PR changes 10-29 lines, ignoring generated files. size:M This PR changes 30-99 lines, ignoring generated files. labels Jun 23, 2026
@zombieJ

zombieJ commented Jun 23, 2026

Copy link
Copy Markdown
Member

来自 Code Review Skill

这个 PR 在做什么

这个 PR 为 Dropdown 打开态增加滚动父容器锁定:通过 getScrollParent 找到最近滚动容器,将其 overflow 临时设为 hidden,关闭时恢复,并用 WeakMap 做共享容器的引用计数。它也补了单实例、window 场景和多实例共享容器的测试,用来修复 #6606

结论

不适合合并

核心思路对,但当前实现的引用计数在真实 DOM 下会失效:第一个 Dropdown 锁住容器后,第二个 Dropdown 可能已经找不到这个滚动父容器,导致仍会提前解锁。

问题清单

🔴 高优先级(阻塞合并)

  • 已锁定的滚动父容器不会被后续 Dropdown 计入引用计数src/components/dropdown/dropdown.tsx:118

    getScrollParent 只会返回 overflowYscroll/auto/overlay 且可滚动的节点;但第一个 Dropdown 打开后,src/components/dropdown/dropdown.tsx:134 已经把该容器设成 overflow: hidden。第二个 Dropdown 再打开时,getScrollParent 会跳过这个已锁容器,无法执行 state.count += 1,于是第一个关闭时 src/components/dropdown/dropdown.tsx:153 会把容器提前恢复,第二个面板仍打开但父容器已经可滚。

    建议解析 scroll parent 时先沿祖先链查找 scrollLockState.has(element) 的已锁容器,再 fallback 到 getScrollParent;测试也需要用真实 DOM + 真实 getScrollParent 覆盖两个 Dropdown 共享同一滚动容器的场景,而不是只 mock 返回同一个元素。

🟡 中优先级(建议修复)

  • 只保存 style.overflow 可能丢失原始 overflowY/overflowX 内联样式src/components/dropdown/dropdown.tsx:132

    当前保存的是 shorthand scrollParent.style.overflow,但滚动容器常见写法是 overflowY: auto,而 getScrollParent 本身也是按 overflowY 判断。此时 style.overflow 可能为空;锁定时写入 overflow = 'hidden',恢复时再写空 shorthand,可能把原来的 inline overflowY: auto 清掉,关闭 Dropdown 后容器无法恢复原滚动样式。

    建议保存并恢复 overflowX / overflowY,或只针对垂直滚动设置并恢复 style.overflowY

🟢 低优先级(可选改进)

无。

- 新增 getLockTarget 方法精确查找要锁定的容器
- 优化滚动锁定逻辑,防止多 Dropdown 共享容器时提前解锁
- 更新测试用例验证修复效果
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature lgtm This PR has been approved by a maintainer size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dropdown在滚动容器内,展开后再滚动时标题和面板分离

2 participants