Skip to content
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

enhance: Lock switch field if needConfirm #892

Merged
merged 3 commits into from
Nov 12, 2024
Merged

enhance: Lock switch field if needConfirm #892

merged 3 commits into from
Nov 12, 2024

Conversation

zombieJ
Copy link
Member

@zombieJ zombieJ commented Nov 12, 2024

ref ant-design/ant-design#50979

Summary by CodeRabbit

  • 新功能
    • RangePicker 组件中新增 allowEmpty 属性,允许选择空日期范围。
    • 引入自定义钩子 useRangeActiveLock,管理组件的活动状态。
  • 测试
    • 增加测试用例,验证在未确认第一个输入字段前,点击第二个字段不改变焦点的行为。
  • 文档
    • 更新了相关文档以反映新功能和组件的修改。

Copy link

vercel bot commented Nov 12, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
picker ✅ Ready (Inspect) Visit Preview 💬 Add feedback Nov 12, 2024 6:53am

Copy link

coderabbitai bot commented Nov 12, 2024

Walkthrough

此拉取请求对多个文件进行了修改,主要集中在 RangePicker 组件及其相关钩子上。新增了 allowEmpty 属性,允许选择空日期范围,并移除了禁用时间的逻辑。引入了新的自定义钩子 useRangeActiveLock,用于管理组件的活动状态。此外,useRangeValue 钩子进行了功能扩展,新增了 hasSubmitValue 函数以检查提交值的状态。测试文件也更新了,以增强对用户交互场景的覆盖。

Changes

文件路径 更改摘要
docs/examples/debug.tsx 添加 allowEmpty 属性到 RangePicker,移除 disabledTime 逻辑和相关的控制台日志。新增第二个 RangePicker 实例。
src/PickerInput/RangePicker.tsx 更新 PopupShowTimeConfig 的导入语句,修改 onSelectorFocus 方法以包含 needConfirm 检查,新增 hasSubmitValue 变量。
src/PickerInput/hooks/useRangeActiveLock.ts 新增自定义钩子 useRangeActiveLock,管理活动状态,返回 focused 状态及相关函数。
src/PickerInput/hooks/useRangeValue.ts 新增 hasSubmitValue 函数,修改 useRangeValue 的返回值,移除 useCalendarValue 的导出。
tests/range.spec.tsx 添加新的测试用例以验证未确认的输入字段焦点行为,更新 beforeEach 设置以清理文档状态。

Possibly related PRs

Poem

在兔子洞中跳跃欢,
日期选择新变化。
空范围也能选,
用户体验更美好! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai 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:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @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

Documentation and Community

  • 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

@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: 4

🧹 Outside diff range and nitpick comments (6)
src/PickerInput/hooks/useRangeActiveLock.ts (2)

12-14: 建议添加状态初始化的注释说明

为了提高代码可维护性,建议为这些状态变量添加用途说明的注释。

建议添加如下注释:

+ // 当前激活的索引
const [activeIndex, setActiveIndex] = React.useState<number | null>(null);
+ // 组件是否处于焦点状态
const [focused, setFocused] = React.useState<boolean>(false);
+ // 激活状态的历史记录
const [activeList, setActiveList] = React.useState<number[]>([]);

1-23: 缺少 JSDoc 文档

作为一个公共 Hook,建议添加完整的 JSDoc 文档说明其用途、参数和返回值。

建议在文件开头添加如下文档:

