From aa0af9d67c63a24876a17d2df40a995d03b49ea0 Mon Sep 17 00:00:00 2001 From: kykim00 Date: Thu, 27 Nov 2025 11:24:58 +0900 Subject: [PATCH] feat: add formatMultipleDates prop to DatePicker Allow custom formatting of multiple selected dates in input field. The formatter receives dates array and a formatDate helper function. Fixes #6066 --- docs-site/src/components/Examples/config.tsx | 5 +++ .../src/examples/ts/selectsMultipleFormat.tsx | 27 ++++++++++++ src/index.tsx | 14 ++++++- src/test/min_time_test.test.tsx | 1 + src/test/multiple_selected_dates.test.tsx | 42 +++++++++++++++++++ 5 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 docs-site/src/examples/ts/selectsMultipleFormat.tsx diff --git a/docs-site/src/components/Examples/config.tsx b/docs-site/src/components/Examples/config.tsx index 634d0179e7..e593f4d9bf 100644 --- a/docs-site/src/components/Examples/config.tsx +++ b/docs-site/src/components/Examples/config.tsx @@ -96,6 +96,7 @@ import SpecificDateRange from "../../examples/ts/specificDateRange?raw"; import ExcludeTimePeriod from "../../examples/ts/excludeTimePeriod?raw"; import SelectsMultiple from "../../examples/ts/selectsMultiple?raw"; import SelectsMultipleMonths from "../../examples/ts/selectsMultipleMonths?raw"; +import SelectsMultipleFormat from "../../examples/ts/selectsMultipleFormat?raw"; import StrictParsing from "../../examples/ts/strictParsing?raw"; import TabIndex from "../../examples/ts/tabIndex?raw"; import Today from "../../examples/ts/today?raw"; @@ -501,6 +502,10 @@ export const EXAMPLE_CONFIG: IExampleConfig[] = [ title: "Select multiple dates", component: SelectsMultiple, }, + { + title: "Select multiple dates with custom format", + component: SelectsMultipleFormat, + }, { title: "Select multiple months", component: SelectsMultipleMonths, diff --git a/docs-site/src/examples/ts/selectsMultipleFormat.tsx b/docs-site/src/examples/ts/selectsMultipleFormat.tsx new file mode 100644 index 0000000000..9ecd6f3d63 --- /dev/null +++ b/docs-site/src/examples/ts/selectsMultipleFormat.tsx @@ -0,0 +1,27 @@ +const SelectsMultipleFormat = () => { + const [selectedDates, setSelectedDates] = useState([]); + + const onChange = (dates: Date[] | null) => { + setSelectedDates(dates ?? []); + }; + + const formatMultipleDates = ( + dates: Date[], + formatDate: (date: Date) => string, + ) => { + return dates.map(formatDate).join(" | "); + }; + + return ( + + ); +}; + +render(SelectsMultipleFormat); diff --git a/src/index.tsx b/src/index.tsx index 84b72a3b32..a15ed05ef3 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -218,6 +218,7 @@ export type DatePickerProps = OmitUnion< | { selectsRange?: never; selectsMultiple?: never; + formatMultipleDates?: never; onChange?: ( date: Date | null, event?: @@ -228,6 +229,7 @@ export type DatePickerProps = OmitUnion< | { selectsRange: true; selectsMultiple?: never; + formatMultipleDates?: never; onChange?: ( date: [Date | null, Date | null], event?: @@ -238,8 +240,12 @@ export type DatePickerProps = OmitUnion< | { selectsRange?: never; selectsMultiple: true; + formatMultipleDates?: ( + dates: Date[], + formatDate: (date: Date) => string, + ) => string; onChange?: ( - date: Date[] | null, + dates: Date[] | null, event?: | React.MouseEvent | React.KeyboardEvent, @@ -448,6 +454,7 @@ export class DatePicker extends Component { selectedDates, selectsMultiple, selectsRange, + formatMultipleDates, value, } = this.props; const dateFormat = @@ -466,6 +473,11 @@ export class DatePicker extends Component { rangeSeparator, }); } else if (selectsMultiple) { + if (formatMultipleDates) { + const formatDateFn = (date: Date) => + safeDateFormat(date, { dateFormat, locale }); + return formatMultipleDates(selectedDates ?? [], formatDateFn); + } return safeMultipleDatesFormat(selectedDates ?? [], { dateFormat, locale, diff --git a/src/test/min_time_test.test.tsx b/src/test/min_time_test.test.tsx index d705695294..ae53598403 100644 --- a/src/test/min_time_test.test.tsx +++ b/src/test/min_time_test.test.tsx @@ -24,6 +24,7 @@ const DatePickerWithState = ( | "dateFormat" | "selectsRange" | "selectsMultiple" + | "formatMultipleDates" | "onSelect" >, ) => { diff --git a/src/test/multiple_selected_dates.test.tsx b/src/test/multiple_selected_dates.test.tsx index 63f430d79d..106d20a565 100644 --- a/src/test/multiple_selected_dates.test.tsx +++ b/src/test/multiple_selected_dates.test.tsx @@ -93,4 +93,46 @@ describe("Multiple Dates Selected", function () { expect(input).not.toBeNull(); expect(input?.value).toBe("01/01/2024 (+2)"); }); + + it("should override default format when formatMultipleDates is provided", () => { + const { container: datePicker } = getDatePicker({ + selectsMultiple: true, + selectedDates: [ + new Date("2024/01/01"), + new Date("2024/01/15"), + new Date("2024/03/15"), + ], + formatMultipleDates: (dates, formatDate) => + dates.map(formatDate).join(" | "), + }); + + const input = datePicker.querySelector("input"); + + expect(input).not.toBeNull(); + expect(input?.value).toBe("01/01/2024 | 01/15/2024 | 03/15/2024"); + }); + + it("should pass correct arguments to formatMultipleDates", () => { + const selectedDates = [new Date("2024/01/01"), new Date("2024/01/15")]; + const mockFormatter = jest.fn( + (dates: Date[], formatDate: (d: Date) => string) => + dates.map(formatDate).join(", "), + ); + + getDatePicker({ + selectsMultiple: true, + selectedDates, + formatMultipleDates: mockFormatter, + }); + + expect(mockFormatter).toHaveBeenCalledTimes(1); + + const [receivedDates, receivedFormatDate] = mockFormatter.mock.calls[0]!; + expect(receivedDates).toHaveLength(2); + expect(receivedDates[0]?.getTime()).toBe(selectedDates[0]?.getTime()); + expect(receivedDates[1]?.getTime()).toBe(selectedDates[1]?.getTime()); + + expect(typeof receivedFormatDate).toBe("function"); + expect(receivedFormatDate(new Date("2024/01/01"))).toBe("01/01/2024"); + }); });