Skip to content

fix: presets和maxDate的对比 #930

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

Open
wants to merge 18 commits into
base: master
Choose a base branch
from

Conversation

Roxannej
Copy link

@Roxannej Roxannej commented May 26, 2025

maxDate和presets的值的对比,禁用点击 ant-design/ant-design#53693

Summary by CodeRabbit

  • 新功能

    • 预设面板及相关组件新增可选的最大日期(maxDate)限制,超出最大日期的预设项将自动禁用,无法点击或悬停选择。
  • 测试

    • 增加了针对禁用预设项的测试用例,确保超出最大日期的预设不会触发事件回调。
    • 新增多种预设类型与最大日期边界条件的测试,提升预设交互的覆盖率和稳定性。

fix: presets和maxDate的比较
Copy link

vercel bot commented May 26, 2025

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 Jun 3, 2025 0:05am

Copy link

coderabbitai bot commented May 26, 2025

"""

Walkthrough

本次更改在日期选择器相关组件中引入了 maxDate 属性,用于限制可选日期的上限。maxDate 通过 RangePickerSinglePicker 组件传递至 Popup,再传至 PresetPanel,用于禁用超过最大日期的预设项,并在相关事件处理时加入判断,确保超出最大日期的预设不可交互。同时,新增了对应的测试用例验证禁用逻辑。

Changes

文件或路径 变更摘要
src/PickerInput/Popup/PresetPanel.tsx PresetPanelProps 新增 maxDate,渲染时禁用超过 maxDate 的预设项,相关事件处理增加判断
src/PickerInput/Popup/index.tsx PopupProps 新增 maxDate,并将其传递给 PresetPanel
src/PickerInput/RangePicker.tsx <Popup> 组件新增 maxDate 属性传递
src/PickerInput/SinglePicker.tsx <Popup> 组件新增 maxDate 属性传递
tests/picker.spec.tsx 新增测试,验证禁用预设项不会触发 hover 或 change 事件

Sequence Diagram(s)

sequenceDiagram
    participant RangePicker/SinglePicker
    participant Popup
    participant PresetPanel

    RangePicker/SinglePicker->>Popup: 传递 maxDate 属性
    Popup->>PresetPanel: 传递 maxDate 属性
    PresetPanel->>PresetPanel: 判断预设项是否超出 maxDate,超出则禁用
    User->>PresetPanel: 鼠标悬停或点击预设项
    PresetPanel-->>User: 若禁用则不响应事件
Loading

Suggested reviewers

  • zombieJ
  • afc163

Poem

🐰
兔儿跳跃在代码田,
日期上限定新篇。
预设禁用不再乱,
测试验证保安全。
maxDate 护航选日间,
选择安心乐无边!
🌸📅✨
"""

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/PickerInput/Popup/PresetPanel.tsx