+/**
+ * 用于管理日期范围选择器的激活状态的 Hook
+ * @returns {[boolean, (focused: boolean) => void, (index: number) => void]} 
+ * - focused: 当前是否处于焦点状态
+ * - triggerFocus: 设置焦点状态的函数
+ * - setActiveIndex: 设置当前激活索引的函数
+ */
export default function useRangeActiveLock(): [
docs/examples/debug.tsx (1)

86-86: 建议完善示例文档

这个新增的 RangePicker 实例虽然展示了 allowEmpty 的用法,但建议:

  1. 添加注释说明 allowEmpty 属性的作用和使用场景
  2. 根据 PR 的目标,展示 needConfirm 相关的功能
  3. 为不同的使用场景提供完整的示例代码

建议添加如下注释:

+ // 允许清空的日期范围选择器示例
+ // allowEmpty: 允许用户清空已选择的日期范围
  <RangePicker {...sharedLocale} style={{ width: 400 }} allowEmpty />
src/PickerInput/hooks/useRangeValue.ts (1)

337-339: 建议为 hasSubmitValue 函数添加 JSDoc 文档

为了提高代码的可维护性,建议添加 JSDoc 文档说明以下内容:

  • 函数的用途
  • 参数 index 的含义和取值范围
  • 返回值的含义

建议添加如下文档:

+/**
+ * 检查指定索引位置是否存在已提交的值
+ * @param index - 要检查的索引位置(0 表示开始日期,1 表示结束日期)
+ * @returns 如果指定位置存在值则返回 true,否则返回 false
+ */
function hasSubmitValue(index: number) {
  return !!submitValue()[index];
}
src/PickerInput/RangePicker.tsx (1)

635-649: 增强的焦点管理逻辑

新增的焦点管理逻辑通过以下方式提升了用户体验:

  1. 在需要确认时防止不必要的焦点切换
  2. 确保用户完成当前字段的输入后才能切换到下一个字段
  3. 与 allowEmpty 和 needConfirm 属性完美配合

建议添加注释说明这段复杂逻辑的具体用途。

 onSelectorFocus: SelectorProps['onFocus'] = (event, index) => {
+    // 当需要确认且上一个值未提交时,保持焦点在上一个输入框
     const activeListLen = activeIndexList.length;
     const lastActiveIndex = activeIndexList[activeListLen - 1];
     if (
tests/range.spec.tsx (1)

2059-2073: 测试用例实现正确且有价值!

该测试用例很好地验证了在需要确认的情况下,点击第二个输入框不应该切换焦点的行为。建议考虑增加以下测试场景:

  1. 确认后再点击第二个输入框的情况
  2. 在第一个输入框选择日期后按 ESC 取消的情况

建议添加如下测试用例:

  it('should not click to focus on next field if first field is not confirm', () => {
    const onCalendarChange = jest.fn();
    const { container } = render(
      <DayRangePicker onCalendarChange={onCalendarChange} showTime needConfirm />,
    );

    // Select first field
    openPicker(container, 0);
    selectCell(11);
    expect(onCalendarChange).toHaveBeenCalled();

    // Not click confirm and click next field
    openPicker(container, 1);
    expect(container.querySelectorAll('.rc-picker-input')[0]).toHaveClass('rc-picker-input-active');
+
+   // Should allow focus second field after confirm
+   fireEvent.click(document.querySelector('.rc-picker-ok button'));
+   openPicker(container, 1);
+   expect(container.querySelectorAll('.rc-picker-input')[1]).toHaveClass('rc-picker-input-active');
+
+   // Should keep focus on first field when press ESC
+   openPicker(container, 0);
+   selectCell(12);
+   fireEvent.keyDown(container, { keyCode: KeyCode.ESC });
+   openPicker(container, 1);
+   expect(container.querySelectorAll('.rc-picker-input')[0]).toHaveClass('rc-picker-input-active');
  });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e1d6848 and 00b392e.

📒 Files selected for processing (5)
  • docs/examples/debug.tsx (1 hunks)
  • src/PickerInput/RangePicker.tsx (3 hunks)
  • src/PickerInput/hooks/useRangeActiveLock.ts (1 hunks)
  • src/PickerInput/hooks/useRangeValue.ts (3 hunks)
  • tests/range.spec.tsx (2 hunks)
🔇 Additional comments (5)
src/PickerInput/hooks/useRangeValue.ts (3)

79-79: 移除 useCalendarValue 的导出是个好的改进!

useCalendarValue 改为模块内部函数有助于更好的封装,这样的改动符合信息隐藏原则。


189-190: 返回类型的更新清晰且类型安全!

返回类型的文档注释和实际返回值保持一致,维护了良好的类型安全性。

Also applies to: 342-342


337-342: 验证 hasSubmitValue 的使用情况

为确保新增的 hasSubmitValue 函数被正确使用,建议验证其调用位置。

✅ Verification successful

hasSubmitValue 函数使用验证完成

hasSubmitValue 函数在代码中的使用是合理且安全的:

  • RangePicker.tsx 中作为 useRangeValue hook 的返回值被正确解构
  • 用于判断指定索引位置是否已填充值的逻辑检查
  • allowEmptycalendarValue 配合使用,确保日期选择器的正确交互行为
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# 搜索 hasSubmitValue 的使用位置
rg "hasSubmitValue" --type ts

Length of output: 416


Script:

#!/bin/bash
# 查看 RangePicker.tsx 中 hasSubmitValue 的具体使用上下文
rg "hasSubmitValue" src/PickerInput/RangePicker.tsx -B 5 -A 5

Length of output: 755

src/PickerInput/RangePicker.tsx (2)

36-36: 类型导入优化!

使用 TypeScript 的 type 关键字导入类型是一个很好的实践,它可以帮助优化编译性能。


328-329: 新增 hasSubmitValue 函数用于验证提交状态

新增的 hasSubmitValue 函数用于检查特定索引是否有已提交的值,这对于后续的焦点管理逻辑至关重要。

Comment on lines 3 to 11
export default function useRangeActiveLock(): [
focused: boolean,
triggerFocus: (focused: boolean) => void,
// lastOperation: (type?: OperationType) => OperationType,
// activeIndex: number,
setActiveIndex: (index: number) => void,
// nextActiveIndex: NextActive<DateType>,
// activeList: number[],
] {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

类型定义与实现不匹配

返回类型签名中包含了被注释掉的类型定义,这与实际实现的返回值不一致。建议移除未使用的类型定义或实现完整功能。

建议应用以下更改:

export default function useRangeActiveLock(): [
  focused: boolean,
  triggerFocus: (focused: boolean) => void,
-  // lastOperation: (type?: OperationType) => void,
-  // activeIndex: number,
  setActiveIndex: (index: number) => void,
-  // nextActiveIndex: NextActive<DateType>,
-  // activeList: number[],
] {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export default function useRangeActiveLock(): [
focused: boolean,
triggerFocus: (focused: boolean) => void,
// lastOperation: (type?: OperationType) => OperationType,
// activeIndex: number,
setActiveIndex: (index: number) => void,
// nextActiveIndex: NextActive<DateType>,
// activeList: number[],
] {
export default function useRangeActiveLock(): [
focused: boolean,
triggerFocus: (focused: boolean) => void,
setActiveIndex: (index: number) => void,
] {

setActiveList([...activeList, index]);
};

return [focused, setFocused, onActive];
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

返回值命名不一致

返回值中 setFocused 与类型定义中的 triggerFocus 命名不一致,onActivesetActiveIndex 命名不一致。

建议修改为:

- return [focused, setFocused, onActive];
+ return [focused, triggerFocus: setFocused, setActiveIndex: onActive];

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 17 to 20
const onActive = (index: number) => {
setActiveIndex(index);
setActiveList([...activeList, index]);
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

潜在的内存泄漏问题

activeList 数组只会不断增长而没有清理机制,可能导致内存泄漏。建议限制数组大小或在适当时机清理。

建议修改实现方式:

const onActive = (index: number) => {
  setActiveIndex(index);
-  setActiveList([...activeList, index]);
+  setActiveList(prevList => {
+    const newList = [...prevList, index];
+    // 只保留最近的 N 个记录
+    return newList.slice(-5);
+  });
};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const onActive = (index: number) => {
setActiveIndex(index);
setActiveList([...activeList, index]);
};
const onActive = (index: number) => {
setActiveIndex(index);
setActiveList(prevList => {
const newList = [...prevList, index];
// 只保留最近的 N 个记录
return newList.slice(-5);
});
};

Comment on lines +63 to +83
// disabledTime={(date, range, info) => {
// // console.log(`Time-${range}`, range, info);
// const { from } = info;

if (from) {
console.log(
`Time-${range}`,
from.format('YYYY-MM-DD HH:mm:ss'),
date.format('YYYY-MM-DD HH:mm:ss'),
);
}
// if (from) {
// console.log(
// `Time-${range}`,
// from.format('YYYY-MM-DD HH:mm:ss'),
// date.format('YYYY-MM-DD HH:mm:ss'),
// );
// }

if (from && from.isSame(date, 'day')) {
return {
disabledHours: () => [from.hour()],
disabledMinutes: () => [0, 1, 2, 3],
disabledSeconds: () => [0, 1, 2, 3],
};
}
return {};
}}
// if (from && from.isSame(date, 'day')) {
// return {
// disabledHours: () => [from.hour()],
// disabledMinutes: () => [0, 1, 2, 3],
// disabledSeconds: () => [0, 1, 2, 3],
// };
// }
// return {};
// }}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议删除注释代码而不是保留

建议完全删除这段被注释的代码,而不是将其保留在文件中。如果这些代码对于示例或测试很重要,应该:

  1. 将其移动到单独的示例文件中
  2. 或者在文档中说明这种用法

保留注释掉的代码会:

  • 增加维护负担
  • 造成代码混乱
  • 可能导致过时的实现被重新使用

Copy link

codecov bot commented Nov 12, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 95.46%. Comparing base (e1d6848) to head (f6fb834).
Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #892      +/-   ##
==========================================
+ Coverage   95.43%   95.46%   +0.02%     
==========================================
  Files          64       64              
  Lines        2719     2732      +13     
  Branches      762      769       +7     
==========================================
+ Hits         2595     2608      +13     
  Misses        121      121              
  Partials        3        3              

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

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.

1 participant