(react) restrict inputs formats in date picker components

Enforces a standardized approach for end consumers using the
component's API. Consumers are now required to provide a date as an
ISO string in either UTC or with a UTC offset. The support for native
Date objects has been removed to ensure consistent and reliable
behavior across all implementations. By adopting this uniform input
format, we can simplify usage and avoid potential inconsistencies in
date handling.
This commit is contained in:
Lebaud Antoine
2023-07-12 22:51:32 +02:00
committed by aleb_the_flash
parent 8cf8e1eba2
commit 0dc46d1144
11 changed files with 403 additions and 324 deletions

View File

@@ -8,7 +8,7 @@ import { Button } from ":/components/Button";
vi.mock("@internationalized/date", async () => {
const mod = await vi.importActual<typeof import("@internationalized/date")>(
"@internationalized/date"
"@internationalized/date",
);
return {
...mod,
@@ -151,9 +151,8 @@ describe("<DateRangePicker/>", () => {
});
it.each([
["2022-05-25", "2022-05-26"],
["25 Mar 2025", "2029-05-26"],
["2023-06-01T13:50", "2024-05-26"],
["2022-05-25T00:00:00.000Z", "2022-05-26T00:00:00.000Z"],
["2023-06-01T00:00:00.000Z", "2024-05-26T00:00:00.000Z"],
])("has a default value", async (start: string, end: string) => {
render(
<CunninghamProvider>
@@ -180,7 +179,10 @@ describe("<DateRangePicker/>", () => {
startLabel="Start date"
endLabel="End date"
name="datepicker"
defaultValue={["2023-04-25", "2023-05-25"]}
defaultValue={[
"2023-04-25T00:00:00.000Z",
"2023-05-25T00:00:00.000Z",
]}
/>
</CunninghamProvider>,
);
@@ -375,7 +377,10 @@ describe("<DateRangePicker/>", () => {
<DateRangePicker
startLabel="Start date"
endLabel="End date"
defaultValue={["2023-01-01", "2023-01-01"]}
defaultValue={[
"2023-01-01T00:00:00.000Z",
"2023-01-01T00:00:00.000Z",
]}
name="datepicker"
/>
</CunninghamProvider>,
@@ -437,7 +442,10 @@ describe("<DateRangePicker/>", () => {
startLabel="Start date"
endLabel="End date"
name="datepicker"
defaultValue={["2023-01-01", "2023-01-01"]}
defaultValue={[
"2023-01-01T00:00:00.000Z",
"2023-01-01T00:00:00.000Z",
]}
/>
</CunninghamProvider>,
);
@@ -486,7 +494,10 @@ describe("<DateRangePicker/>", () => {
startLabel="Start date"
endLabel="End date"
name="datepicker"
defaultValue={["2023-01-01", "2023-01-01"]}
defaultValue={[
"2023-01-01T00:00:00.000Z",
"2023-01-01T00:00:00.000Z",
]}
/>
</CunninghamProvider>,
);
@@ -657,8 +668,11 @@ describe("<DateRangePicker/>", () => {
startLabel="Start date"
endLabel="End date"
name="datepicker"
defaultValue={["2022-05-25", "2022-05-26"]}
value={["2022-05-25", "2022-05-26"]}
defaultValue={[
"2022-05-25T00:00:00.000Z",
"2022-05-26T00:00:00.000Z",
]}
value={["2022-05-25T00:00:00.000Z", "2022-05-26T00:00:00.000Z"]}
/>
</CunninghamProvider>,
),
@@ -668,9 +682,9 @@ describe("<DateRangePicker/>", () => {
});
it.each([
["this_not_a_valid_date", "2022-05-12"],
["2022-05-12", "2023-13-13"],
["2025-25-05", "2022-05-12"],
["this_not_a_valid_date", "2022-05-12T00:00:00.000Z"],
["2022-05-12T00:00:00.000Z", "2023-13-13"],
["2025-25-05T00:00:00.000Z", "2022-05-12T00:00:00.000Z"],
])("has not a valid range value", async (start: string, end: string) => {
vi.spyOn(console, "error").mockImplementation(() => undefined);
expect(() =>
@@ -697,76 +711,77 @@ describe("<DateRangePicker/>", () => {
startLabel="Start date"
endLabel="End date"
name="datepicker"
defaultValue={["2024-05-25", "2022-05-26"]}
defaultValue={[
"2024-05-25T00:00:00.000Z",
"2022-05-26T00:00:00.000Z",
]}
/>
</CunninghamProvider>,
);
await expectDateRangePickerStateToBe("invalid");
});
it.each([[new Date(2022, 5, 25), new Date(2022, 5, 27)]])(
"clears date",
async (start: Date, end: Date) => {
const user = userEvent.setup();
render(
<CunninghamProvider>
<DateRangePicker
startLabel="Start date"
endLabel="End date"
name="datepicker"
defaultValue={[start, end]}
/>
</CunninghamProvider>,
);
it("clears date", async () => {
const user = userEvent.setup();
render(
<CunninghamProvider>
<DateRangePicker
startLabel="Start date"
endLabel="End date"
name="datepicker"
defaultValue={[
"2023-05-25T00:00:00.000Z",
"2023-05-27T00:00:00.000Z",
]}
/>
</CunninghamProvider>,
);
const clearButton = screen.getByRole("button", {
name: "Clear date",
});
await user.click(clearButton);
expectCalendarToBeOpen();
const clearButton = screen.getByRole("button", {
name: "Clear date",
});
await user.click(clearButton);
expectCalendarToBeOpen();
// Date field's value should be set to a placeholder value.
const [startInput, endInput] = await screen.queryAllByRole(
"presentation",
);
expect(startInput.textContent).eq("mm/dd/yyyy");
expect(endInput.textContent).eq("mm/dd/yyyy");
// Date field's value should be set to a placeholder value.
const [startInput, endInput] = await screen.queryAllByRole("presentation");
expect(startInput.textContent).eq("mm/dd/yyyy");
expect(endInput.textContent).eq("mm/dd/yyyy");
const startGridCell = screen.getByRole("gridcell", {
name: `${start.getDate()}`,
})!;
const startGridCell = screen.getByRole("gridcell", {
name: "25",
})!;
// Make sure start grid-cell is not selected anymore.
expect(startGridCell.getAttribute("aria-selected")).toBeNull();
expect(
startGridCell.classList.contains(
"c__calendar__wrapper__grid__week-row__background--range--start",
),
).toBe(false);
// Make sure start grid-cell is not selected anymore.
expect(startGridCell.getAttribute("aria-selected")).toBeNull();
expect(
startGridCell.classList.contains(
"c__calendar__wrapper__grid__week-row__background--range--start",
),
).toBe(false);
// Make sure end grid-cell is not selected anymore.
const endGridCell = screen.getByRole("gridcell", {
name: `${end.getDate()}`,
})!;
expect(endGridCell.getAttribute("aria-selected")).toBeNull();
expect(
endGridCell.classList.contains(
"c__calendar__wrapper__grid__week-row__background--range--end",
),
).toBe(false);
// Make sure end grid-cell is not selected anymore.
const endGridCell = screen.getByRole("gridcell", {
name: "27",
})!;
expect(endGridCell.getAttribute("aria-selected")).toBeNull();
expect(
endGridCell.classList.contains(
"c__calendar__wrapper__grid__week-row__background--range--end",
),
).toBe(false);
// Close the calendar.
const toggleButton = (await screen.findAllByRole("button"))![1];
await user.click(toggleButton);
// Close the calendar.
const toggleButton = (await screen.findAllByRole("button"))![1];
await user.click(toggleButton);
// Make sure the empty date field is hidden when closing the calendar.
await expectDateFieldsToBeHidden();
},
);
// Make sure the empty date field is hidden when closing the calendar.
await expectDateFieldsToBeHidden();
});
it.each([
["2023-01-01", "2023-01-01"],
["2023-01-01", "2023-03-01"],
["2023-01-01T00:00:00.000Z", "2023-01-01T00:00:00.000Z"],
["2023-01-01T00:00:00.000Z", "2023-03-01T00:00:00.000Z"],
])(
"has a start or a end date inferior to minValue",
async (start: string, end: string) => {
@@ -777,7 +792,7 @@ describe("<DateRangePicker/>", () => {
endLabel="End date"
name="datepicker"
defaultValue={[start, end]}
minValue="2023-02-01"
minValue="2023-02-01T00:00:00.000Z"
/>
</CunninghamProvider>,
);
@@ -786,8 +801,8 @@ describe("<DateRangePicker/>", () => {
);
it.each([
["2023-01-01", "2023-03-01"],
["2023-03-01", "2023-03-01"],
["2023-01-01T00:00:00.000Z", "2023-03-01T00:00:00.000Z"],
["2023-03-01T00:00:00.000Z", "2023-03-01T00:00:00.000Z"],
])(
"has a start or a end date superior to maxValue",
async (start: string, end: string) => {
@@ -798,7 +813,7 @@ describe("<DateRangePicker/>", () => {
endLabel="End date"
name="datepicker"
defaultValue={[start, end]}
maxValue="2023-02-01"
maxValue="2023-02-01T00:00:00.000Z"
/>
</CunninghamProvider>,
);
@@ -813,7 +828,10 @@ describe("<DateRangePicker/>", () => {
startLabel="Start date"
endLabel="End date"
name="datepicker"
defaultValue={["2023-01-01", "2023-01-01"]}
defaultValue={[
"2023-01-01T00:00:00.000Z",
"2023-01-01T00:00:00.000Z",
]}
disabled={true}
/>
</CunninghamProvider>,
@@ -846,7 +864,10 @@ describe("<DateRangePicker/>", () => {
startLabel="Start date"
endLabel="End date"
name="datepicker"
defaultValue={["2023-01-01", "2023-01-01"]}
defaultValue={[
"2023-01-01T00:00:00.000Z",
"2023-01-01T00:00:00.000Z",
]}
/>
</CunninghamProvider>,
);
@@ -864,7 +885,10 @@ describe("<DateRangePicker/>", () => {
startLabel="Start date"
endLabel="End date"
name="datepicker"
defaultValue={["2023-01-01", "2023-01-10"]}
defaultValue={[
"2023-01-01T00:00:00.000Z",
"2023-01-10T00:00:00.000Z",
]}
/>
</CunninghamProvider>,
);
@@ -903,8 +927,8 @@ describe("<DateRangePicker/>", () => {
const user = userEvent.setup();
const Wrapper = () => {
const [value, setValue] = useState<[string, string] | null>([
"2023-04-25",
"2023-04-26",
"2023-04-25T00:00:00.000Z",
"2023-04-26T00:00:00.000Z",
]);
return (
<CunninghamProvider>
@@ -924,7 +948,9 @@ describe("<DateRangePicker/>", () => {
render(<Wrapper />);
// Make sure value is selected.
screen.getByText("Value = 2023-04-25 2023-04-26|");
screen.getByText(
"Value = 2023-04-25T00:00:00.000Z 2023-04-26T00:00:00.000Z|",
);
// Make sure value is initially render in the date field component.
const [startInput, endInput] = await screen.queryAllByRole("presentation");
@@ -951,7 +977,7 @@ describe("<DateRangePicker/>", () => {
// Make sure value is selected.
screen.getByText(
`Value = 2023-04-11T22:00:00.000Z 2023-04-13T22:00:00.000Z|`
`Value = 2023-04-12T00:00:00.000Z 2023-04-14T00:00:00.000Z|`,
);
// Clear value.