mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-12-02 12:09:14 +08:00
feat: Calendar onSelect
support info.source
param (#42432)
* test: add test case * test: add test case * docs: update api * docs: faq update
This commit is contained in:
parent
4efa0b64db
commit
71fc54e53a
@ -5,7 +5,7 @@ import { useContext, useMemo } from 'react';
|
||||
import { FormItemInputContext } from '../form/context';
|
||||
import { Button, Group } from '../radio';
|
||||
import Select from '../select';
|
||||
import type { CalendarMode } from './generateCalendar';
|
||||
import type { CalendarMode, SelectInfo } from './generateCalendar';
|
||||
|
||||
const YearSelectOffset = 10;
|
||||
const YearSelectTotal = 20;
|
||||
@ -147,7 +147,7 @@ export interface CalendarHeaderProps<DateType> {
|
||||
locale: Locale;
|
||||
mode: CalendarMode;
|
||||
fullscreen: boolean;
|
||||
onChange: (date: DateType) => void;
|
||||
onChange: (date: DateType, source: SelectInfo['source']) => void;
|
||||
onModeChange: (mode: CalendarMode) => void;
|
||||
}
|
||||
function CalendarHeader<DateType>(props: CalendarHeaderProps<DateType>) {
|
||||
@ -165,7 +165,6 @@ function CalendarHeader<DateType>(props: CalendarHeaderProps<DateType>) {
|
||||
|
||||
const sharedProps = {
|
||||
...props,
|
||||
onChange,
|
||||
fullscreen,
|
||||
divRef,
|
||||
};
|
||||
@ -173,8 +172,20 @@ function CalendarHeader<DateType>(props: CalendarHeaderProps<DateType>) {
|
||||
return (
|
||||
<div className={`${prefixCls}-header`} ref={divRef}>
|
||||
<FormItemInputContext.Provider value={mergedFormItemInputContext}>
|
||||
<YearSelect {...sharedProps} />
|
||||
{mode === 'month' && <MonthSelect {...sharedProps} />}
|
||||
<YearSelect
|
||||
{...sharedProps}
|
||||
onChange={(v) => {
|
||||
onChange(v, 'year');
|
||||
}}
|
||||
/>
|
||||
{mode === 'month' && (
|
||||
<MonthSelect
|
||||
{...sharedProps}
|
||||
onChange={(v) => {
|
||||
onChange(v, 'month');
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</FormItemInputContext.Provider>
|
||||
<ModeSwitch {...sharedProps} onModeChange={onModeChange} />
|
||||
</div>
|
||||
|
@ -76,7 +76,7 @@ describe('Calendar', () => {
|
||||
const { container } = render(<Calendar onSelect={onSelect} onChange={onChange} />);
|
||||
|
||||
fireEvent.click(container.querySelector('.ant-picker-cell')!);
|
||||
expect(onSelect).toHaveBeenCalledWith(expect.anything());
|
||||
expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'date' });
|
||||
|
||||
const value = onSelect.mock.calls[0][0];
|
||||
expect(Dayjs.isDayjs(value)).toBe(true);
|
||||
@ -270,7 +270,7 @@ describe('Calendar', () => {
|
||||
const end = Dayjs('2019-11-01');
|
||||
const onValueChange = jest.fn();
|
||||
createWrapper(start, end, value, onValueChange);
|
||||
expect(onValueChange).toHaveBeenCalledWith(value.year(2019).month(3));
|
||||
expect(onValueChange).toHaveBeenCalledWith(value.year(2019).month(3), 'year');
|
||||
});
|
||||
|
||||
it('if start.month > value.month, set value.month to start.month', () => {
|
||||
@ -279,7 +279,7 @@ describe('Calendar', () => {
|
||||
const end = Dayjs('2019-03-01');
|
||||
const onValueChange = jest.fn();
|
||||
createWrapper(start, end, value, onValueChange);
|
||||
expect(onValueChange).toHaveBeenCalledWith(value.year(2019).month(10));
|
||||
expect(onValueChange).toHaveBeenCalledWith(value.year(2019).month(10), 'year');
|
||||
});
|
||||
|
||||
it('if change year and month > end month, set value.month to end.month', () => {
|
||||
@ -302,7 +302,7 @@ describe('Calendar', () => {
|
||||
fireEvent.click(
|
||||
Array.from(wrapper.container.querySelectorAll('.ant-select-item-option')).at(-1)!,
|
||||
);
|
||||
expect(onValueChange).toHaveBeenCalledWith(value.year(2019).month(2));
|
||||
expect(onValueChange).toHaveBeenCalledWith(value.year(2019).month(2), 'year');
|
||||
});
|
||||
|
||||
it('onMonthChange should work correctly', () => {
|
||||
@ -324,7 +324,7 @@ describe('Calendar', () => {
|
||||
);
|
||||
openSelect(wrapper.container, '.ant-picker-calendar-month-select');
|
||||
clickSelectItem(wrapper.container);
|
||||
expect(onValueChange).toHaveBeenCalledWith(value.month(10));
|
||||
expect(onValueChange).toHaveBeenCalledWith(value.month(10), 'month');
|
||||
});
|
||||
|
||||
it('onTypeChange should work correctly', () => {
|
||||
|
95
components/calendar/__tests__/select.test.tsx
Normal file
95
components/calendar/__tests__/select.test.tsx
Normal file
@ -0,0 +1,95 @@
|
||||
import Dayjs from 'dayjs';
|
||||
import 'dayjs/locale/zh-cn';
|
||||
import MockDate from 'mockdate';
|
||||
import { resetWarned } from 'rc-util/lib/warning';
|
||||
import React from 'react';
|
||||
import Calendar from '..';
|
||||
import { fireEvent, render, waitFakeTimer } from '../../../tests/utils';
|
||||
|
||||
describe('Calendar.onSelect', () => {
|
||||
beforeAll(() => {
|
||||
MockDate.set(Dayjs('2000-01-01').valueOf());
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
resetWarned();
|
||||
jest.useFakeTimers();
|
||||
jest.clearAllTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('source of year select', async () => {
|
||||
const onSelect = jest.fn();
|
||||
const { container } = render(<Calendar onSelect={onSelect} />);
|
||||
|
||||
fireEvent.mouseDown(container.querySelector('.ant-select-selector')!);
|
||||
await waitFakeTimer();
|
||||
|
||||
fireEvent.click(container.querySelector('.ant-select-item-option')!);
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'year' });
|
||||
});
|
||||
|
||||
it('source of month select', async () => {
|
||||
const onSelect = jest.fn();
|
||||
const { container } = render(<Calendar onSelect={onSelect} />);
|
||||
|
||||
fireEvent.mouseDown(container.querySelectorAll('.ant-select-selector')[1]!);
|
||||
await waitFakeTimer();
|
||||
|
||||
fireEvent.click(container.querySelector('.ant-select-item-option')!);
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'month' });
|
||||
});
|
||||
|
||||
it('source of customize', async () => {
|
||||
const onSelect = jest.fn();
|
||||
const { container } = render(
|
||||
<Calendar
|
||||
onSelect={onSelect}
|
||||
headerRender={({ onChange }) => (
|
||||
<button
|
||||
className="bamboo"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
onChange(Dayjs('1999-01-01'));
|
||||
}}
|
||||
>
|
||||
Trigger
|
||||
</button>
|
||||
)}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(container.querySelector('.bamboo')!);
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'customize' });
|
||||
});
|
||||
|
||||
it('source of date', () => {
|
||||
const onSelect = jest.fn();
|
||||
const { container } = render(<Calendar onSelect={onSelect} />);
|
||||
|
||||
fireEvent.click(container.querySelector('.ant-picker-cell')!);
|
||||
expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'date' });
|
||||
});
|
||||
|
||||
it('source of date with month panel', async () => {
|
||||
const onSelect = jest.fn();
|
||||
const onPanelChange = jest.fn();
|
||||
const { container } = render(<Calendar onSelect={onSelect} onPanelChange={onPanelChange} />);
|
||||
|
||||
// Click year radio
|
||||
fireEvent.click(container.querySelectorAll('.ant-radio-button-input')[1]!);
|
||||
expect(onPanelChange).toHaveBeenCalledWith(expect.anything(), 'year');
|
||||
|
||||
fireEvent.click(container.querySelector('.ant-picker-cell')!);
|
||||
expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'date' });
|
||||
});
|
||||
});
|
@ -1,12 +1,12 @@
|
||||
import classNames from 'classnames';
|
||||
import { PickerPanel as RCPickerPanel } from 'rc-picker';
|
||||
import type { GenerateConfig } from 'rc-picker/lib/generate';
|
||||
import type { CellRenderInfo } from 'rc-picker/lib/interface';
|
||||
import type {
|
||||
PickerPanelBaseProps as RCPickerPanelBaseProps,
|
||||
PickerPanelDateProps as RCPickerPanelDateProps,
|
||||
PickerPanelTimeProps as RCPickerPanelTimeProps,
|
||||
} from 'rc-picker/lib/PickerPanel';
|
||||
import type { GenerateConfig } from 'rc-picker/lib/generate';
|
||||
import type { CellRenderInfo } from 'rc-picker/lib/interface';
|
||||
import useMergedState from 'rc-util/lib/hooks/useMergedState';
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
@ -14,8 +14,8 @@ import { useLocale } from '../locale';
|
||||
import CalendarHeader from './Header';
|
||||
import enUS from './locale/en_US';
|
||||
|
||||
import useStyle from './style';
|
||||
import warning from '../_util/warning';
|
||||
import useStyle from './style';
|
||||
|
||||
type InjectDefaultProps<Props> = Omit<
|
||||
Props,
|
||||
@ -43,6 +43,10 @@ export type HeaderRender<DateType> = (config: {
|
||||
onTypeChange: (type: CalendarMode) => void;
|
||||
}) => React.ReactNode;
|
||||
|
||||
export interface SelectInfo {
|
||||
source: 'year' | 'month' | 'date' | 'customize';
|
||||
}
|
||||
|
||||
export interface CalendarProps<DateType> {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
@ -68,7 +72,7 @@ export interface CalendarProps<DateType> {
|
||||
fullscreen?: boolean;
|
||||
onChange?: (date: DateType) => void;
|
||||
onPanelChange?: (date: DateType, mode: CalendarMode) => void;
|
||||
onSelect?: (date: DateType) => void;
|
||||
onSelect?: (date: DateType, selectInfo: SelectInfo) => void;
|
||||
}
|
||||
|
||||
function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) {
|
||||
@ -198,10 +202,10 @@ function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) {
|
||||
triggerPanelChange(mergedValue, newMode);
|
||||
};
|
||||
|
||||
const onInternalSelect = (date: DateType) => {
|
||||
const onInternalSelect = (date: DateType, source: SelectInfo['source']) => {
|
||||
triggerChange(date);
|
||||
|
||||
onSelect?.(date);
|
||||
onSelect?.(date, { source });
|
||||
};
|
||||
|
||||
// ====================== Locale ======================
|
||||
@ -310,7 +314,9 @@ function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) {
|
||||
headerRender({
|
||||
value: mergedValue,
|
||||
type: mergedMode,
|
||||
onChange: onInternalSelect,
|
||||
onChange: (nextDate) => {
|
||||
onInternalSelect(nextDate, 'customize');
|
||||
},
|
||||
onTypeChange: triggerModeChange,
|
||||
})
|
||||
) : (
|
||||
@ -332,7 +338,9 @@ function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) {
|
||||
locale={contextLocale?.lang}
|
||||
generateConfig={generateConfig}
|
||||
cellRender={mergedCellRender}
|
||||
onSelect={onInternalSelect}
|
||||
onSelect={(nextDate) => {
|
||||
onInternalSelect(nextDate, 'date');
|
||||
}}
|
||||
mode={panelMode}
|
||||
picker={panelMode}
|
||||
disabledDate={mergedDisabledDate}
|
||||
|
@ -55,7 +55,7 @@ When data is in the form of dates, such as schedules, timetables, prices calenda
|
||||
| value | The current selected date | [dayjs](https://day.js.org/) | - | |
|
||||
| onChange | Callback for when date changes | function(date: Dayjs) | - | |
|
||||
| onPanelChange | Callback for when panel changes | function(date: Dayjs, mode: string) | - | |
|
||||
| onSelect | Callback for when a date is selected | function(date: Dayjs) | - | |
|
||||
| onSelect | Callback for when a date is selected, include source info | function(date: Dayjs, info: { source: 'year' \| 'month' \| 'date' \| 'customize' }) | - | `info`: 5.6.0 |
|
||||
|
||||
## Design Token
|
||||
|
||||
@ -74,3 +74,17 @@ See [How to set locale for date-related components](/components/date-picker/#loc
|
||||
### Date-related components locale is not working?
|
||||
|
||||
See FAQ [Date-related-components-locale-is-not-working?](/docs/react/faq#date-related-components-locale-is-not-working)
|
||||
|
||||
### How to get date from panel click?
|
||||
|
||||
`onSelect` provide `info.source` to help on this:
|
||||
|
||||
```tsx
|
||||
<Calendar
|
||||
onSelect={(date, { source }) => {
|
||||
if (source === 'date') {
|
||||
console.log('Panel Select:', source);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
@ -60,7 +60,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*-p-wQLik200AAA
|
||||
| value | 展示日期 | [dayjs](https://day.js.org/) | - | |
|
||||
| onChange | 日期变化回调 | function(date: Dayjs) | - | |
|
||||
| onPanelChange | 日期面板变化回调 | function(date: Dayjs, mode: string) | - | |
|
||||
| onSelect | 点击选择日期回调 | function(date: Dayjs) | - | |
|
||||
| onSelect | 选择日期回调,包含来源信息 | function(date: Dayjs, info: { source: 'year' \| 'month' \| 'date' \| 'customize' }) | - | `info`: 5.6.0 |
|
||||
|
||||
## Design Token
|
||||
|
||||
@ -79,3 +79,17 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*-p-wQLik200AAA
|
||||
### 为什么时间类组件的国际化 locale 设置不生效?
|
||||
|
||||
参考 FAQ [为什么时间类组件的国际化 locale 设置不生效?](/docs/react/faq#为什么时间类组件的国际化-locale-设置不生效)。
|
||||
|
||||
### 如何仅获取来自面板点击的日期?
|
||||
|
||||
`onSelect` 事件提供额外的来源信息,你可以通过 `info.source` 来判断来源:
|
||||
|
||||
```tsx
|
||||
<Calendar
|
||||
onSelect={(date, { source }) => {
|
||||
if (source === 'date') {
|
||||
console.log('Panel Select:', source);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
Loading…
Reference in New Issue
Block a user