Skip to content

Conversation

@cactuser-Lu
Copy link
Contributor

@cactuser-Lu cactuser-Lu commented Sep 30, 2025

🤔 This is a ...

  • New feature

支持键盘 Esc 关闭 \左右切换

Summary by CodeRabbit

  • 新功能

    • 引导(Tour)新增键盘交互:按 Esc 关闭引导,左右方向键在步骤间切换,提升可访问性与操作便捷性。
    • 新增可选属性 keyboard(默认启用),可开启或禁用键盘导航。
    • 遮罩支持按 Esc 触发关闭行为,增强交互一致性。
  • 修复

    • 在可编辑输入获焦时自动忽略键盘导航,避免冲突。
  • 测试

    • 增加针对键盘交互的单元测试,覆盖 Esc、方向键、边界及可编辑元素场景。

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Sep 30, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
tour Ready Ready Preview Comment Sep 30, 2025 6:38am

@coderabbitai
Copy link

coderabbitai bot commented Sep 30, 2025

Walkthrough

为 Tour 组件新增键盘交互:增加可选属性 keyboard?: boolean(默认开启),在打开时注册全局 keydown 监听以支持 Esc 关闭与左右方向键切换步骤;Mask/Portal 与 TourStep 支持 onEsc 回调;同时升级部分依赖版本。

Changes

Cohort / File(s) Summary
组件键盘交互实现
src/Tour.tsx
新增 keyboard?: boolean 支持;引入 useEventKeyCode;新增 handleClosehandleEscClosekeyboardHandler(处理 EscArrowLeftArrowRight);在打开时注册/关闭时注销 window keydown 监听;忽略可编辑目标;将 Esc 回调通过 onEsc 传递到 TourStepMask
Mask 键盘透传
src/Mask.tsx
MaskProps 添加 onEsc 回调,并将其透传至 Portal,以支持遮罩层上的 Esc 触发。
公开接口扩展
src/interface.ts
TourProps 中新增可选字段 keyboard?: boolean
测试覆盖
tests/index.test.tsx
新增键盘交互测试:Esc 关闭、左右箭头导航、边界检查、在输入/textarea 中忽略键盘操作,以及 keyboard={false} 时禁用导航。
依赖版本更新
package.json
升级 @rc-component/portal@rc-component/util 版本。
其它元数据
manifest_file
若干 manifest 相关行变更(小幅修改)。

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User as 用户
  participant KB as 键盘事件
  participant Tour as Tour 组件
  participant Host as 调用方/回调
  participant Mask as Mask/Portal

  Note over Tour,KB: 当 Tour.open 且 keyboard 为 true 时,注册 window keydown 处理器
  User->>KB: 按键 (Esc / ArrowLeft / ArrowRight)
  KB->>Tour: keydown -> keyboardHandler(e)
  alt 事件目标为可编辑元素
    Tour-->>KB: 忽略事件
  else Esc 被按下
    Tour->>Tour: handleEscClose -> handleClose(current)
    Tour->>Host: 触发 onClose(current)
    Tour->>Mask: 调用 Mask.onEsc(event)
  else ArrowLeft / ArrowRight
    Tour->>Tour: 计算 nextIndex
    Tour->>Host: 调用 onInternalChange(nextIndex)
    Host-->>Tour: (若受控)外部 onChange 同步状态
  end
  Note over Tour: 组件关闭或卸载时撤销 keydown 监听
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • zombieJ

Poem

我是小兔轻跳步,键盘轻触路自铺。
左右跃步去又来,Esc 一按幕布无。
导游更新新姿势,欢欢喜喜走一圈 🐇✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题中存在拼写错误 'keyborad',应为 'keyboard',但标题清晰描述了主要变更:为Tour组件添加键盘事件支持。
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 docstrings
🧪 Generate unit tests (beta)
  • 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

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

@gemini-code-assist
Copy link

Summary of Changes

Hello @cactuser-Lu, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求通过引入键盘事件支持,显著提升了 Tour 组件的用户体验和可访问性。现在用户可以通过键盘快捷键关闭 Tour 或在不同步骤之间切换,这使得交互更加流畅和便捷。

