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
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 26 additions & 17 deletions src/PickerInput/Popup/PresetPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import * as React from 'react';
import type { ValueDate } from '../../interface';
import moment from 'moment';

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

function executeValue<ValueType extends object>(value: ValueDate<ValueType>['value']): ValueType {
Expand All @@ -15,7 +17,7 @@ function executeValue<ValueType extends object>(value: ValueDate<ValueType>['val
export default function PresetPanel<DateType extends object = any>(
props: PresetPanelProps<DateType>,
) {
const { prefixCls, presets, onClick, onHover } = props;
const { prefixCls, presets, onClick, onHover, maxDate } = props;
Comment on lines 17 to +20
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.


if (!presets.length) {
return null;
Expand All @@ -24,22 +26,29 @@ export default function PresetPanel<DateType extends object = any>(
return (
<div className={`${prefixCls}-presets`}>
<ul>
{presets.map(({ label, value }, index) => (
<li
key={index}
onClick={() => {
onClick(executeValue(value));
}}
onMouseEnter={() => {
onHover(executeValue(value));
}}
onMouseLeave={() => {
onHover(null);
}}
>
{label}
</li>
))}
{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.

</ul>
</div>
);
Expand Down
5 changes: 5 additions & 0 deletions src/PickerInput/Popup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export interface PopupProps<DateType extends object = any, PresetValue = DateTyp
isInvalid: (date: DateType | DateType[]) => boolean;
onOk: VoidFunction;


maxDate?: DateType;
onPanelMouseDown?: React.MouseEventHandler<HTMLDivElement>;
}

Expand Down Expand Up @@ -79,6 +81,8 @@ export default function Popup<DateType extends object = any>(props: PopupProps<D
defaultOpenValue,
onOk,
onSubmit,

maxDate,
} = props;

const { prefixCls } = React.useContext(PickerContext);
Expand Down Expand Up @@ -181,6 +185,7 @@ export default function Popup<DateType extends object = any>(props: PopupProps<D
<div className={`${prefixCls}-panel-layout`}>
{/* `any` here since PresetPanel is reused for both Single & Range Picker which means return type is not stable */}
<PresetPanel<any>
maxDate={maxDate}
prefixCls={prefixCls}
presets={presets}
onClick={onPresetSubmit}
Expand Down
1 change: 1 addition & 0 deletions src/PickerInput/RangePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,7 @@ function RangePicker<DateType extends object = any>(
onNow={onNow}
// Render
cellRender={onInternalCellRender}
maxDate={maxDate}
/>
);

Expand Down
1 change: 1 addition & 0 deletions src/PickerInput/SinglePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,7 @@ function Picker<DateType extends object = any>(
onNow={onNow}
// Render
cellRender={onInternalCellRender}
maxDate={maxDate}
/>
);

Expand Down
26 changes: 26 additions & 0 deletions tests/picker.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1197,6 +1197,32 @@ describe('Picker.Basic', () => {
expect(onChange.mock.calls[0][0].format('YYYY-MM-DD')).toEqual('1990-09-04');
});

it('presets - disabled does not trigger', () => {
const onChange = jest.fn();
const onHover = jest.fn();

const futureDate = dayjs().add(1, 'day');
render(
<DayPicker
onChange={onChange}
onHover={onHover}
open
presets={[{ label: 'Future', value: futureDate }]}
maxDate={dayjs()} // maxDate 是今天,futureDate 是明天,禁用
/>,
);

const presetEle = document.querySelector('.rc-picker-presets li');

// Hover
fireEvent.mouseEnter(presetEle);
expect(onHover).not.toHaveBeenCalled();

// Click
fireEvent.click(presetEle);
expect(onChange).not.toHaveBeenCalled();
});

it('presets support callback', () => {
const onChange = jest.fn();
const mockPresetValue = jest.fn().mockImplementationOnce(() => getDay('2000-09-03'));
Expand Down