Skip to content

Commit

Permalink
Merge pull request #4394 from qburst/issue-4076/fix/emit-onCalendarCl…
Browse files Browse the repository at this point in the history
…ose-onsame-range-select

Fix #4076: Trigger onCalendarClose event and onChange even when the same date is selected as the start and the end date in a date range
  • Loading branch information
martijnrusschen authored Jan 4, 2024
2 parents 0c93dc6 + 0188371 commit 41713cf
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 3 deletions.
39 changes: 39 additions & 0 deletions src/date_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -862,3 +862,42 @@ export function startOfMinute(d) {
export function isSameMinute(d1, d2) {
return startOfMinute(d1).getTime() === startOfMinute(d2).getTime();
}

/**
* Returns a cloned date with midnight time (00:00:00)
*
* @param {Date} date The date for which midnight time is required
* @param {Date} dateToCompare the date to compare with
* @returns {Date} A new datetime object representing the input date with midnight time
*/
export function getMidnightDate(date) {
if (!isDate(date)) {
throw new Error("Invalid date");
}

const dateWithoutTime = new Date(date);
dateWithoutTime.setHours(0, 0, 0, 0);
return dateWithoutTime;
}

/**
* Is the first date before the second one?
*
* @param {Date} date The date that should be before the other one to return true
* @param {Date} dateToCompare The date to compare with
* @returns {boolean} The first date is before the second date
*
* Note:
* This function considers the mid-night of the given dates for comparison.
* It evaluates whether date is before dateToCompare based on their mid-night timestamps.
*/
export function isDateBefore(date, dateToCompare) {
if (!isDate(date) || !isDate(dateToCompare)) {
throw new Error("Invalid date received");
}

const midnightDate = getMidnightDate(date);
const midnightDateToCompare = getMidnightDate(dateToCompare);

return isBefore(midnightDate, midnightDateToCompare);
}
7 changes: 5 additions & 2 deletions src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
isMonthDisabled,
isYearDisabled,
getHolidaysMap,
isDateBefore,
} from "./date_utils";
import TabLoop from "./tab_loop";
import onClickOutside from "react-onclickoutside";
Expand Down Expand Up @@ -591,8 +592,10 @@ export default class DatePicker extends React.Component {
if (!this.props.selectsRange) {
this.setOpen(false);
}

const { startDate, endDate } = this.props;
if (startDate && !endDate && !isBefore(date, startDate)) {

if (startDate && !endDate && !isDateBefore(date, startDate)) {
this.setOpen(false);
}
}
Expand Down Expand Up @@ -655,7 +658,7 @@ export default class DatePicker extends React.Component {
if (noRanges) {
onChange([changedDate, null], event);
} else if (hasStartRange) {
if (isBefore(changedDate, startDate)) {
if (isDateBefore(changedDate, startDate)) {
onChange([changedDate, null], event);
} else {
onChange([startDate, changedDate], event);
Expand Down
49 changes: 49 additions & 0 deletions test/date_utils_test.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import {
getHolidaysMap,
arraysAreEqual,
startOfMinute,
isDateBefore,
getMidnightDate,
} from "../src/date_utils";
import setMinutes from "date-fns/setMinutes";
import setHours from "date-fns/setHours";
Expand Down Expand Up @@ -1248,4 +1250,51 @@ describe("date_utils", () => {
expect(startOfMinute(d)).toEqual(expected);
});
});

describe("getMidnightDate", () => {
it("should return a date with midnight time when a valid date is provided", () => {
const inputDate = new Date(2023, 0, 1, 12, 30, 45); // January 1, 2023, 12:30:45 PM

const result = getMidnightDate(inputDate);

expect(result).toEqual(new Date(2023, 0, 1, 0, 0, 0, 0)); // January 1, 2023, 00:00:00.000
});

it("should throw an error when an invalid date is provided", () => {
const invalidDate = "not a date";

expect(() => {
getMidnightDate(invalidDate);
}).toThrowError("Invalid date");
});
});

describe("isDateBefore", () => {
it("should return true when date is before dateToCompare", () => {
const date = new Date(2022, 11, 31); // December 31, 2022
const dateToCompare = new Date(2023, 0, 1); // January 1, 2023

const result = isDateBefore(date, dateToCompare);

expect(result).toBe(true);
});

it("should return false when date is not before dateToCompare", () => {
const date = new Date(2023, 0, 1); // January 1, 2023
const dateToCompare = new Date(2022, 11, 31); // December 31, 2022

const result = isDateBefore(date, dateToCompare);

expect(result).toBe(false);
});

it("should throw an error when either date or dateToCompare is not a valid date", () => {
expect(() => {
const invalidDate = "not a date";
const validDate = new Date(2023, 0, 1); // January 1, 2023

isDateBefore(invalidDate, validDate);
}).toThrowError();
});
});
});
102 changes: 101 additions & 1 deletion test/datepicker_test.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { findDOMNode } from "react-dom";
import TestUtils from "react-dom/test-utils";
import { enUS, enGB } from "date-fns/locale";
import { mount } from "enzyme";
import { render, fireEvent } from "@testing-library/react";
import { render, act, waitFor, fireEvent } from "@testing-library/react";
import defer from "lodash/defer";
import DatePicker, { registerLocale } from "../src/index.jsx";
import Day from "../src/day.jsx";
Expand Down Expand Up @@ -44,6 +44,18 @@ function goToLastMonth(datePicker) {
TestUtils.Simulate.click(findDOMNode(lastMonthButton));
}

function formatDayWithZeros(day) {
const dayString = day.toString();

if (dayString.length === 1) {
return `00${dayString}`;
}
if (dayString.length === 2) {
return `0${dayString}`;
}
return dayString;
}

describe("DatePicker", () => {
afterEach(() => {
jest.resetAllMocks();
Expand Down Expand Up @@ -2019,6 +2031,94 @@ describe("DatePicker", () => {
expect(onChangeSpy.mock.calls[0][0][0]).toBeNull();
expect(onChangeSpy.mock.calls[0][0][1]).toBeNull();
});

it("should call the onChange even when the startDate and the endDate is same in the range (case when we programmatically set the startDate, but set the same endDate through UI)", async () => {
let startDate = new Date();
let endDate = null;

const onChangeSpy = jest.fn();

const { container } = render(
<DatePicker
startDate={startDate}
endDate={endDate}
onChange={onChangeSpy}
shouldCloseOnSelect
selectsRange
/>,
);

const input = container.querySelector("input");
expect(input).toBeTruthy();
fireEvent.click(input);

let calendar = container.querySelector(".react-datepicker");
expect(calendar).toBeTruthy();

// Select the same date as the start date
const startDatePrefixedWithZeros = formatDayWithZeros(
startDate.getDate(),
);
const endDateElement = container.querySelector(
`.react-datepicker__day--${startDatePrefixedWithZeros}`,
);
fireEvent.click(endDateElement);

await act(async () => {
await waitFor(() => {
expect(onChangeSpy).toHaveBeenCalled();
});
});
});

it("should hide the calendar even when the startDate and the endDate is same in the range", async () => {
let startDate = new Date();
let endDate = null;

const onCalendarCloseSpy = jest.fn();

const onChange = (dates) => {
const [start, end] = dates;
startDate = start;
endDate = end;
};

const { container } = render(
<DatePicker
startDate={startDate}
endDate={endDate}
onChange={onChange}
onCalendarClose={onCalendarCloseSpy}
shouldCloseOnSelect
selectsRange
/>,
);

const input = container.querySelector("input");
expect(input).toBeTruthy();
fireEvent.click(input);

let calendar = container.querySelector(".react-datepicker");
expect(calendar).toBeTruthy();

// Select the same date as the start date
const startDatePrefixedWithZeros = formatDayWithZeros(
startDate.getDate(),
);
const endDateElement = container.querySelector(
`.react-datepicker__day--${startDatePrefixedWithZeros}`,
);
fireEvent.click(endDateElement);

await act(async () => {
await waitFor(() => {
calendar = container.querySelector(".react-datepicker");
expect(calendar).toBeFalsy();

expect(onCalendarCloseSpy).toHaveBeenCalled();
});
});
});
});

describe("duplicate dates when multiple months", () => {
Expand Down

0 comments on commit 41713cf

Please sign in to comment.