Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/date_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
21 changes: 15 additions & 6 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -670,12 +670,14 @@ export class DatePicker extends Component<DatePickerProps, DatePickerState> {
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();

Expand Down Expand Up @@ -832,6 +834,7 @@ export class DatePicker extends Component<DatePickerProps, DatePickerState> {
if (selectsRange) {
const noRanges = !startDate && !endDate;
const hasStartRange = startDate && !endDate;
const hasOnlyEndRange = !startDate && !!endDate;
const isRangeFilled = startDate && endDate;
if (noRanges) {
onChange?.([changedDate, null], event);
Expand All @@ -847,6 +850,12 @@ export class DatePicker extends Component<DatePickerProps, DatePickerState> {
} 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);
Expand Down
8 changes: 8 additions & 0 deletions src/test/date_utils_test.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
111 changes: 111 additions & 0 deletions src/test/datepicker_test.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<DatePicker
selectsRange
startDate={null}
endDate={newDate(endDateLabel)}
dateFormat="yyyy-MM-dd"
onChange={() => {}}
isClearable
/>,
);
const input = safeQuerySelector<HTMLInputElement>(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(
<DatePicker
selectsRange
startDate={newDate(startDateLabel)}
endDate={newDate(endDateLabel)}
dateFormat="yyyy-MM-dd"
onChange={onChangeSpy}
isClearable
/>,
);
const input = safeQuerySelector<HTMLInputElement>(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(
<DatePicker
selectsRange
startDate={null}
selected={newDate(endDateLabel)}
endDate={newDate(endDateLabel)}
dateFormat="yyyy-MM-dd"
onChange={onChangeSpy}
isClearable
/>,
);
const input = safeQuerySelector<HTMLInputElement>(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(
<DatePicker
selectsRange
startDate={null}
selected={newDate(endDateLabel)}
endDate={newDate(endDateLabel)}
dateFormat="yyyy-MM-dd"
onChange={onChangeSpy}
isClearable
/>,
);
const input = safeQuerySelector<HTMLInputElement>(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(),
);
});
});
});
Loading