diff --git a/src/date_utils.ts b/src/date_utils.ts index 5f5e92f0b..12600b89b 100644 --- a/src/date_utils.ts +++ b/src/date_utils.ts @@ -245,11 +245,11 @@ export function safeDateRangeFormat( rangeSeparator?: string; }, ): string { - if (!startDate) { + if (!startDate && !endDate) { return ""; } - const formattedStartDate = safeDateFormat(startDate, props); + const formattedStartDate = startDate ? safeDateFormat(startDate, props) : ""; const formattedEndDate = endDate ? safeDateFormat(endDate, props) : ""; const dateRangeSeparator = props.rangeSeparator || DATE_RANGE_SEPARATOR; diff --git a/src/index.tsx b/src/index.tsx index f49abf316..0e845da7b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -670,12 +670,14 @@ export class DatePicker extends Component { this.props.locale, strictParsing, ); - const endDateNew = parseDate( - valueEnd ?? "", - dateFormat, - this.props.locale, - strictParsing, - ); + const endDateNew = startDateNew + ? parseDate( + valueEnd ?? "", + dateFormat, + this.props.locale, + strictParsing, + ) + : null; const startChanged = startDate?.getTime() !== startDateNew?.getTime(); const endChanged = endDate?.getTime() !== endDateNew?.getTime(); @@ -832,6 +834,7 @@ export class DatePicker extends Component { if (selectsRange) { const noRanges = !startDate && !endDate; const hasStartRange = startDate && !endDate; + const hasOnlyEndRange = !startDate && !!endDate; const isRangeFilled = startDate && endDate; if (noRanges) { onChange?.([changedDate, null], event); @@ -847,6 +850,12 @@ export class DatePicker extends Component { } else { onChange?.([startDate, changedDate], event); } + } else if (hasOnlyEndRange) { + if (changedDate && isDateBefore(changedDate, endDate)) { + onChange?.([changedDate, endDate], event); + } else { + onChange?.([changedDate, null], event); + } } if (isRangeFilled) { onChange?.([changedDate, null], event); diff --git a/src/test/date_utils_test.test.ts b/src/test/date_utils_test.test.ts index 0d53ad9c8..bb211f2f8 100644 --- a/src/test/date_utils_test.test.ts +++ b/src/test/date_utils_test.test.ts @@ -1264,6 +1264,14 @@ describe("date_utils", () => { ); }); + it("should return a formatted endDate prefixed by a dash when startDate is null", () => { + const startDate = null; + const endDate = new Date("2021-04-20 00:00:00"); + expect(safeDateRangeFormat(startDate, endDate, props)).toBe( + " - 04/20/2021", + ); + }); + it("should return a formatted startDate followed by a dash followed by a formatted endDate when startDate and endDate both have values", () => { const startDate = new Date("2021-04-20 00:00:00"); const endDate = new Date("2021-04-28 00:00:00"); diff --git a/src/test/datepicker_test.test.tsx b/src/test/datepicker_test.test.tsx index 859bfa8b2..790b19c95 100644 --- a/src/test/datepicker_test.test.tsx +++ b/src/test/datepicker_test.test.tsx @@ -4531,4 +4531,115 @@ describe("DatePicker", () => { expect(input?.value).toBe("07/17/2025"); }); }); + + describe("Date Range - Handle null start date", () => { + it("should display the endDate when the startDate is not available", () => { + const endDateLabel = "2025-11-22"; + const { container } = render( + {}} + isClearable + />, + ); + const input = safeQuerySelector(container, "input"); + expect(input.value).toBe(` - ${endDateLabel}`); + }); + + it("should clear the input when the startDate alone is cleared while the endDate is still available", () => { + const startDateLabel = "2025-11-17"; + const endDateLabel = "2025-11-22"; + const onChangeSpy = jest.fn(); + + const { container } = render( + , + ); + const input = safeQuerySelector(container, "input"); + expect(input.value).toBe(`${startDateLabel} - ${endDateLabel}`); + + fireEvent.change(input, { target: { value: ` - ${endDateLabel}` } }); + expect(onChangeSpy).toHaveBeenCalledTimes(1); + expect(onChangeSpy).toHaveBeenCalledWith([null, null], expect.anything()); + }); + + it("should clear the endDate and set the startDate when the endDate is alone available and the newly selected startDate is greater than the endDate", () => { + const endDateLabel = "2025-11-20"; + const onChangeSpy = jest.fn(); + const { container } = render( + , + ); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); + + expect(container.querySelector(".react-datepicker")).toBeTruthy(); + const newStartDateEl = safeQuerySelector( + container, + ".react-datepicker__day--021", + ); + fireEvent.click(newStartDateEl); + + expect(onChangeSpy).toHaveBeenCalledTimes(1); + const changedDateRange = onChangeSpy.mock.calls[0][0]; + const [changedStartDate, changedEndDate] = changedDateRange; + + expect(changedEndDate).toBe(null); + expect(changedStartDate.toISOString()).toBe( + newDate("2025-11-21").toISOString(), + ); + }); + + it("should set the startDate when the endDate is alone available and the newly selected startDate is less than the endDate", () => { + const endDateLabel = "2025-11-20"; + const onChangeSpy = jest.fn(); + const { container } = render( + , + ); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); + + expect(container.querySelector(".react-datepicker")).toBeTruthy(); + const newStartDateEl = safeQuerySelector( + container, + ".react-datepicker__day--019", + ); + fireEvent.click(newStartDateEl); + + expect(onChangeSpy).toHaveBeenCalledTimes(1); + const changedDateRange = onChangeSpy.mock.calls[0][0]; + const [changedStartDate, changedEndDate] = changedDateRange; + + expect(changedEndDate.toISOString()).toBe( + newDate(endDateLabel).toISOString(), + ); + expect(changedStartDate.toISOString()).toBe( + newDate("2025-11-19").toISOString(), + ); + }); + }); });