Oops! Something went wrong! :(

ESLint: 8.57.1

Error: Cannot read config file: /.eslintrc.js
Error: Cannot find module '@umijs/fabric/dist/eslint'
Require stack:

  • /.eslintrc.js
  • /node_modules/.pnpm/@eslint[email protected]/node_modules/@eslint/eslintrc/dist/eslintrc.cjs
  • /node_modules/.pnpm/[email protected]/node_modules/eslint/lib/cli-engine/cli-engine.js
  • /node_modules/.pnpm/[email protected]/node_modules/eslint/lib/eslint/eslint.js
  • /node_modules/.pnpm/[email protected]/node_modules/eslint/lib/eslint/index.js
  • /node_modules/.pnpm/[email protected]/node_modules/eslint/lib/cli.js
  • /node_modules/.pnpm/[email protected]/node_modules/eslint/bin/eslint.js
    at Module._resolveFilename (node:internal/modules/cjs/loader:1405:15)
    at require.resolve (node:internal/modules/helpers:145:19)
    at Object. (/.eslintrc.js:2:21)
    at Module._compile (node:internal/modules/cjs/loader:1734:14)
    at Object..js (node:internal/modules/cjs/loader:1899:10)
    at Module.load (node:internal/modules/cjs/loader:1469:32)
    at Module._load (node:internal/modules/cjs/loader:1286:12)
    at TracingChannel.traceSync (node:diagnostics_channel:322:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:235:24)
    at Module.require (node:internal/modules/cjs/loader:1491:12)

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a76aa6 and 201eb0b.

📒 Files selected for processing (1)
  • src/PickerInput/Popup/PresetPanel.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/PickerInput/Popup/PresetPanel.tsx
✨ Finishing Touches
  • 📝 Generate Docstrings

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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

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

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 generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this 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: 3

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e289d5 and 1e172c4.

📒 Files selected for processing (5)
  • src/PickerInput/Popup/PresetPanel.tsx (3 hunks)
  • src/PickerInput/Popup/index.tsx (3 hunks)
  • src/PickerInput/RangePicker.tsx (1 hunks)
  • src/PickerInput/SinglePicker.tsx (1 hunks)
  • tests/picker.spec.tsx (1 hunks)
🔇 Additional comments (6)
src/PickerInput/SinglePicker.tsx (1)

524-524: 代码修改看起来不错!

正确地将 maxDate 属性传递给了 Popup 组件。

src/PickerInput/Popup/index.tsx (2)

37-38: 接口定义正确!

正确地为 PopupProps 接口添加了可选的 maxDate 属性。


72-73: 属性传递实现正确!

正确地从 props 中解构了 maxDate 并传递给 PresetPanel 组件。

Also applies to: 151-151

tests/picker.spec.tsx (1)

1118-1143: 测试用例编写得很好!

测试清晰地验证了当预设值超过 maxDate 时,不会触发 onChangeonHover 回调的行为。

src/PickerInput/Popup/PresetPanel.tsx (1)

3-3:

❌ Incorrect review comment

使用一致的日期库

项目中其他地方使用的是 dayjs(如测试文件所示),但这里引入了 moment。为保持一致性,建议使用相同的日期库。

Also applies to: 30-30


🏁 Script executed:

#!/bin/bash
# 检查项目中使用的日期库
echo "=== 检查 dayjs 的使用情况 ==="
rg -A 2 "import.*dayjs" --type ts --type tsx

echo -e "\n=== 检查 moment 的使用情况 ==="
rg -A 2 "import.*moment" --type ts --type tsx

echo -e "\n=== 检查 package.json 中的依赖 ==="
cat package.json | jq '.dependencies, .devDependencies' | grep -E "(dayjs|moment)"

Length of output: 467


Ripgrep 未识别 tsx 类型,导致第一次搜索不生效。我们需要在所有 TypeScript/JavaScript 文件中全局查找 dayjsmoment 的导入,确认项目中实际使用情况,请执行:


🏁 Script executed:

#!/bin/bash
echo "=== 检查 dayjs 导入 ==="
rg -A2 "import.*dayjs" -g '*.ts*' -g '*.js*'

echo -e "\n=== 检查 moment 导入 ==="
rg -A2 "import.*moment" -g '*.ts*' -g '*.js*'

Length of output: 12359


忽略统一为 dayjs 的建议

项目设计上支持多种日期库(dayjs、moment、luxon 等),PresetPanel.tsx 中引入 moment 是为了为 Moment 类型提供专属预设面板。以下位置也使用了 moment,与之保持一致更合理:

  • src/generate/moment.ts
  • 测试文件:tests/picker.spec.tsx、tests/generate.spec.tsx、tests/util/commonUtil.tsx 等
  • 文档示例:docs/examples 下多处示例均引入 moment

无需将该处替换为 dayjs。

Likely an incorrect or invalid review comment.

src/PickerInput/RangePicker.tsx (1)

595-595: 新增 maxDate 属性以支持禁用超出最大日期的预设项
此处在 <Popup> 组件上显式传递了 maxDate={maxDate},用以让下层 PresetPanel 根据上限日期禁用对应的预设选项。请确认:

  1. PopupProps 接口已同步扩展,包含对 maxDate 的类型声明。
  2. PresetPanel 中对该属性的逻辑处理已正确实现并有对应单元测试覆盖。

Comment on lines 17 to +20
export default function PresetPanel<DateType extends object = any>(
props: PresetPanelProps<DateType>,
) {
const { prefixCls, presets, onClick, onHover } = props;
const { prefixCls, presets, onClick, onHover, maxDate } = props;
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

需要 generateConfig 进行日期比较

为了与项目其他部分保持一致并支持不同的日期库,建议通过 props 传入 generateConfig

 export interface PresetPanelProps<ValueType = any> {
   prefixCls: string;
   presets: ValueDate<ValueType>[];
   onClick: (value: ValueType) => void;
   onHover: (value: ValueType) => void;
-  maxDate?: DateType;
+  maxDate?: ValueType;
+  generateConfig: GenerateConfig<ValueType>;
 }

 export default function PresetPanel<DateType extends object = any>(
   props: PresetPanelProps<DateType>,
 ) {
-  const { prefixCls, presets, onClick, onHover, maxDate } = props;
+  const { prefixCls, presets, onClick, onHover, maxDate, generateConfig } = props;

还需要在调用此组件的地方传入 generateConfig


🏁 Script executed:

#!/bin/bash
# 检查 PresetPanel 的使用位置,确认是否可以访问 generateConfig
echo "=== 查找 PresetPanel 的使用位置 ==="
rg "PresetPanel" --type ts --type tsx -A 5 -B 5

echo -e "\n=== 检查 generateConfig 的使用模式 ==="
ast-grep --pattern 'generateConfig'

Length of output: 33299


🏁 Script executed:

#!/bin/bash
echo "=== Inspect PresetPanel.tsx ==="
sed -n '1,200p' src/PickerInput/Popup/PresetPanel.tsx || true

echo -e "\n=== Search for maxDate usage ==="
grep -R "maxDate" -n src/PickerInput/Popup/PresetPanel.tsx || true

Length of output: 1930


需要为 PresetPanel 添加 generateConfig 支持,移除对 moment 的直接依赖

为了与项目其他地方保持一致并支持多种日期库,建议对 src/PickerInput/Popup/PresetPanel.tsx 做以下修改:

• 在 Props 接口中:

  • 修正错误的 maxDate?: dateTy;maxDate?: ValueType;
  • 新增 generateConfig: GenerateConfig<ValueType>;

• 在组件签名和解构 props 时引入 generateConfig

• 将

const isDisabled = moment(value).isAfter(maxDate) || moment(value).isSame(maxDate);

替换为

const rawValue = executeValue(value);
const isDisabled =
  generateConfig.isAfter(rawValue, maxDate) ||
  generateConfig.isSame(rawValue, maxDate);

• 在调用此组件的地方(如 PopupPanel.tsx)传入 generateConfig

示例 diff:

--- a/src/PickerInput/Popup/PresetPanel.tsx
+++ b/src/PickerInput/Popup/PresetPanel.tsx
@@
-export interface PresetPanelProps<ValueType = any> {
+export interface PresetPanelProps<ValueType = any> {
   prefixCls: string;
   presets: ValueDate<ValueType>[];
   onClick: (value: ValueType) => void;
   onHover: (value: ValueType) => void;
-  maxDate?: dateTy;
+  maxDate?: ValueType;
+  generateConfig: GenerateConfig<ValueType>;
 }

-export default function PresetPanel<DateType extends object = any>(
-  props: PresetPanelProps<DateType>,
-) {
-  const { prefixCls, presets, onClick, onHover, maxDate } = props;
+export default function PresetPanel<DateType extends object = any>(
+  props: PresetPanelProps<DateType>,
+) {
+  const { prefixCls, presets, onClick, onHover, maxDate, generateConfig } = props;

@@
-        const isDisabled = moment(value).isAfter(maxDate) || moment(value).isSame(maxDate);
+        const raw = executeValue(value);
+        const isDisabled =
+          generateConfig.isAfter(raw, maxDate) ||
+          generateConfig.isSame(raw, maxDate);

请同时在使用 <PresetPanel> 的所有调用处(如 PopupPanel)传入当前的 generateConfig

🤖 Prompt for AI Agents
In src/PickerInput/Popup/PresetPanel.tsx lines 17 to 20, the component currently
does not accept generateConfig as a prop and directly uses moment for date
comparison, which reduces flexibility and consistency. To fix this, add
generateConfig: GenerateConfig<ValueType> to the component's props interface,
destructure it from props, and replace the moment-based date comparison with
generateConfig.isAfter and generateConfig.isSame calls using the computed
rawValue. Also, ensure that all usages of PresetPanel pass in the generateConfig
prop to maintain proper functionality.

Comment on lines 29 to 51
{presets.map(({ label, value }, index) => {
const isDisabled = moment(value).isAfter(maxDate) || moment(value).isSame(maxDate);
return (
<li
key={index}
onClick={() => {
if (!isDisabled) {
onClick(executeValue(value));
}
}}
onMouseEnter={() => {
if (!isDisabled) {
onHover(executeValue(value));
}
}}
onMouseLeave={() => {
onHover(null);
}}
>
{label}
</li>
);
})}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

修复禁用逻辑和处理函数值

当前实现存在几个问题需要修复:

  1. 根据 PR 目标,应该只禁用大于 maxDate 的日期,而不是大于等于
  2. 没有处理 value 为函数的情况
  3. 没有为禁用的预设项添加视觉指示
-        {presets.map(({ label, value }, index) => {
-          const isDisabled = moment(value).isAfter(maxDate) || moment(value).isSame(maxDate);
-          return (
-            <li
-              key={index}
-              onClick={() => {
-                if (!isDisabled) {
-                  onClick(executeValue(value));
-                }
-              }}
-              onMouseEnter={() => {
-                if (!isDisabled) {
-                  onHover(executeValue(value));
-                }
-              }}
-              onMouseLeave={() => {
-                onHover(null);
-              }}
-            >
-              {label}
-            </li>
-          );
-        })}
+        {presets.map(({ label, value }, index) => {
+          const actualValue = executeValue(value);
+          const isDisabled = maxDate && generateConfig.isAfter(actualValue, maxDate);
+          return (
+            <li
+              key={index}
+              className={isDisabled ? `${prefixCls}-presets-item-disabled` : undefined}
+              onClick={() => {
+                if (!isDisabled) {
+                  onClick(actualValue);
+                }
+              }}
+              onMouseEnter={() => {
+                if (!isDisabled) {
+                  onHover(actualValue);
+                }
+              }}
+              onMouseLeave={() => {
+                onHover(null);
+              }}
+            >
+              {label}
+            </li>
+          );
+        })}

注意:这里假设组件可以访问 generateConfig。如果不能,需要将其作为 prop 传入。

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

🤖 Prompt for AI Agents
In src/PickerInput/Popup/PresetPanel.tsx lines 29 to 51, the code checks if a
preset date is after or the same as maxDate to disable it, but the logic should
only disable dates strictly after maxDate. Also, the code does not handle cases
where value might be a function, nor does it visually indicate disabled items.
Fix this by updating the isDisabled condition to only check if value is after
maxDate, add handling for function values, and add a disabled style or attribute
to visually distinguish disabled items.

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/picker.spec.tsx (1)

1200-1224: 建议增加更多测试场景

当前测试覆盖了预设值超过 maxDate 的情况。建议额外测试以下场景以提高覆盖率:

  1. 预设值在 maxDate 范围内时,应该正常触发回调
  2. 同时设置 minDatemaxDate 时的边界情况
  3. RangePicker 组件中的类似行为
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e172c4 and ac97da7.

📒 Files selected for processing (4)
  • src/PickerInput/Popup/index.tsx (3 hunks)
  • src/PickerInput/RangePicker.tsx (1 hunks)
  • src/PickerInput/SinglePicker.tsx (1 hunks)
  • tests/picker.spec.tsx (1 hunks)
🔇 Additional comments (5)
src/PickerInput/SinglePicker.tsx (1)

537-537: 代码变更正确!

正确地将 maxDate 属性传递给 Popup 组件,使得子组件能够访问最大日期限制。

src/PickerInput/Popup/index.tsx (2)

47-48: 属性传递实现正确!

代码变更遵循了现有的模式:

  • PopupProps 接口中正确添加了可选的 maxDate 属性
  • 从 props 中解构 maxDate
  • 将其传递给 PresetPanel 组件

这确保了 PresetPanel 能够基于最大日期限制来禁用相应的预设选项。

Also applies to: 84-85, 188-188


188-188:

❓ Verification inconclusive

建议验证 PresetPanel 的实现

虽然 maxDate 被正确传递给了 PresetPanel,但建议确认 PresetPanel 组件已正确实现了基于 maxDate 禁用预设项的逻辑。

运行以下脚本来验证 PresetPanel 的实现:


🏁 Script executed:

#!/bin/bash
# 描述:检查 PresetPanel 组件是否正确处理 maxDate 属性

# 查找 PresetPanel 组件的实现
fd "PresetPanel" --type f --extension tsx --extension ts --exec rg -A 20 "maxDate" {} \;

# 查找 PresetPanel 中与禁用逻辑相关的代码
ast-grep --pattern 'const PresetPanel = $_' src/PickerInput/Popup/PresetPanel.tsx

Length of output: 1306


确认 PresetPanel 的 maxDate 禁用逻辑

已验证 src/PickerInput/Popup/PresetPanel.tsx 中对 maxDate 的处理逻辑:

  • 在渲染预设项时,通过 moment(value).isAfter(maxDate) || moment(value).isSame(maxDate) 判断并禁用大于或等于 maxDate 的值。

请确认以下事项:

  • 预期是否要同时禁用等于 maxDate 的预设项?
    • 如果应允许等于 maxDate,建议将禁用条件修改为仅 moment(value).isAfter(maxDate)
tests/picker.spec.tsx (1)

1200-1224: 测试用例实现完善!

新增的测试用例正确验证了当预设日期超过 maxDate 时的行为:

  • 设置明天作为预设值,今天作为 maxDate,使预设项被禁用
  • 验证了鼠标悬停和点击事件都不会触发相应的回调函数
  • 测试逻辑清晰,符合现有的测试模式

这个测试用例很好地覆盖了 PR 要解决的核心问题。

src/PickerInput/RangePicker.tsx (1)

624-624: 正确的属性传递实现

向 Popup 组件添加 maxDate 属性是正确的实现。这个修改使得 Popup 组件能够感知最大允许日期,从而支持禁用超出最大日期的预设项功能,与 PR 目标完全一致。

Copy link

codecov bot commented May 29, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.80%. Comparing base (0e289d5) to head (201eb0b).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #930   +/-   ##
=======================================
  Coverage   98.80%   98.80%           
=======================================
  Files          65       65           
  Lines        2669     2675    +6     
  Branches      737      742    +5     
=======================================
+ Hits         2637     2643    +6     
  Misses         29       29           
  Partials        3        3           

☔ 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.

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