Skip to content

Conversation

jon-cullison
Copy link
Contributor

@jon-cullison jon-cullison commented Jul 29, 2025

When using the menu with a dropdown that has autofocus on, if the menu is closed and the current active item is removed, opening the menu will not focus any item and the arrow keys will not function.

I have attached a video showing this behavior before and after my fix.

Before

In this video I have created a button and a dropdown with a menu. The button removes the second menu item. I tab to the dropdown, open it, and use the arrow keys to select the second item. Then I press ESC to close the menu. I then tab to the button, press it using ENTER, and then reopen the dropdown by tabbing to it and pressing enter. You will notice that the focus is not in the menu - also I am pressing arrow keys but they do nothing.

rc-menu-pre-fix-2.mp4

After

I perform the same steps as above, but the focus moves to the first item after removing the second item.

rc-menu-post-fix.mp4

Summary by CodeRabbit

  • Bug Fixes
    • 优化菜单聚焦逻辑:仅当当前激活项确实存在于可用项集合中才使用,否则回退到第一个可聚焦的菜单项,避免因无效激活项导致的聚焦异常。
  • Tests
    • 新增单元测试,覆盖激活项被移除后聚焦回退到可聚焦项的场景,验证焦点行为正确性并防止回归。

Copy link

coderabbitai bot commented Jul 29, 2025

Walkthrough

调整 Menu 组件的 imperative handle 中 focus 方法:在使用 mergedActiveKey 作为聚焦目标前,先校验其为真值且存在于当前 keys 集合内;否则回退到第一个可聚焦元素或第一个非禁用子项。新增单元测试覆盖当 mergedActiveKey 无效或对应项被移除时的回退聚焦行为,并引入对 DOM 可见性/可聚焦性的模拟工具。

Changes

Cohort / File(s) Change Summary
Menu 聚焦逻辑调整
src/Menu.tsx
focus 方法中,只有当 mergedActiveKey 为真且 keys.includes(mergedActiveKey) 时才使用它作为聚焦目标;否则回退到首个可聚焦或首个非禁用子项。
单元测试新增
tests/Menu.spec.tsx
引入 spyElementPrototypes 用于模拟 DOM 可见性/可聚焦性,并新增测试:当 mergedActiveKey 无效或对应项被移除时,focus() 正确回退并聚焦可用项。

Sequence Diagram(s)

sequenceDiagram
    participant Consumer as 调用方
    participant MenuHandle as Menu.imperativeHandle
    participant MenuState as Menu 状态 (keys, items)

    Consumer->>MenuHandle: call focus()
    MenuHandle->>MenuState: read mergedActiveKey, keys, firstFocusable, firstNonDisabled
    alt mergedActiveKey 为真 且 keys 包含 mergedActiveKey
        MenuHandle->>MenuState: focus(mergedActiveKey)
        Note right of MenuHandle #D6EAF8: 使用合法的 mergedActiveKey
    else 否则
        MenuHandle->>MenuState: focus(firstFocusable || firstNonDisabled)
        Note right of MenuHandle #FDEBD0: 回退到首个可聚焦或非禁用项
    end
    MenuState-->>MenuHandle: focus result
    MenuHandle-->>Consumer: 返回/完成
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • zombieJ

Poem

🥕
我是兔子来巡视,
键值核查更周全。
若主键迷路去远方,
我跳回首位把灯点亮。
小改动,安心上场。

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c3d3c38 and dc04152.

📒 Files selected for processing (1)
  • tests/Menu.spec.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/Menu.spec.tsx
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

codecov bot commented Jul 29, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.72%. Comparing base (c1b586d) to head (dc04152).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #791   +/-   ##
=======================================
  Coverage   99.72%   99.72%           
=======================================
  Files          27       27           
  Lines         734      736    +2     
  Branches      198      199    +1     
=======================================
+ Hits          732      734    +2     
  Misses          2        2           

☔ View full report in Codecov by Sentry.
📢 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.

@afc163
Copy link
Member

afc163 commented Jul 29, 2025

Could you add a test case?

@jon-cullison
Copy link
Contributor Author

Could you add a test case?

The only way I have been able to reproduce this issue outside of a test is when there is a <RcDropdown /> component rendering the menu and focusing it with autofocus set to true.

I have tried to recreate the issue in a test, but have been unable to do so. Setting the focus with .focus() in a test does not appear to behave the same as using the keyboard in an actual browser. I have also tried to use the keyboard functions in the tests to tab to the menu, but did not work either.

@jon-cullison
Copy link
Contributor Author

@afc163 Not sure where to go from here since I have been unable to reproduce this issue in a test.

@jon-cullison
Copy link
Contributor Author

@afc163 I spent some more time on this and was able to create a test which verifies the fix.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
tests/Menu.spec.tsx (3)

806-806: 使用 jest.spyOn 监听 focus,而非直接覆写实例方法

直接覆写 DOM 实例方法可读性差且不易恢复;使用 jest.spyOn 更安全。

建议修改如下:

-    const focusSpy = jest.fn();
@@
-    const item1 = getByTestId('item1');
-    item1.focus = focusSpy;
+    const item1 = getByTestId('item1');
+    const focusSpy = jest.spyOn(item1, 'focus').mockImplementation(() => {});

并在断言后恢复:

-    expect(focusSpy).toHaveBeenCalled();
+    expect(focusSpy).toHaveBeenCalled();
+    focusSpy.mockRestore();

Also applies to: 851-853


847-849: 断言元素被移除时更语义化的写法

与其依赖 getBy* 抛错,不如使用 queryBy* 明确表达“应不存在”。

应用以下补丁(与上文对解构同步):

-    // Verify the item is removed
-    expect(() => getByTestId('item2')).toThrow();
+    // Verify the item is removed
+    expect(queryByTestId('item2')).toBeNull();

856-858: 这里无需额外 act 包裹

menuRef.current.focus() 为同步调用;@testing-library/react 已为事件做了 act 封装。可直接调用以简化测试。

建议:

-    await act(async () => {
-      menuRef.current.focus();
-    });
+    menuRef.current.focus();
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ae911bc and c3d3c38.

📒 Files selected for processing (1)
  • tests/Menu.spec.tsx (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/Menu.spec.tsx (1)
src/interface.ts (1)
  • MenuRef (124-132)
🔇 Additional comments (1)
tests/Menu.spec.tsx (1)

11-11: 引入 domHook OK

用于模拟可见性场景以触发可聚焦路径,合理。

@afc163 afc163 merged commit c7f9880 into react-component:master Aug 29, 2025
9 checks passed
@mellis481
Copy link

@afc163 Please advise when this feature is expected to be released. Thanks!

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.

3 participants