Highlights

  • 键盘事件支持: 为 Tour 组件的步骤添加了键盘事件支持,允许用户通过键盘进行交互。
  • Esc 键关闭: 用户现在可以使用 Esc 键关闭 Tour。
  • 左右箭头导航: 用户可以使用 左箭头右箭头 键在 Tour 步骤之间进行导航。
  • keyboard 属性: 引入了一个新的 keyboard 属性,默认为 true,允许开发者控制是否启用键盘交互功能。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

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 keyboard navigation to the Tour component, which is a great enhancement for accessibility. The implementation is well done. I have one suggestion to refactor the keyboard event handler to use modern browser APIs and improve its structure, which should also fix a minor bug. Additionally, there's a small typo in the pull request title ('keyborad' should be 'keyboard').

Comment on lines 168 to 193
const keyboardHandler = useEvent((e: KeyboardEvent) => {
if (keyboard && e.keyCode === KeyCode.ESC) {
if (mergedClosable !== null) {
e.stopPropagation();
e.preventDefault();
handleClose();
}
return;
}

if (keyboard && e.keyCode === KeyCode.LEFT) {
if (mergedCurrent > 0) {
e.preventDefault();
onInternalChange(mergedCurrent - 1);
}
return;
}

if (keyboard && e.keyCode === KeyCode.RIGHT) {
e.preventDefault();
if (mergedCurrent < steps.length - 1) {
onInternalChange(mergedCurrent + 1);
}
return;
}
});

Choose a reason for hiding this comment

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

medium

The keyboard handler implementation can be improved for better readability, correctness, and to adhere to modern web standards.

  • Use e.key instead of e.keyCode: The keyCode property is deprecated. It's better to use e.key for future-proofing and consistency with modern web APIs. This change would also allow for the removal of the KeyCode import.
  • Conditional preventDefault: The e.preventDefault() for the right arrow key is called unconditionally. It should only be called when the step actually changes (i.e., inside the if block), similar to the logic for the left arrow key. This prevents blocking the default browser behavior when at the last step.
  • Improved Structure: A switch statement on e.key would be more readable and maintainable than a series of if statements. The if (keyboard) check can also be done once at the beginning of the function.

Here is a suggested refactoring that applies these points:

  const keyboardHandler = useEvent((e: KeyboardEvent) => {
    if (!keyboard) {
      return;
    }

    switch (e.key) {
      case 'Escape':
        if (mergedClosable !== null) {
          e.stopPropagation();
          e.preventDefault();
          handleClose();
        }
        break;
      case 'ArrowLeft':
        if (mergedCurrent > 0) {
          e.preventDefault();
          onInternalChange(mergedCurrent - 1);
        }
        break;
      case 'ArrowRight':
        if (mergedCurrent < steps.length - 1) {
          e.preventDefault();
          onInternalChange(mergedCurrent + 1);
        }
        break;
      default:
        // Do nothing
        break;
    }
  });

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: 1

🧹 Nitpick comments (2)
src/interface.ts (1)

61-61: 建议添加 JSDoc 注释说明 keyboard 属性的用途。

新增的 keyboard 属性缺少文档注释,建议添加说明以提高 API 的可读性和可维护性。例如:

   open?: boolean;
+  /** Whether to enable keyboard interaction (Esc to close, Left/Right to navigate). Default: true */
   keyboard?: boolean;
   defaultOpen?: boolean;
src/Tour.tsx (1)

195-201: 考虑使用 useEffect 代替 useLayoutEffect

事件监听器的注册和清理逻辑正确,依赖项数组也准确。但是,对于键盘事件监听器的注册,使用 useEffect 而非 useLayoutEffect 更合适,因为这个操作不需要在浏览器绘制之前同步执行,使用 useEffect 可以避免阻塞视觉更新。

建议修改:

