Skip to content

Commit 007b190

Browse files
committed
Enhance DatePicker behavior for date range selection and refocus logic
- Updated the DatePicker component to improve handling of date selection completion when using the selectsRange prop. - Added logic to determine if the input should refocus based on the selection of start and end dates. - Introduced new tests to verify refocus behavior for various date range scenarios, ensuring correct functionality when selecting dates in a range. This update addresses cases in which date selection happens through keyboard, which will avoid unnecessary refocus to the date input when the date is partialy selected. Closes #5784
1 parent 7c6c3d5 commit 007b190

File tree

2 files changed

+188
-15
lines changed

2 files changed

+188
-15
lines changed

src/index.tsx

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,17 @@ export class DatePicker extends Component<DatePickerProps, DatePickerState> {
716716
monthSelectedIn?: number,
717717
) => {
718718
if (this.props.readOnly) return;
719-
if (this.props.shouldCloseOnSelect && !this.props.showTimeSelect) {
719+
720+
const { selectsRange, startDate, endDate, swapRange } = this.props;
721+
const isDateSelectionComplete =
722+
!selectsRange ||
723+
(startDate && !endDate && (swapRange || !isDateBefore(date, startDate)));
724+
725+
if (
726+
this.props.shouldCloseOnSelect &&
727+
!this.props.showTimeSelect &&
728+
isDateSelectionComplete
729+
) {
720730
// Preventing onFocus event to fix issue
721731
// https://github.com/Hacker0x01/react-datepicker/issues/628
722732
this.sendFocusBackToInput();
@@ -730,20 +740,8 @@ export class DatePicker extends Component<DatePickerProps, DatePickerState> {
730740
}
731741
if (!this.props.shouldCloseOnSelect || this.props.showTimeSelect) {
732742
this.setPreSelection(date);
733-
} else if (!this.props.inline) {
734-
if (!this.props.selectsRange) {
735-
this.setOpen(false);
736-
}
737-
738-
const { startDate, endDate } = this.props;
739-
740-
if (
741-
startDate &&
742-
!endDate &&
743-
(this.props.swapRange || !isDateBefore(date, startDate))
744-
) {
745-
this.setOpen(false);
746-
}
743+
} else if (isDateSelectionComplete) {
744+
this.setOpen(false);
747745
}
748746
};
749747

src/test/datepicker_test.test.tsx

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4642,4 +4642,179 @@ describe("DatePicker", () => {
46424642
);
46434643
});
46444644
});
4645+
4646+
describe("Refocus Input", () => {
4647+
it("should refocus the date input when a date is selected", async () => {
4648+
const selectedDate = newDate("2025-11-01");
4649+
const onChangeSpy = jest.fn();
4650+
const { container } = render(
4651+
<DatePicker
4652+
selected={selectedDate}
4653+
dateFormat="yyyy-MM-dd"
4654+
onChange={onChangeSpy}
4655+
/>,
4656+
);
4657+
4658+
const input = safeQuerySelector<HTMLInputElement>(container, "input");
4659+
fireEvent.focus(input);
4660+
4661+
expect(container.querySelector(".react-datepicker")).toBeTruthy();
4662+
4663+
const newSelectedDateEl = safeQuerySelector(
4664+
container,
4665+
".react-datepicker__day--002",
4666+
);
4667+
fireEvent.click(newSelectedDateEl);
4668+
4669+
await waitFor(() => {
4670+
expect(document.activeElement).not.toBe(newSelectedDateEl);
4671+
expect(document.activeElement).toBe(input);
4672+
});
4673+
});
4674+
4675+
describe("Date Range", () => {
4676+
it("should not refocus the input when the endDate is not selected in the Date Range", async () => {
4677+
const selectedDate = newDate("2025-11-01");
4678+
let startDate, endDate;
4679+
const onChangeSpy = jest.fn((dates) => {
4680+
[startDate, endDate] = dates;
4681+
});
4682+
4683+
const { container } = render(
4684+
<DatePicker
4685+
selectsRange
4686+
selected={selectedDate}
4687+
startDate={startDate}
4688+
endDate={endDate}
4689+
dateFormat="yyyy-MM-dd"
4690+
onChange={onChangeSpy}
4691+
/>,
4692+
);
4693+
const input = safeQuerySelector<HTMLInputElement>(container, "input");
4694+
fireEvent.focus(input);
4695+
4696+
expect(container.querySelector(".react-datepicker")).toBeTruthy();
4697+
const newStartDateEl = safeQuerySelector(
4698+
container,
4699+
".react-datepicker__day--002",
4700+
);
4701+
fireEvent.click(newStartDateEl);
4702+
4703+
expect(onChangeSpy).toHaveBeenCalledTimes(1);
4704+
4705+
await waitFor(() => {
4706+
expect(document.activeElement).not.toBe(input);
4707+
expect(document.activeElement).toBe(newStartDateEl);
4708+
});
4709+
});
4710+
4711+
it("should refocus the input when the endDate is selected in the Date Range (if the end date is after the start date)", async () => {
4712+
const selectedDate = newDate("2025-11-01");
4713+
let startDate = selectedDate,
4714+
endDate;
4715+
const onChangeSpy = jest.fn((dates) => {
4716+
[startDate, endDate] = dates;
4717+
});
4718+
4719+
const { container } = render(
4720+
<DatePicker
4721+
selectsRange
4722+
selected={selectedDate}
4723+
startDate={startDate}
4724+
endDate={endDate}
4725+
dateFormat="yyyy-MM-dd"
4726+
onChange={onChangeSpy}
4727+
/>,
4728+
);
4729+
const input = safeQuerySelector<HTMLInputElement>(container, "input");
4730+
fireEvent.focus(input);
4731+
4732+
expect(container.querySelector(".react-datepicker")).toBeTruthy();
4733+
const endDateEl = safeQuerySelector(
4734+
container,
4735+
".react-datepicker__day--005",
4736+
);
4737+
fireEvent.click(endDateEl);
4738+
4739+
expect(onChangeSpy).toHaveBeenCalledTimes(1);
4740+
4741+
await waitFor(() => {
4742+
expect(document.activeElement).toBe(input);
4743+
});
4744+
});
4745+
4746+
it("should not refocus the input when the selected endDate is before the startDate", async () => {
4747+
const selectedDate = newDate("2025-11-05");
4748+
let startDate = selectedDate,
4749+
endDate;
4750+
const onChangeSpy = jest.fn((dates) => {
4751+
[startDate, endDate] = dates;
4752+
});
4753+
4754+
const { container } = render(
4755+
<DatePicker
4756+
selectsRange
4757+
selected={selectedDate}
4758+
startDate={startDate}
4759+
endDate={endDate}
4760+
dateFormat="yyyy-MM-dd"
4761+
onChange={onChangeSpy}
4762+
/>,
4763+
);
4764+
const input = safeQuerySelector<HTMLInputElement>(container, "input");
4765+
fireEvent.focus(input);
4766+
4767+
expect(container.querySelector(".react-datepicker")).toBeTruthy();
4768+
const endDateEl = safeQuerySelector(
4769+
container,
4770+
".react-datepicker__day--002",
4771+
);
4772+
fireEvent.click(endDateEl);
4773+
4774+
expect(onChangeSpy).toHaveBeenCalledTimes(1);
4775+
4776+
await waitFor(() => {
4777+
expect(document.activeElement).not.toBe(input);
4778+
expect(document.activeElement).toBe(endDateEl);
4779+
});
4780+
});
4781+
4782+
it('should refocus the input when the selected endDate is before the startDate when the "swapRange" prop is set', async () => {
4783+
const selectedDate = newDate("2025-11-05");
4784+
let startDate = selectedDate,
4785+
endDate;
4786+
const onChangeSpy = jest.fn((dates) => {
4787+
[startDate, endDate] = dates;
4788+
});
4789+
4790+
const { container } = render(
4791+
<DatePicker
4792+
selectsRange
4793+
swapRange
4794+
selected={selectedDate}
4795+
startDate={startDate}
4796+
endDate={endDate}
4797+
dateFormat="yyyy-MM-dd"
4798+
onChange={onChangeSpy}
4799+
/>,
4800+
);
4801+
const input = safeQuerySelector<HTMLInputElement>(container, "input");
4802+
fireEvent.focus(input);
4803+
4804+
expect(container.querySelector(".react-datepicker")).toBeTruthy();
4805+
const endDateEl = safeQuerySelector(
4806+
container,
4807+
".react-datepicker__day--002",
4808+
);
4809+
fireEvent.click(endDateEl);
4810+
4811+
expect(onChangeSpy).toHaveBeenCalledTimes(1);
4812+
4813+
await waitFor(() => {
4814+
expect(document.activeElement).not.toBe(endDateEl);
4815+
expect(document.activeElement).toBe(input);
4816+
});
4817+
});
4818+
});
4819+
});
46454820
});

0 commit comments

Comments
 (0)