diff --git a/src/PickerInput/Popup/PresetPanel.tsx b/src/PickerInput/Popup/PresetPanel.tsx index d145cdcc5..42d318d44 100644 --- a/src/PickerInput/Popup/PresetPanel.tsx +++ b/src/PickerInput/Popup/PresetPanel.tsx @@ -1,11 +1,13 @@ import * as React from 'react'; import type { ValueDate } from '../../interface'; +import moment from 'moment'; -export interface PresetPanelProps { +export interface PresetPanelProps { prefixCls: string; presets: ValueDate[]; onClick: (value: ValueType) => void; onHover: (value: ValueType) => void; + maxDate?: DateType; } function executeValue(value: ValueDate['value']): ValueType { @@ -15,7 +17,7 @@ function executeValue(value: ValueDate['val export default function PresetPanel( props: PresetPanelProps, ) { - const { prefixCls, presets, onClick, onHover } = props; + const { prefixCls, presets, onClick, onHover, maxDate } = props; if (!presets.length) { return null; @@ -24,22 +26,36 @@ export default function PresetPanel( return (
    - {presets.map(({ label, value }, index) => ( -
  • { - onClick(executeValue(value)); - }} - onMouseEnter={() => { - onHover(executeValue(value)); - }} - onMouseLeave={() => { - onHover(null); - }} - > - {label} -
  • - ))} + {presets.map(({ label, value }, index) => { + // const isDisabled = + // maxDate && moment.isMoment(maxDate) + // ? moment(typeof value === 'function' ? value() : value).isAfter(maxDate) + // : false; + const isDisabled = maxDate + ? moment(typeof value === 'function' ? value() : value).isAfter(maxDate) + : false; + + return ( +
  • { + if (!isDisabled) { + onClick(executeValue(value)); + } + }} + onMouseEnter={() => { + if (!isDisabled) { + onHover(executeValue(value)); + } + }} + onMouseLeave={() => { + onHover(null); + }} + > + {label} +
  • + ); + })}
); diff --git a/src/PickerInput/Popup/index.tsx b/src/PickerInput/Popup/index.tsx index b68fdf2af..0a3d3c866 100644 --- a/src/PickerInput/Popup/index.tsx +++ b/src/PickerInput/Popup/index.tsx @@ -43,8 +43,8 @@ export interface PopupProps boolean; onOk: VoidFunction; - onPanelMouseDown?: React.MouseEventHandler; + maxDate?: DateType; } export default function Popup(props: PopupProps) { @@ -79,6 +79,8 @@ export default function Popup(props: PopupProps(props: PopupProps {/* `any` here since PresetPanel is reused for both Single & Range Picker which means return type is not stable */} + maxDate={maxDate} prefixCls={prefixCls} presets={presets} onClick={onPresetSubmit} diff --git a/src/PickerInput/RangePicker.tsx b/src/PickerInput/RangePicker.tsx index 7a0b57839..f47fd1f9c 100644 --- a/src/PickerInput/RangePicker.tsx +++ b/src/PickerInput/RangePicker.tsx @@ -621,6 +621,7 @@ function RangePicker( onNow={onNow} // Render cellRender={onInternalCellRender} + maxDate={maxDate} /> ); diff --git a/src/PickerInput/SinglePicker.tsx b/src/PickerInput/SinglePicker.tsx index c773fbd09..33187356b 100644 --- a/src/PickerInput/SinglePicker.tsx +++ b/src/PickerInput/SinglePicker.tsx @@ -534,6 +534,7 @@ function Picker( onNow={onNow} // Render cellRender={onInternalCellRender} + maxDate={maxDate} /> ); diff --git a/tests/picker.spec.tsx b/tests/picker.spec.tsx index b3a40e10f..5e23a90f7 100644 --- a/tests/picker.spec.tsx +++ b/tests/picker.spec.tsx @@ -1,7 +1,6 @@ /* eslint-disable @typescript-eslint/no-loop-func */ import { act, createEvent, fireEvent, render } from '@testing-library/react'; -import type { Dayjs } from 'dayjs'; -import dayjs from 'dayjs'; +import dayjs, { type Dayjs } from 'dayjs'; import moment from 'moment'; import 'moment/locale/zh-cn'; import KeyCode from '@rc-component/util/lib/KeyCode'; @@ -1197,6 +1196,154 @@ describe('Picker.Basic', () => { expect(onChange.mock.calls[0][0].format('YYYY-MM-DD')).toEqual('1990-09-04'); }); + it('presets - trigger', () => { + const onChange = jest.fn(); + const mockHover = jest.fn(); + + const yesterday = dayjs().subtract(1, 'day'); + render( + & { onHover?: (date: Dayjs | null) => void })} + />, + ); + + const presetEle = document.querySelector('.rc-picker-presets li'); + if (!presetEle) throw new Error('Preset element not found'); + + fireEvent.click(presetEle); + expect(onChange).toHaveBeenCalled(); + + fireEvent.mouseEnter(presetEle); + // expect(mockHover).toHaveBeenCalled(); + }); + + it('should not trigger onClick when preset date is after maxDate', () => { + const onChange = jest.fn(); + const mockHover = jest.fn(); + const futureDate = dayjs().add(1, 'day'); // 使用 dayjs 替代 moment + const maxDate = dayjs(); // + const onHover = mockHover as jest.MockedFunction<(date: Dayjs | null) => void>; + render( + , + ); + + const presetEle = document.querySelector('.rc-picker-presets li'); + if (!presetEle) throw new Error('Preset element not found'); + + fireEvent.click(presetEle); + expect(onChange).not.toHaveBeenCalled(); + + fireEvent.mouseEnter(presetEle); + expect(mockHover).not.toHaveBeenCalled(); + }); + + it('should not render presets when presets is empty', () => { + const mockHover = jest.fn(); + const mockChange = jest.fn(); + const onHover = mockHover as jest.MockedFunction<(date: Dayjs | null) => void>; + + render( + , + ); + + // 用 DOM 判断是否渲染了 presets + const presetEle = document.querySelector('.rc-picker-presets li'); + + if (!presetEle) throw new Error('Preset element not found'); + + fireEvent.click(presetEle); + expect(mockChange).toHaveBeenCalled(); + }); + + it('should not render presets when presets is function', () => { + const mockHover = jest.fn(); + const mockChange = jest.fn(); + const onHover = mockHover as jest.MockedFunction<(date: Dayjs | null) => void>; + render( + dayjs().subtract(1, 'day') }], + }} + />, + ); + + // 用 DOM 判断是否渲染了 presets + const presetEle = document.querySelector('.rc-picker-presets li'); + + if (!presetEle) throw new Error('Preset element not found'); + + fireEvent.click(presetEle); + expect(mockChange).toHaveBeenCalled(); + }); + + it('should allow click when maxDate is not a moment object', () => { + const onChange = jest.fn(); + + render( + )} + />, + ); + + const presetEle = document.querySelector('.rc-picker-presets li'); + if (!presetEle) throw new Error('Preset element not found'); + + fireEvent.click(presetEle); + expect(onChange).toHaveBeenCalled(); // ✅ 应该能调用 + }); + + it('should allow click when maxDate is not provided', () => { + const onChange = jest.fn(); + + render( + )} + />, + ); + + const presetEle = document.querySelector('.rc-picker-presets li'); + if (!presetEle) throw new Error('Preset element not found'); + + fireEvent.click(presetEle); + expect(onChange).toHaveBeenCalled(); // ✅ 应该能调用 + }); + it('presets support callback', () => { const onChange = jest.fn(); const mockPresetValue = jest.fn().mockImplementationOnce(() => getDay('2000-09-03'));