Skip to content
8 changes: 5 additions & 3 deletions packages/components/date-picker/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@ import type {
DateValue,
DatePickerYearChangeTrigger,
DatePickerMonthChangeTrigger,
PresetDate,
} from './type';
import type { TagInputRemoveContext } from '../tag-input';

export default defineComponent({
name: 'TDatePicker',
props,
setup(props) {
setup(props, { slots }) {
const COMPONENT_NAME = usePrefixClass('date-picker');

const {
Expand Down Expand Up @@ -368,7 +369,7 @@ export default defineComponent({
}

// 预设
function onPresetClick(presetValue: DateValue | (() => DateValue)) {
function onPresetClick(presetValue: DateValue | (() => DateValue), context: { preset: PresetDate; e: MouseEvent }) {
const presetVal = isFunction(presetValue) ? presetValue() : presetValue;
onChange?.(
formatDate(presetVal, {
Expand All @@ -385,6 +386,7 @@ export default defineComponent({
format: formatRef.value.format,
});
popupVisible.value = false;
props.onPresetClick?.(context);
}

function onYearChange(nextYear: number) {
Expand Down Expand Up @@ -461,7 +463,7 @@ export default defineComponent({
popupVisible={!isReadOnly.value && popupVisible.value}
valueDisplay={() => renderTNodeJSX('valueDisplay', { params: valueDisplayParams.value })}
{...(props.selectInputProps as TdDatePickerProps['selectInputProps'])}
panel={() => <TSinglePanel {...panelProps.value} />}
panel={() => <TSinglePanel {...panelProps.value} v-slots={{ presets: slots.presets }} />}
tagInputProps={{
onRemove: onTagRemoveClick,
}}
Expand Down
2 changes: 1 addition & 1 deletion packages/components/date-picker/DateRangePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ export default defineComponent({
popupProps={popupProps.value}
rangeInputProps={rangeInputProps.value}
popupVisible={popupVisible.value}
panel={() => <TRangePanel {...panelProps.value} />}
panel={() => <TRangePanel {...panelProps.value} v-slots={{ presets: slots.presets }} />}
/>
</div>
);
Expand Down
96 changes: 96 additions & 0 deletions packages/components/date-picker/__tests__/date-picker.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,102 @@ describe('DatePicker', () => {
expect(wrapper.element).toMatchSnapshot();
});

it('DatePicker: the presets slot appears when the pop-up panel is opened', async () => {
const presetsSlotId = 'date-picker-presets-slot-content';
const attachClass = 'date-picker-presets-slot-attach';

const wrapper = mount({
render() {
return (
<div class={attachClass}>
<DatePicker popupProps={{ attach: `.${attachClass}` }}>
{{
presets: () => <span data-testid={presetsSlotId}>presets</span>,
}}
</DatePicker>
</div>
);
},
});

const trigger = wrapper.find('.t-input');
await trigger.trigger('mousedown');
await trigger.trigger('mouseup');
await trigger.trigger('click');
await nextTick();
await new Promise((resolve) => setTimeout(resolve, 0));

const presetsEl = wrapper.find(`[data-testid="${presetsSlotId}"]`);
expect(presetsEl.exists()).toBe(true);
expect(presetsEl.text()).toBe('presets');
});

it('DatePicker: clicking the preset button triggers preset-click and passes { preset, e }', async () => {
const onPresetClick = vi.fn();
const attachClass = 'date-picker-preset-click-attach';
const presets = { today: '2020-12-28', yesterday: '2020-12-27' };

const wrapper = mount({
render() {
return (
<div class={attachClass}>
<DatePicker presets={presets} onPresetClick={onPresetClick} popupProps={{ attach: `.${attachClass}` }} />
</div>
);
},
});

const trigger = wrapper.find('.t-input');
await trigger.trigger('mousedown');
await trigger.trigger('mouseup');
await trigger.trigger('click');
await nextTick();
await new Promise((resolve) => setTimeout(resolve, 0));

const buttons = wrapper.findAll('.t-button');
const todayButton = buttons.find((b) => b.text() === 'today');
expect(todayButton).toBeTruthy();
if (todayButton) await todayButton.trigger('click');
await nextTick();

expect(onPresetClick).toHaveBeenCalledTimes(1);
const context = onPresetClick.mock.calls[0][0];
expect(context).toHaveProperty('preset');
expect(context).toHaveProperty('e');
expect(context.preset).toEqual({ today: '2020-12-28' });
expect(context.e).toBeInstanceOf(MouseEvent);
});

it('DateRangePicker: the presets slot appears when the pop-up panel is opened', async () => {
const presetsSlotId = 'date-range-presets-slot-content';
const attachClass = 'date-range-presets-test-attach';

const wrapper = mount({
render() {
return (
<div class={attachClass}>
<DateRangePicker popupProps={{ attach: `.${attachClass}` }}>
{{
presets: () => <span data-testid={presetsSlotId}>presets</span>,
}}
</DateRangePicker>
</div>
);
},
});

const trigger = wrapper.find('.t-input');
await trigger.trigger('mousedown');
await trigger.trigger('mouseup');
await trigger.trigger('click');
await nextTick();
await new Promise((resolve) => setTimeout(resolve, 0));

const presetsEl = wrapper.find(`[data-testid="${presetsSlotId}"]`);
expect(presetsEl.exists()).toBe(true);
expect(presetsEl.text()).toBe('presets');
});

it("DatePicker: :defaultTime[string] & :valueType['time-stamp'] without enableTimePicker", async () => {
// 测试 DatePicker 当 valueType 为 time-stamp 且提供 defaultTime 但不启用 enableTimePicker 时
const defaultTime = '10:30:45';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<template>
<t-space direction="vertical">
<t-date-picker v-model="value" :presets="presets1" @preset-click="handlePresetClick" />
<t-date-picker v-model="value">
<template #presets> <t-button @click="value = dayjs().toDate().toLocaleDateString()">今天</t-button> </template>
</t-date-picker>
<t-date-range-picker v-model="range1" :presets="presets" />
<t-date-range-picker v-model="range2" :presets="presets" enable-time-picker />
</t-space>
Expand All @@ -8,12 +12,24 @@
<script lang="ts" setup>
import dayjs from 'dayjs';
import { ref } from 'vue';
import { DateRangePickerProps } from 'tdesign-vue-next';
import { DateRangePickerProps, DatePickerProps } from 'tdesign-vue-next';

const presets1 = ref<DatePickerProps['presets']>({
今天: dayjs().toDate(),
});
const presets = ref<DateRangePickerProps['presets']>({
最近7天: [dayjs().subtract(6, 'day').toDate(), dayjs().toDate()],
最近3天: [dayjs().subtract(2, 'day').toDate(), dayjs().toDate()],
今天: [dayjs().toDate(), dayjs().toDate()],
});

const value = ref('2022-01-01');
const range1 = ref(['2022-01-01', '2022-08-08']);
const range2 = ref(['2022-01-01 11:11:11', '2022-08-08 12:12:12']);

type PresetClickContext = Parameters<NonNullable<DatePickerProps['onPresetClick']>>[0];

const handlePresetClick = (context: PresetClickContext) => {
console.log(context);
};
</script>
12 changes: 12 additions & 0 deletions packages/components/date-picker/_example/date-presets-alt.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<template>
<t-space direction="vertical">
<t-date-picker v-model="value" :presets="presets1" @preset-click="handlePresetClick" />
<t-date-picker v-model="value">
<template #presets> <t-button @click="value = dayjs().toDate().toLocaleDateString()">今天</t-button> </template>
</t-date-picker>
<t-date-range-picker v-model="range1" :presets="presets" />
<t-date-range-picker v-model="range2" :presets="presets" enable-time-picker />
</t-space>
Expand All @@ -9,12 +13,20 @@
import { ref } from 'vue';
import dayjs from 'dayjs';

const presets1 = ref({
今天: dayjs().toDate(),
});
const presets = ref({
最近7天: [dayjs().subtract(6, 'day').toDate(), dayjs().toDate()],
最近3天: [dayjs().subtract(2, 'day').toDate(), dayjs().toDate()],
今天: [dayjs().toDate(), dayjs().toDate()],
});

const value = ref('2022-01-01');
const range1 = ref(['2022-01-01', '2022-08-08']);
const range2 = ref(['2022-01-01 11:11:11', '2022-08-08 12:12:12']);

const handlePresetClick = (context) => {
console.log(context);
};
</script>
40 changes: 22 additions & 18 deletions packages/components/date-picker/components/base/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import TButton from '../../../button';
import type { TdDatePickerProps } from '../../type';

export default defineComponent({
name: 'TDatePickerTable',
name: 'TDatePickerFooter',
props: {
enableTimePicker: Boolean,
presetsPlacement: String,
Expand All @@ -25,7 +25,6 @@ export default defineComponent({
const footerClass = computed(() => [COMPONENT_NAME.value, `${COMPONENT_NAME.value}--${props.presetsPlacement}`]);

const renderPresets = () => {
if (!props.presets) return null;
if (isPlainObject(props.presets))
return Object.keys(props.presets).map((key: string) => (
<TButton
Expand All @@ -39,22 +38,27 @@ export default defineComponent({
{key}
</TButton>
));
return renderTNodeJSX('presets');
const presetsNode = renderTNodeJSX('presets');
return presetsNode ?? null;
};
return () => {
const presetsContent = renderPresets();
const hasPresetsContent = Array.isArray(presetsContent) ? presetsContent.length > 0 : !!presetsContent;
return (
<div class={footerClass.value}>
{hasPresetsContent ? <div class={presetsClass.value}>{presetsContent}</div> : null}
{props.enableTimePicker && props.needConfirm && (
<TButton
disabled={!props.selectedValue}
size="small"
theme="primary"
onClick={(e: MouseEvent) => props.onConfirmClick?.({ e })}
>
{t(globalConfig.value.confirm)}
</TButton>
)}
</div>
);
};
return () => (
<div class={footerClass.value}>
{<div class={presetsClass.value}>{renderPresets()}</div>}
{props.enableTimePicker && props.needConfirm && (
<TButton
disabled={!props.selectedValue}
size="small"
theme="primary"
onClick={(e: MouseEvent) => props.onConfirmClick?.({ e })}
>
{t(globalConfig.value.confirm)}
</TButton>
)}
</div>
);
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ export default defineComponent({
onConfirmClick: Function,
selectedValue: [String, Number, Array, Date] as PropType<TdDatePickerProps['value']>,
},
setup(props) {
// 默认为 true
const showPanelFooter = computed(() => (props.enableTimePicker && props.needConfirm) || props.presets);
setup(props, { slots }) {
// 有 presets 对象、presets 插槽、或需要确认按钮时显示 footer
const showPanelFooter = computed(
() => (props.enableTimePicker && props.needConfirm) || props.presets || !!slots.presets,
);

return () =>
showPanelFooter.value ? (
Expand All @@ -28,6 +30,7 @@ export default defineComponent({
presetsPlacement={props.presetsPlacement}
selectedValue={props.selectedValue}
needConfirm={props.needConfirm}
v-slots={{ presets: slots.presets }}
/>
) : null;
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export default defineComponent({
onTimePickerChange: Function,
needConfirm: Boolean,
},
setup(props) {
setup(props, { slots }) {
const COMPONENT_NAME = usePrefixClass('date-range-picker__panel');
const { globalConfig } = useConfig('datePicker');

Expand Down Expand Up @@ -207,6 +207,7 @@ export default defineComponent({
onConfirmClick={props.onConfirmClick}
presetsPlacement={props.presetsPlacement}
needConfirm={props.needConfirm}
v-slots={{ presets: slots.presets }}
/>
) : null}
<div class={`${COMPONENT_NAME.value}-content-wrapper`}>
Expand Down Expand Up @@ -258,6 +259,7 @@ export default defineComponent({
onConfirmClick={props.onConfirmClick}
presetsPlacement={props.presetsPlacement}
needConfirm={props.needConfirm}
v-slots={{ presets: slots.presets }}
/>
) : null}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default defineComponent({
onTimePickerChange: Function,
disableTime: Function,
},
setup(props) {
setup(props, { slots }) {
const COMPONENT_NAME = usePrefixClass('date-picker__panel');
const { globalConfig } = useConfig('datePicker');

Expand Down Expand Up @@ -129,9 +129,13 @@ export default defineComponent({
]}
onClick={(e) => props.onPanelClick?.({ e })}
>
{['top', 'left'].includes(props.presetsPlacement) ? <TExtraContent {...extraProps.value} /> : null}
{['top', 'left'].includes(props.presetsPlacement) ? (
<TExtraContent {...extraProps.value} v-slots={slots.presets ? { presets: slots.presets } : {}} />
) : null}
<TPanelContent {...panelContentProps.value} />
{['bottom', 'right'].includes(props.presetsPlacement) ? <TExtraContent {...extraProps.value} /> : null}
{['bottom', 'right'].includes(props.presetsPlacement) ? (
<TExtraContent {...extraProps.value} v-slots={slots.presets ? { presets: slots.presets } : {}} />
) : null}
</div>
);
},
Expand Down
6 changes: 6 additions & 0 deletions packages/tdesign-vue-next/.changelog/pr-6490.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
pr_number: 6490
contributor: ZTH520
---

- fix(DatePicker): 修复 `presets` 插槽用法无效,`onPresetClick`事件未正确触发的问题 @ZTH520 ([#6490](https://github.com/Tencent/tdesign-vue-next/pull/6490))
Loading