- useLayoutEffect(() => {
+ useEffect(() => {
    if (!mergedOpen) return;
    window.addEventListener('keydown', keyboardHandler);
    return () => {
      window.removeEventListener('keydown', keyboardHandler);
    };
  }, [mergedOpen, keyboardHandler]);

注意:需要在文件顶部添加 useEffect 的导入:

- import { useMemo } from 'react';
+ import { useMemo, useEffect } from 'react';
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c25065a and a343a90.

📒 Files selected for processing (2)
  • src/Tour.tsx (4 hunks)
  • src/interface.ts (1 hunks)
🔇 Additional comments (3)
src/Tour.tsx (3)

7-8: 代码更改看起来不错!

正确引入了 useEventKeyCode 工具,用于实现键盘事件处理。


39-39: 代码更改看起来不错!

keyboard 属性默认值为 true 是合理的设计,既启用了新功能,又保持了向后兼容性。


161-164: 代码更改看起来不错!

将关闭逻辑提取为 handleClose 函数是良好的代码组织实践,便于在多处复用(键盘处理、按钮点击、完成回调)。

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: 0

♻️ Duplicate comments (1)
src/Tour.tsx (1)

168-204: 建议采用现代 API 并改进代码结构。

当前实现已经解决了之前评审中关于 preventDefault 调用时机和输入元素过滤的问题,这很好!但仍有改进空间:

  1. 使用 e.key 替代 e.keyCodekeyCode 已被弃用,应使用 e.key 以符合现代 Web 标准。
  2. 使用 switch 语句:相比多个 if 语句,switch 语句可读性和可维护性更好。

应用以下差异来改进实现:

  const keyboardHandler = useEvent((e: KeyboardEvent) => {
    // Ignore keyboard events from input-like elements to avoid interfering when typing
    const el = e.target as HTMLElement | null;
    if (
        el?.tagName === 'INPUT' ||
        el?.tagName === 'TEXTAREA' ||
        el?.tagName === 'SELECT' ||
        el?.isContentEditable
      ) {
        return;
      }

-   if (keyboard && e.keyCode === KeyCode.ESC) {
-     if (mergedClosable !== null) {
-       e.stopPropagation();
-       e.preventDefault();
-       handleClose();
-     }
-     return;
-   }
-
-   if (keyboard && e.keyCode === KeyCode.LEFT) {
-     if (mergedCurrent > 0) {
-       e.preventDefault();
-       onInternalChange(mergedCurrent - 1);
-     }
-     return;
-   }
-
-   if (keyboard && e.keyCode === KeyCode.RIGHT) {
-     if (mergedCurrent < steps.length - 1) {
-       e.preventDefault();
-       onInternalChange(mergedCurrent + 1);
-     }
-     return;
-   }
+   if (!keyboard) {
+     return;
+   }
+
+   switch (e.key) {
+     case 'Escape':
+       if (mergedClosable !== null) {
+         e.stopPropagation();
+         e.preventDefault();
+         handleClose();
+       }
+       break;
+     case 'ArrowLeft':
+       if (mergedCurrent > 0) {
+         e.preventDefault();
+         onInternalChange(mergedCurrent - 1);
+       }
+       break;
+     case 'ArrowRight':
+       if (mergedCurrent < steps.length - 1) {
+         e.preventDefault();
+         onInternalChange(mergedCurrent + 1);
+       }
+       break;
+     default:
+       break;
+   }
  });
🧹 Nitpick comments (1)
src/Tour.tsx (1)

7-8: 建议使用现代的 e.key API 替代已弃用的 KeyCode

KeyCode 已被弃用,建议使用 e.key 属性来检测按键。这样可以移除对 KeyCode 的导入依赖,并使代码更符合现代 Web 标准。

应用以下差异以移除 KeyCode 导入:

 import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect';
 import useEvent from '@rc-component/util/lib/hooks/useEvent';
-import KeyCode from '@rc-component/util/lib/KeyCode';
 import useControlledState from '@rc-component/util/lib/hooks/useControlledState';
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a343a90 and 551f26c.

📒 Files selected for processing (1)
  • src/Tour.tsx (4 hunks)
🔇 Additional comments (4)
src/Tour.tsx (4)

39-39: 默认启用键盘支持是合理的设计!

keyboard 默认设置为 true 符合渐进增强原则,为大多数用户提供更好的可访问性体验。


93-93: 依赖项更新正确!

setMergedCurrent 添加到依赖数组中符合 React Hooks 的最佳实践,即使该函数引用通常是稳定的。


161-164: 提取 handleClose 函数提高了代码复用性!

将关闭逻辑提取为独立函数是良好的重构实践,使得键盘处理和按钮点击可以共享相同的关闭逻辑。


206-212: 事件监听器的注册和清理实现正确!

使用 useLayoutEffect 在 tour 打开时注册全局键盘事件监听器,并在关闭或组件卸载时正确清理,避免了内存泄漏。

src/Tour.tsx Outdated
// Ignore keyboard events from input-like elements to avoid interfering when typing
const el = e.target as HTMLElement | null;
if (
el?.tagName === 'INPUT' ||
Copy link
Member

Choose a reason for hiding this comment

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

@aojunhao123 感觉这类检查还挺通用的,我们是不是可以抽到 rc-util 的 KeyCode util 里?

Copy link
Contributor

Choose a reason for hiding this comment

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

可以的。经讨论,该工具函数顺带处理 IME 的 case

src/Tour.tsx Outdated
return;
}

if (keyboard && e.keyCode === KeyCode.ESC) {
Copy link
Contributor

Choose a reason for hiding this comment

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

直接用e.key === 'Escape' 来判断就行,keyCode已经是废弃api了,下面也同理

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK

@vercel
Copy link

vercel bot commented Dec 30, 2025

Someone is attempting to deploy a commit to the React Component Team on Vercel.

A member of the Team first needs to authorize it.

Comment on lines +170 to +172
if (KeyCode.isEditableTarget(e)) {
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

这里我有个疑问,如果tour里有一些表单,比如input,此时我点击input聚焦后,再按下esc,是不是就直接return了?感觉这个是有点问题的,豆酱老师觉得呢 @zombieJ

Copy link
Member

Choose a reason for hiding this comment

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

这不是预期的么……输入框里键盘操作都不应该控制 Tour 的切换

Copy link
Contributor

@aojunhao123 aojunhao123 Dec 30, 2025

Choose a reason for hiding this comment

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

这里我有个疑问,如果tour里有一些表单,比如input,此时我点击input聚焦后,再按下esc,是不是就直接return了?感觉这个是有点问题的

经讨论,在与表单交互时(非IME),esc依旧能够关闭tour,这个case会在Portal中通过加锁处理

src/Tour.tsx Outdated
return;
}

if (keyboard && e.key === 'Escape') {
Copy link
Member

Choose a reason for hiding this comment

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

使用 Portal 自带的 onEsc 如何?它会处理层叠逻辑

Copy link
Contributor

Choose a reason for hiding this comment

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

+1

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK

@aojunhao123
Copy link
Contributor

佬,还跟进吗

@codecov
Copy link

codecov bot commented Jan 8, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.16%. Comparing base (c25065a) to head (c716314).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master      #86      +/-   ##
==========================================
+ Coverage   99.07%   99.16%   +0.08%     
==========================================
  Files          10       10              
  Lines         217      240      +23     
  Branches       97      106       +9     
==========================================
+ Hits          215      238      +23     
  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 Jan 8, 2026

图片

}
return getPlacements(arrowPointAtCenter);
}, [builtinPlacements, arrowPointAtCenter]);
const handleClose = () => {
Copy link
Member

Choose a reason for hiding this comment

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

test case

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK

@socket-security
Copy link

socket-security bot commented Jan 9, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedgh-pages@​3.2.3992510082100
Addedeslint@​7.32.09710010050100
Addedrc-test@​7.1.3751007490100
Addedfather@​4.6.13931008198100
Addedreact@​19.2.31001008497100
Addedtypescript@​5.9.31001009010090
Addedprettier@​3.7.4901009795100
Addedreact-dom@​19.2.31001009298100

View full report

@socket-security
Copy link

socket-security bot commented Jan 9, 2026

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn Critical
Critical CVE: tschaub npm gh-pages vulnerable to prototype pollution

CVE: GHSA-8mmm-9v2q-x3f9 tschaub gh-pages vulnerable to prototype pollution (CRITICAL)

Affected versions: < 5.0.0

Patched version: 5.0.0

From: package.jsonnpm/gh-pages@3.2.3

ℹ Read more on: This package | This alert | What is a critical CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known critical CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/gh-pages@3.2.3. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm entities is 91.0% likely obfuscated

Confidence: 0.91

Location: Package overview

From: ?npm/father@4.6.13npm/cheerio@1.0.0-rc.12npm/dumi@2.4.21npm/entities@4.5.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/entities@4.5.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

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: 0

🧹 Nitpick comments (1)
tests/index.test.tsx (1)

1293-1325: 测试覆盖良好,可考虑补充箭头键导航测试。

ESC 关闭功能的测试逻辑正确,正确验证了:

  • 全局键盘事件监听(在 window 上模拟 keydown)
  • onClose 回调被调用并传入当前步骤索引
  • Tour 组件从 DOM 中移除

不过,根据 PR 目标,键盘支持还包括左右箭头键切换步骤。建议补充以下测试用例以提高覆盖率:

  1. 测试左右箭头键在多步骤 Tour 中的导航功能
  2. 测试 keyboard={false} 时键盘事件是否被禁用
  3. 测试组件卸载时键盘事件监听器是否正确清理
  4. 测试 Tour 关闭状态下按 ESC 不会触发 onClose
💡 可选的补充测试用例示例
it('should navigate steps with arrow keys', () => {
  const onChange = jest.fn();
  const Demo = () => {
    const [current, setCurrent] = useState(0);
    return (
      <Tour
        open
        current={current}
        onChange={(step) => {
          setCurrent(step);
          onChange(step);
        }}
        steps={[
          { title: 'Step 1', description: 'First step' },
          { title: 'Step 2', description: 'Second step' },
          { title: 'Step 3', description: 'Third step' },
        ]}
      />
    );
  };
  
  render(<Demo />);
  
  // Press right arrow to go to next step
  fireEvent.keyDown(window, { key: 'ArrowRight' });
  expect(onChange).toHaveBeenCalledWith(1);
  
  // Press left arrow to go to previous step
  fireEvent.keyDown(window, { key: 'ArrowLeft' });
  expect(onChange).toHaveBeenCalledWith(0);
});

it('should not respond to keyboard when keyboard prop is false', () => {
  const onClose = jest.fn();
  render(
    <Tour
      open
      keyboard={false}
      onClose={onClose}
      steps={[{ title: 'Step 1', description: 'First step' }]}
    />
  );
  
  fireEvent.keyDown(window, { key: 'Escape' });
  expect(onClose).not.toHaveBeenCalled();
});
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ff54a9 and 9734750.

📒 Files selected for processing (1)
  • tests/index.test.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Socket Security: Pull Request Alerts

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: 0

🧹 Nitpick comments (2)
tests/index.test.tsx (2)

1293-1325: 建议补充测试用例:当 keyboard={false} 时按 ESC 键的行为。

当前测试验证了按 ESC 键关闭 Tour 的基本功能,但缺少对 keyboard={false} 场景的测试。根据 UX 最佳实践,即使禁用键盘导航,ESC 键通常仍应能关闭模态框/引导组件。建议补充测试用例明确这一行为预期。

💡 建议的测试用例
+    it('should close tour when press ESC even if keyboard is disabled', () => {
+      const onClose = jest.fn();
+      const Demo = () => {
+        const [open, setOpen] = useState(true);
+        return (
+          <Tour
+            open={open}
+            keyboard={false}
+            onClose={(current) => {
+              setOpen(false);
+              onClose(current);
+            }}
+            steps={[
+              {
+                title: '创建',
+                description: '创建一条数据',
+              },
+            ]}
+          />
+        );
+      };
+      
+      render(<Demo />);
+      expect(document.querySelector('.rc-tour')).toBeTruthy();
+      
+      fireEvent.keyDown(window, { key: 'Escape' });
+      
+      expect(onClose).toHaveBeenCalledWith(0);
+      expect(document.querySelector('.rc-tour')).toBeFalsy();
+    });

1402-1404: 可选的代码风格优化:将 mockClear 放在独立行以提高可读性。

当前 onChange.mockClear() 的位置在逻辑上是正确的,但将其移到独立行能让测试的意图更清晰——表明这是在重置 mock 状态后开始新的断言。

♻️ 建议的调整
       // Press ArrowRight at last step - should not change
-      onChange.mockClear();
       fireEvent.keyDown(window, { key: 'ArrowRight' });
+      
+      onChange.mockClear();
       expect(onChange).not.toHaveBeenCalled();
       expect(document.querySelector('.rc-tour-title').innerHTML).toBe('step 2');

或者在断言前清除:

       // Press ArrowRight at last step - should not change
+      onChange.mockClear();
       fireEvent.keyDown(window, { key: 'ArrowRight' });
       expect(onChange).not.toHaveBeenCalled();
       expect(document.querySelector('.rc-tour-title').innerHTML).toBe('step 2');
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9734750 and c716314.

📒 Files selected for processing (1)
  • tests/index.test.tsx
🔇 Additional comments (1)
tests/index.test.tsx (1)

1446-1493: 测试覆盖全面:正确验证了可编辑元素中的键盘事件处理。

该测试用例考虑周到,验证了在 input 和 textarea 中按下方向键时不会触发 Tour 导航,这是良好的 UX 实践。测试正确地将事件触发在具体元素上(而非 window),准确模拟了真实的用户交互场景。

@zombieJ zombieJ merged commit fb6dd39 into react-component:master Jan 12, 2026
7 of 8 checks passed
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