- Add variant prop to DatePicker and DateRangePicker - Add hideLabel prop for accessible hidden labels - Label(s) rendered outside wrapper in classic mode - DateRangePicker shows both labels above fields in classic mode - Compact height in classic mode - Add unit tests and Storybook stories Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1541 lines
46 KiB
TypeScript
1541 lines
46 KiB
TypeScript
import userEvent from "@testing-library/user-event";
|
|
import { render, screen, within } from "@testing-library/react";
|
|
import React, { FormEvent, useState } from "react";
|
|
import { expect, vi } from "vitest";
|
|
import { CunninghamProvider } from ":/components/Provider";
|
|
import { DatePicker } from ":/components/Forms/DatePicker/DatePicker";
|
|
import { Button } from ":/components/Button";
|
|
|
|
vi.mock("@internationalized/date", async () => {
|
|
const mod = await vi.importActual<typeof import("@internationalized/date")>(
|
|
"@internationalized/date",
|
|
);
|
|
return {
|
|
...mod,
|
|
// Note: Restoring mocks will cause the function to return 'undefined'.
|
|
// Consider providing a default implementation to be restored instead.
|
|
getLocalTimeZone: vi.fn().mockReturnValue("Europe/Paris"),
|
|
};
|
|
});
|
|
|
|
describe("<DatePicker/>", () => {
|
|
const expectCalendarToBeClosed = () => {
|
|
expect(screen.queryByRole("application")).toBeNull();
|
|
};
|
|
|
|
const expectCalendarToBeOpen = () => {
|
|
const calendar = screen.queryByRole("application");
|
|
expect(calendar).toBeDefined();
|
|
expect(calendar).not.toBeNull();
|
|
expect(Array.from(calendar!.classList)).contains(
|
|
"c__calendar__wrapper--opened",
|
|
);
|
|
};
|
|
|
|
const expectDateFieldToBeHidden = () => {
|
|
const dateField = screen.queryByRole("presentation");
|
|
expect(dateField).toBeTruthy();
|
|
expect(Array.from(dateField!.parentElement!.classList)).contains(
|
|
"c__date-picker__inner--collapsed",
|
|
);
|
|
};
|
|
|
|
const expectDateFieldToBeDisplayed = () => {
|
|
expect(screen.queryByRole("presentation")).toBeTruthy();
|
|
};
|
|
|
|
const expectDatesToBeEqual = (
|
|
firstDate: Date | string | undefined | null,
|
|
secondDate: Date | string | undefined | null,
|
|
) => {
|
|
expect(firstDate).toBeDefined();
|
|
expect(secondDate).toBeDefined();
|
|
expect(new Date(firstDate!).toLocaleDateString()).eq(
|
|
new Date(secondDate!).toLocaleDateString(),
|
|
);
|
|
};
|
|
|
|
const expectFocusedMonthToBeEqual = (expectedMonth: string) => {
|
|
const focusedMonth = screen
|
|
.getByRole("combobox", {
|
|
name: "Select a month",
|
|
})!
|
|
.textContent?.replace("arrow_drop_down", "");
|
|
expect(focusedMonth).eq(expectedMonth);
|
|
};
|
|
|
|
const expectFocusedYearToBeEqual = (expectedYear: string) => {
|
|
const focusedYear = screen
|
|
.getByRole("combobox", {
|
|
name: "Select a year",
|
|
})!
|
|
.textContent?.replace("arrow_drop_down", "");
|
|
|
|
expect(focusedYear).eq(expectedYear);
|
|
};
|
|
|
|
const expectMenuToBeOpen = (menu: HTMLElement) => {
|
|
expect(Array.from(menu.classList)).contains("c__calendar__menu--opened");
|
|
};
|
|
|
|
const expectMenuToBeClosed = (menu: HTMLElement) => {
|
|
expect(Array.from(menu.classList)).not.contains(
|
|
"c__calendar__menu--opened",
|
|
);
|
|
};
|
|
|
|
const expectDatePickerStateToBe = async (state: string) => {
|
|
const input = (await screen.findAllByRole("button"))![0];
|
|
const classNames =
|
|
input.parentElement && Array.from(input.parentElement.classList);
|
|
expect(classNames).contains(`c__date-picker--${state}`);
|
|
};
|
|
|
|
it("toggles calendar", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker label="Pick a date" name="datepicker" />
|
|
</CunninghamProvider>,
|
|
);
|
|
|
|
const [input, button] = await screen.findAllByRole("button");
|
|
|
|
// It returns the clickable div.
|
|
expect(input.tagName).toEqual("DIV");
|
|
|
|
// It returns the toggle button.
|
|
expect(button.tagName).toEqual("BUTTON");
|
|
|
|
// Calendar is initially closed.
|
|
expectCalendarToBeClosed();
|
|
expectDateFieldToBeHidden();
|
|
|
|
// Toggle button opens the calendar.
|
|
await user.click(button);
|
|
expectCalendarToBeOpen();
|
|
expectDateFieldToBeDisplayed();
|
|
|
|
// Clicking again closes the calendar.
|
|
await user.click(button);
|
|
expectCalendarToBeClosed();
|
|
expectDateFieldToBeHidden();
|
|
|
|
// Click on the input opens the calendar.
|
|
await user.click(input);
|
|
expectCalendarToBeOpen();
|
|
expectDateFieldToBeDisplayed();
|
|
|
|
// Clicking again on the input should not close the calendar.
|
|
// Click could happen while selecting the date field input.
|
|
await user.click(input);
|
|
expectCalendarToBeOpen();
|
|
expectDateFieldToBeDisplayed();
|
|
|
|
// While the calendar open, clicking on
|
|
// the toggle button closes the calendar.
|
|
await user.click(button);
|
|
expectCalendarToBeClosed();
|
|
});
|
|
|
|
it("toggles calendar with keyboard", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker label="Pick a date" name="datepicker" />
|
|
</CunninghamProvider>,
|
|
);
|
|
const [input, toggleButton] = await screen.findAllByRole("button")!;
|
|
|
|
await user.keyboard("{Tab}");
|
|
expect(input).toHaveFocus();
|
|
|
|
await user.keyboard("{Tab}");
|
|
expect(toggleButton).toHaveFocus();
|
|
|
|
await user.keyboard("{Enter}");
|
|
expectCalendarToBeOpen();
|
|
});
|
|
|
|
it("focuses in the right order with no picked date", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker label="Pick a date" name="datepicker" />
|
|
</CunninghamProvider>,
|
|
);
|
|
// Get elements that should receive focus when no date is picked.
|
|
const [input, toggleButton] = await screen.findAllByRole("button")!;
|
|
const [monthSegment, daySegment, yearSegment] =
|
|
await screen.findAllByRole("spinbutton");
|
|
|
|
await user.keyboard("{Tab}");
|
|
expect(input).toHaveFocus();
|
|
|
|
await user.keyboard("{Tab}");
|
|
expect(toggleButton).toHaveFocus();
|
|
|
|
await user.keyboard("{Tab}");
|
|
expect(monthSegment).toHaveFocus();
|
|
|
|
await user.keyboard("{Tab}");
|
|
expect(daySegment).toHaveFocus();
|
|
|
|
await user.keyboard("{Tab}");
|
|
expect(yearSegment).toHaveFocus();
|
|
});
|
|
|
|
it("focuses in the right order with a default value", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-04-25T00:00:00.000Z"
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
|
|
// Get elements that should receive focus when a date is already picked.
|
|
const [input, toggleButton] = await screen.findAllByRole("button")!;
|
|
const clearButton = screen.getByRole("button", {
|
|
name: "Clear date",
|
|
});
|
|
const [monthSegment, daySegment, yearSegment] =
|
|
await screen.findAllByRole("spinbutton")!;
|
|
|
|
// Navigate through elements using Tab.
|
|
await user.keyboard("{Tab}");
|
|
expect(input).toHaveFocus();
|
|
|
|
await user.keyboard("{Tab}");
|
|
expect(toggleButton).toHaveFocus();
|
|
|
|
await user.keyboard("{Tab}");
|
|
expect(monthSegment).toHaveFocus();
|
|
|
|
await user.keyboard("{Tab}");
|
|
expect(daySegment).toHaveFocus();
|
|
|
|
await user.keyboard("{Tab}");
|
|
expect(yearSegment).toHaveFocus();
|
|
|
|
await user.keyboard("{Tab}");
|
|
expect(clearButton).toHaveFocus();
|
|
});
|
|
|
|
it("picks a date", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker label="Pick a date" name="datepicker" />
|
|
</CunninghamProvider>,
|
|
);
|
|
const [input, toggleButton] = await screen.findAllByRole("button");
|
|
await user.click(input);
|
|
|
|
// Select all grid-cells.
|
|
const gridCells = await screen.findAllByRole("gridcell");
|
|
|
|
// Select the first clickable grid-cell.
|
|
const gridCellButton = within(
|
|
gridCells.filter(
|
|
(gridCell) => !gridCell.getAttribute("aria-disabled"),
|
|
)![0],
|
|
).getByRole("button")!;
|
|
|
|
// Pick a date.
|
|
await user.click(gridCellButton);
|
|
const gridCellDate = gridCellButton.getAttribute("aria-label");
|
|
|
|
// Calendar should close on date's selection.
|
|
expectCalendarToBeClosed();
|
|
expectDateFieldToBeDisplayed();
|
|
|
|
// Reopen the calendar.
|
|
await user.click(toggleButton);
|
|
|
|
// Get the selected date in calendar grid.
|
|
const selectedDate = within(
|
|
screen.getByRole("gridcell", {
|
|
selected: true,
|
|
})!,
|
|
)
|
|
.getByRole("button")!
|
|
.getAttribute("aria-label")
|
|
?.replace("selected", "");
|
|
|
|
expectDatesToBeEqual(selectedDate, gridCellDate);
|
|
|
|
// Get the selected date in the date field.
|
|
const dateFieldContent = screen.getByRole("presentation").textContent;
|
|
expectDatesToBeEqual(dateFieldContent, gridCellDate);
|
|
});
|
|
|
|
it("type a date", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-04-05T00:00:00.000Z"
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
const [monthSegment, daySegment, yearSegment] =
|
|
await screen.findAllByRole("spinbutton")!;
|
|
// Select the first segment, month one.
|
|
await user.click(monthSegment);
|
|
expect(monthSegment).toHaveFocus();
|
|
|
|
// Type month's value.
|
|
await user.keyboard("{1}{2}");
|
|
expect(daySegment).toHaveFocus();
|
|
|
|
// Type day's value.
|
|
await user.keyboard("{5}");
|
|
expect(yearSegment).toHaveFocus();
|
|
|
|
// Type year's value.
|
|
await user.keyboard("{2}{0}{2}{3}");
|
|
|
|
// Check date field value.
|
|
expectDateFieldToBeDisplayed();
|
|
const dateFieldContent = screen.getByRole("presentation").textContent;
|
|
expectDatesToBeEqual("2023-12-05", dateFieldContent);
|
|
});
|
|
|
|
it("type an invalid date", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-04-05T10:00:00.000Z"
|
|
minValue="2022-12-03T10:00:00.000Z"
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
const [monthSegment, daySegment, yearSegment] =
|
|
await screen.findAllByRole("spinbutton")!;
|
|
// Select the first segment, month one.
|
|
await user.click(monthSegment);
|
|
expect(monthSegment).toHaveFocus();
|
|
|
|
// Type month's value.
|
|
await user.keyboard("{1}{2}");
|
|
expect(daySegment).toHaveFocus();
|
|
|
|
// Type day's value.
|
|
await user.keyboard("{5}");
|
|
expect(yearSegment).toHaveFocus();
|
|
|
|
// Type year's value.
|
|
await user.keyboard("{2}{0}{2}{1}");
|
|
|
|
// Check date field value.
|
|
expectDateFieldToBeDisplayed();
|
|
const dateFieldContent = screen.getByRole("presentation").textContent;
|
|
expectDatesToBeEqual("2021-12-05", dateFieldContent);
|
|
|
|
// Make sure the date picker is showing error.
|
|
expectDatePickerStateToBe("invalid");
|
|
});
|
|
|
|
it.each([
|
|
"2022-05-25T23:59:59.000Z",
|
|
"2023-06-01T00:00:00.000Z",
|
|
"2025-03-25T12:30:00.000Z",
|
|
])("has a default value", async (defaultValue) => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue={defaultValue}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
// Get picked date.
|
|
const dateFieldContent = screen.getByRole("presentation").textContent;
|
|
expectDatesToBeEqual(defaultValue, dateFieldContent);
|
|
expectDateFieldToBeDisplayed();
|
|
});
|
|
|
|
it("has an uncontrolled a controlled value", async () => {
|
|
vi.spyOn(console, "error").mockImplementation(() => undefined);
|
|
expect(() =>
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-05-24T00:00:00.000Z"
|
|
value="2023-05-24T00:00:00.000Z"
|
|
/>
|
|
</CunninghamProvider>,
|
|
),
|
|
).toThrow(
|
|
"You cannot use both defaultValue and value props on DatePicker component",
|
|
);
|
|
});
|
|
|
|
it.each(["this_not_a_valid_date", "2023-13-13", "2025-25-05"])(
|
|
"has not a valid date value",
|
|
async (invalidDate) => {
|
|
vi.spyOn(console, "error").mockImplementation(() => undefined);
|
|
expect(() =>
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue={invalidDate}
|
|
/>
|
|
</CunninghamProvider>,
|
|
),
|
|
).toThrow(/Failed to parse date value:/);
|
|
},
|
|
);
|
|
|
|
it("clears date", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-05-24T00:00:00.000Z"
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
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 dateFieldContent = screen.getByRole("presentation").textContent;
|
|
expect(dateFieldContent).eq("MM/DD/YYYY");
|
|
|
|
const isGridCellSelected = screen
|
|
.getByRole("gridcell", { name: "24" })!
|
|
.getAttribute("aria-selected");
|
|
expect(isGridCellSelected).toBeNull();
|
|
|
|
// 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.
|
|
expectDateFieldToBeHidden();
|
|
});
|
|
|
|
it("has date inferior to minValue", async () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-01-01T00:00:00.000Z"
|
|
minValue="2023-02-01T00:00:00.000Z"
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
await expectDatePickerStateToBe("invalid");
|
|
});
|
|
|
|
it("has date superior to maxValue", async () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-03-01T00:00:00.000Z"
|
|
maxValue="2023-02-01T00:00:00.000Z"
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
await expectDatePickerStateToBe("invalid");
|
|
});
|
|
|
|
it("has an invalid value outside min/max range", async () => {
|
|
// Should be invalid if the picked date is outside the min/max range.
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-03-01T00:00:00.000Z"
|
|
maxValue="2023-04-01T00:00:00.000Z"
|
|
minValue="2023-05-01T00:00:00.000Z"
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
await expectDatePickerStateToBe("invalid");
|
|
});
|
|
|
|
it("works controlled", async () => {
|
|
const user = userEvent.setup();
|
|
const Wrapper = () => {
|
|
const [value, setValue] = useState<string | null>(
|
|
"2023-04-25T00:00:00.000Z",
|
|
);
|
|
return (
|
|
<CunninghamProvider>
|
|
<div>
|
|
<div>Value = {value}|</div>
|
|
<Button onClick={() => setValue(null)}>Clear</Button>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
value={value}
|
|
onChange={(e: string | null) => setValue(e)}
|
|
/>
|
|
</div>
|
|
</CunninghamProvider>
|
|
);
|
|
};
|
|
render(<Wrapper />);
|
|
|
|
// Make sure value is selected.
|
|
screen.getByText("Value = 2023-04-25T00:00:00.000Z|");
|
|
|
|
// Make sure value is initially render in the date field component.
|
|
const dateFieldContent = screen.getByRole("presentation").textContent;
|
|
expectDatesToBeEqual("2023-04-25T00:00:00.000Z", dateFieldContent);
|
|
|
|
// Open the calendar grid.
|
|
const toggleButton = (await screen.findAllByRole("button"))![1];
|
|
await user.click(toggleButton);
|
|
expectCalendarToBeOpen();
|
|
|
|
const gridCell = within(
|
|
screen.getByRole("gridcell", { name: "12" }),
|
|
).getByRole("button")!;
|
|
|
|
// Select a new value in the calendar grid.
|
|
await user.click(gridCell);
|
|
expectCalendarToBeClosed();
|
|
|
|
// Make sure value is selected.
|
|
screen.getByText(`Value = 2023-04-12T00:00:00.000Z|`);
|
|
|
|
// Clear value.
|
|
const clearButton = screen.getByRole("button", {
|
|
name: "Clear",
|
|
});
|
|
await user.click(clearButton);
|
|
|
|
// Make sure value is cleared.
|
|
expectDateFieldToBeHidden();
|
|
screen.getByText("Value = |");
|
|
});
|
|
|
|
it("has a timezone", async () => {
|
|
const user = userEvent.setup();
|
|
const Wrapper = () => {
|
|
const [value, setValue] = useState<string | null>(null);
|
|
return (
|
|
<CunninghamProvider>
|
|
<div>
|
|
<div>Value = {value}|</div>
|
|
<Button onClick={() => setValue(null)}>Clear</Button>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
value={value}
|
|
onChange={(e: string | null) => setValue(e)}
|
|
timezone="America/Sao_Paulo"
|
|
/>
|
|
</div>
|
|
</CunninghamProvider>
|
|
);
|
|
};
|
|
render(<Wrapper />);
|
|
|
|
// Make sure any value is selected.
|
|
screen.getByText("Value = |");
|
|
|
|
// Open the calendar grid.
|
|
const toggleButton = (await screen.findAllByRole("button"))![1];
|
|
await user.click(toggleButton);
|
|
expectCalendarToBeOpen();
|
|
|
|
const monthSegment = await screen.getByRole("spinbutton", {
|
|
name: /month/,
|
|
});
|
|
// Select the first segment, month one.
|
|
await user.click(monthSegment);
|
|
expect(monthSegment).toHaveFocus();
|
|
|
|
// Type date's value.
|
|
await user.keyboard("{5}{1}{2}{2}{0}{2}{3}");
|
|
|
|
// Make sure value is selected at midnight on America/Sao_Paulo.
|
|
screen.getByText(`Value = 2023-05-12T03:00:00.000Z|`);
|
|
});
|
|
|
|
it("renders disabled", async () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-03-01T00:00:00.000Z"
|
|
disabled={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
await expectDatePickerStateToBe("disabled");
|
|
|
|
const [input, button] = await screen.findAllByRole("button");
|
|
|
|
// Make sure toggle button and click on input are disabled.
|
|
expect(input.getAttribute("aria-disabled")).eq("true");
|
|
expect(button).toBeDisabled();
|
|
|
|
// Make sure the clear button is not visible and disabled.
|
|
expect(
|
|
screen.queryByRole("button", { name: "Clear date", hidden: true }),
|
|
).toBeDisabled();
|
|
|
|
// Make sure each segment of the date field is disabled.
|
|
const dateFieldInputs = await screen.queryAllByRole("spinbutton");
|
|
dateFieldInputs.forEach((dateFieldInput) =>
|
|
expect(dateFieldInput).toHaveAttribute("aria-disabled"),
|
|
);
|
|
});
|
|
|
|
it("renders focused", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-03-01T00:00:00.000Z"
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
const toggleButton = (await screen.findAllByRole("button"))![1];
|
|
await user.click(toggleButton);
|
|
expectCalendarToBeOpen();
|
|
await expectDatePickerStateToBe("focused");
|
|
});
|
|
|
|
it("submits forms data", async () => {
|
|
let formData: any;
|
|
const Wrapper = () => {
|
|
const onSubmit = (e: FormEvent<HTMLFormElement>) => {
|
|
e.preventDefault();
|
|
const data = new FormData(e.currentTarget);
|
|
formData = {
|
|
datepicker: data.get("datepicker"),
|
|
};
|
|
};
|
|
|
|
return (
|
|
<CunninghamProvider>
|
|
<div>
|
|
<form onSubmit={onSubmit}>
|
|
<DatePicker label="Pick a date" name="datepicker" />
|
|
<Button>Submit</Button>
|
|
</form>
|
|
</div>
|
|
</CunninghamProvider>
|
|
);
|
|
};
|
|
render(<Wrapper />);
|
|
|
|
const user = userEvent.setup();
|
|
const submitButton = screen.getByRole("button", {
|
|
name: "Submit",
|
|
});
|
|
|
|
// Submit the form being empty.
|
|
await user.click(submitButton);
|
|
expect(formData).toEqual({
|
|
datepicker: "",
|
|
});
|
|
|
|
// Open calendar
|
|
const toggleButton = (await screen.findAllByRole("button"))![1];
|
|
await user.click(toggleButton);
|
|
|
|
const monthSegment = await screen.getByRole("spinbutton", {
|
|
name: /month/,
|
|
});
|
|
// Select the first segment, month one.
|
|
await user.click(monthSegment);
|
|
expect(monthSegment).toHaveFocus();
|
|
|
|
// Type date's value.
|
|
await user.keyboard("{5}{1}{2}{2}{0}{2}{3}");
|
|
|
|
// Submit form being filled with a date.
|
|
await user.click(submitButton);
|
|
expectCalendarToBeClosed();
|
|
expectDateFieldToBeDisplayed();
|
|
|
|
// Make sure form's value matches.
|
|
// It should be equal the 12th of May at midnight in local timezone.
|
|
expect(formData).toEqual({
|
|
datepicker: "2023-05-11T22:00:00.000Z",
|
|
});
|
|
|
|
// Clear picked date.
|
|
const clearButton = screen.getByRole("button", {
|
|
name: "Clear date",
|
|
});
|
|
await user.click(clearButton);
|
|
expectDateFieldToBeDisplayed();
|
|
|
|
// Submit the form being empty.
|
|
await user.click(submitButton);
|
|
|
|
// Date field disappears when the user click outside the component.
|
|
expectDateFieldToBeHidden();
|
|
|
|
// Make sure form's value is null.
|
|
expect(formData).toEqual({
|
|
datepicker: "",
|
|
});
|
|
});
|
|
|
|
it("submits forms data with a default value", async () => {
|
|
let formData: any;
|
|
const Wrapper = () => {
|
|
const onSubmit = (e: FormEvent<HTMLFormElement>) => {
|
|
e.preventDefault();
|
|
const data = new FormData(e.currentTarget);
|
|
formData = {
|
|
datepicker: data.get("datepicker"),
|
|
};
|
|
};
|
|
|
|
return (
|
|
<CunninghamProvider>
|
|
<div>
|
|
<form onSubmit={onSubmit}>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-04-25T12:00:00.000Z"
|
|
/>
|
|
<Button>Submit</Button>
|
|
</form>
|
|
</div>
|
|
</CunninghamProvider>
|
|
);
|
|
};
|
|
render(<Wrapper />);
|
|
|
|
const user = userEvent.setup();
|
|
const submitButton = screen.getByRole("button", {
|
|
name: "Submit",
|
|
});
|
|
|
|
// Submit the form with the default value.
|
|
// Time should be equal to the initial one in UTC.
|
|
await user.click(submitButton);
|
|
expect(formData).toEqual({
|
|
datepicker: "2023-04-25T12:00:00.000Z",
|
|
});
|
|
|
|
// Open calendar
|
|
const toggleButton = (await screen.findAllByRole("button"))![1];
|
|
await user.click(toggleButton);
|
|
|
|
const monthSegment = await screen.getByRole("spinbutton", {
|
|
name: /month/,
|
|
});
|
|
// Select the first segment, month one.
|
|
await user.click(monthSegment);
|
|
expect(monthSegment).toHaveFocus();
|
|
|
|
// Type date's value.
|
|
await user.keyboard("{5}{1}{2}{2}{0}{2}{3}");
|
|
|
|
// Submit form being filled with a date.
|
|
await user.click(submitButton);
|
|
expectCalendarToBeClosed();
|
|
expectDateFieldToBeDisplayed();
|
|
|
|
// Make sure form's value matches.
|
|
// Selection should keep the default time passed to the component.
|
|
// Thus, component output should still be at noon UTC.
|
|
expect(formData).toEqual({
|
|
datepicker: "2023-05-12T12:00:00.000Z",
|
|
});
|
|
|
|
// Clear picked date.
|
|
// We lose info about the initial time.
|
|
const clearButton = screen.getByRole("button", {
|
|
name: "Clear date",
|
|
});
|
|
await user.click(clearButton);
|
|
expectDateFieldToBeDisplayed();
|
|
|
|
// Submit the form being empty.
|
|
await user.click(submitButton);
|
|
|
|
// Date field disappears when the user click outside the component.
|
|
expectDateFieldToBeHidden();
|
|
|
|
// Make sure form's value is null.
|
|
expect(formData).toEqual({
|
|
datepicker: "",
|
|
});
|
|
|
|
// Select the first segment, month one.
|
|
await user.click(monthSegment);
|
|
expect(monthSegment).toHaveFocus();
|
|
|
|
// Type a new date's value, that would be selected at midnight local timezone.
|
|
await user.keyboard("{5}{1}{2}{2}{0}{2}{3}");
|
|
|
|
// Submit form being filled with a date.
|
|
await user.click(submitButton);
|
|
expectCalendarToBeClosed();
|
|
expectDateFieldToBeDisplayed();
|
|
|
|
// Make sure form's value matches.
|
|
// It should be equal the 2023-05-12 at midnight in local timezone.
|
|
expect(formData).toEqual({
|
|
datepicker: "2023-05-11T22:00:00.000Z",
|
|
});
|
|
});
|
|
|
|
it("clicks next and previous focused month", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-05-24T00:00:00.000Z"
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
const toggleButton = (await screen.findAllByRole("button"))![1];
|
|
await user.click(toggleButton);
|
|
|
|
// Focused month of the calendar grid should be the one from the default value.
|
|
expectFocusedMonthToBeEqual("May");
|
|
|
|
const previousMonthButton = screen.getByRole("button", {
|
|
name: "Previous month",
|
|
});
|
|
const nextMonthButton = screen.getByRole("button", {
|
|
name: "Next month",
|
|
});
|
|
|
|
// Focus previous month.
|
|
await user.click(previousMonthButton);
|
|
expectFocusedMonthToBeEqual("Apr");
|
|
|
|
// Get back to the default focused month.
|
|
await user.click(nextMonthButton);
|
|
expectFocusedMonthToBeEqual("May");
|
|
|
|
// Focus next month.
|
|
await user.click(nextMonthButton);
|
|
expectFocusedMonthToBeEqual("Jun");
|
|
});
|
|
|
|
it("clicks next and previous focused year", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-05-24T00:00:00.000Z"
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
const toggleButton = (await screen.findAllByRole("button"))![1];
|
|
await user.click(toggleButton);
|
|
|
|
// Focused year of the calendar grid should be the one from the default value.
|
|
expectFocusedYearToBeEqual("2023");
|
|
|
|
const previousYearButton = screen.getByRole("button", {
|
|
name: "Previous year",
|
|
});
|
|
const nextYearButton = screen.getByRole("button", {
|
|
name: "Next year",
|
|
});
|
|
|
|
// Focus previous year.
|
|
await user.click(previousYearButton);
|
|
expectFocusedYearToBeEqual("2022");
|
|
|
|
// Get back to the default focused month.
|
|
await user.click(nextYearButton);
|
|
expectFocusedYearToBeEqual("2023");
|
|
|
|
// Focus next year.
|
|
await user.click(nextYearButton);
|
|
expectFocusedYearToBeEqual("2024");
|
|
});
|
|
|
|
it("renders disabled next and previous month", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-05-24T00:00:00.000Z"
|
|
minValue="2023-05-22T00:00:00.000Z"
|
|
maxValue="2023-05-26T00:00:00.000Z"
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
|
|
const toggleButton = (await screen.findAllByRole("button"))![1];
|
|
await user.click(toggleButton);
|
|
|
|
// minValue doesn't allow to navigate to previous month.
|
|
const previousMonthButton = screen.getByRole("button", {
|
|
name: "Previous month",
|
|
});
|
|
expect(previousMonthButton).toBeDisabled();
|
|
|
|
// maxValue doesn't allow to navigate to previous month.
|
|
const nextMonthButton = screen.getByRole("button", {
|
|
name: "Next month",
|
|
});
|
|
expect(nextMonthButton).toBeDisabled();
|
|
});
|
|
|
|
it("renders disabled next and previous year", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-05-24T00:00:00.000Z"
|
|
minValue="2023-05-22T00:00:00.000Z"
|
|
maxValue="2023-05-26T00:00:00.000Z"
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
|
|
const toggleButton = (await screen.findAllByRole("button"))![1];
|
|
await user.click(toggleButton);
|
|
|
|
// minValue doesn't allow to navigate to previous year.
|
|
const previousYearButton = screen.getByRole("button", {
|
|
name: "Previous year",
|
|
});
|
|
expect(previousYearButton).toBeDisabled();
|
|
|
|
// maxValue doesn't allow to navigate to next year.
|
|
const nextYearButton = screen.getByRole("button", {
|
|
name: "Next year",
|
|
});
|
|
expect(nextYearButton).toBeDisabled();
|
|
});
|
|
|
|
it("renders partially disabled next and previous month", async () => {
|
|
const user = userEvent.setup();
|
|
const minValue = new Date("2023-04-23T00:00:00.000z");
|
|
const maxValue = new Date("2023-06-23T00:00:00.000z");
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-05-23T00:00:00.000Z"
|
|
minValue={minValue.toISOString()}
|
|
maxValue={maxValue.toISOString()}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
|
|
// Open the calendar.
|
|
const toggleButton = (await screen.findAllByRole("button"))![1];
|
|
await user.click(toggleButton);
|
|
|
|
const previousMonthButton = screen.getByRole("button", {
|
|
name: "Previous month",
|
|
});
|
|
const nextMonthButton = screen.getByRole("button", {
|
|
name: "Next month",
|
|
});
|
|
|
|
// Previous and next month buttons should not be disabled.
|
|
// Some days in previous and next month can be selected.
|
|
expect(previousMonthButton).not.toBeDisabled();
|
|
expect(nextMonthButton).not.toBeDisabled();
|
|
|
|
// Month n-2 should not be accessible,
|
|
// as month n-1 is partially disabled,
|
|
// all days from month n-2 are disabled.
|
|
await user.click(previousMonthButton);
|
|
expect(previousMonthButton).toBeDisabled();
|
|
expect(nextMonthButton).not.toBeDisabled();
|
|
|
|
// Get all the calendar's cells to check their state.
|
|
let gridCells = await screen.findAllByRole("gridcell");
|
|
|
|
// By default, calendar's cells outside the focused month are not visible (month n-1 and n+1).
|
|
// Cells within the focused month superior to the minDate should be clickable.
|
|
// Cells inferior to the minDate, within the month, should be disabled.
|
|
gridCells.forEach((gridCell) => {
|
|
try {
|
|
const button = within(gridCell).getByRole("button")!;
|
|
const value = new Date(
|
|
button
|
|
.getAttribute("aria-label")!
|
|
.replace("First available date", ""),
|
|
);
|
|
expect(value.getMonth() === minValue.getMonth());
|
|
if (value.getDate() < minValue.getDate()) {
|
|
expect(button).toBeDisabled();
|
|
} else {
|
|
expect(button).not.toBeDisabled();
|
|
}
|
|
} catch (e: any) {
|
|
// Make sure outside grid-cells render any button element, even disabled.
|
|
expect(e.message).contains(
|
|
'Unable to find an accessible element with the role "button"',
|
|
);
|
|
}
|
|
});
|
|
|
|
await user.click(nextMonthButton);
|
|
expect(previousMonthButton).not.toBeDisabled();
|
|
expect(nextMonthButton).not.toBeDisabled();
|
|
|
|
await user.click(nextMonthButton);
|
|
expect(previousMonthButton).not.toBeDisabled();
|
|
expect(nextMonthButton).toBeDisabled();
|
|
|
|
gridCells = await screen.findAllByRole("gridcell");
|
|
gridCells.forEach((gridCell) => {
|
|
try {
|
|
const button = within(gridCell).getByRole("button")!;
|
|
const buttonLabel = button
|
|
.getAttribute("aria-label")
|
|
?.replace("Last available date", "");
|
|
expect(buttonLabel).toBeDefined();
|
|
const value = new Date(buttonLabel!);
|
|
expect(value.getMonth() === minValue.getMonth());
|
|
if (value.getDate() > maxValue.getDate()) {
|
|
expect(button).toBeDisabled();
|
|
} else {
|
|
expect(button).not.toBeDisabled();
|
|
}
|
|
} catch (e: any) {
|
|
// Make sure outside grid-cells render any button element, even disabled.
|
|
expect(e.message).contains(
|
|
'Unable to find an accessible element with the role "button"',
|
|
);
|
|
}
|
|
});
|
|
});
|
|
|
|
it("selects a focused month", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-05-23T00:00:00.000Z"
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
const toggleButton = (await screen.findAllByRole("button"))![1];
|
|
await user.click(toggleButton);
|
|
|
|
// Get month dropdown.
|
|
const monthDropdown = screen.getByRole("combobox", {
|
|
name: "Select a month",
|
|
});
|
|
|
|
const [monthMenu, yearMenu] = await screen.findAllByRole("listbox");
|
|
|
|
expectMenuToBeClosed(monthMenu);
|
|
expectMenuToBeClosed(yearMenu);
|
|
|
|
// Make sure the selected item matched the default value.
|
|
let focusedMonth = monthDropdown.textContent?.replace(
|
|
"arrow_drop_down",
|
|
"",
|
|
);
|
|
expect(focusedMonth).eq("May");
|
|
|
|
// Open month dropdown.
|
|
await user.click(monthDropdown);
|
|
expectMenuToBeOpen(monthMenu);
|
|
expectMenuToBeClosed(yearMenu);
|
|
|
|
// Select a month option.
|
|
const option: HTMLLIElement = screen.getByRole("option", {
|
|
name: "August",
|
|
});
|
|
await user.click(option);
|
|
|
|
expectMenuToBeClosed(monthMenu);
|
|
expectMenuToBeClosed(yearMenu);
|
|
|
|
// Make sure focused month has properly updated.
|
|
focusedMonth = monthDropdown.textContent?.replace("arrow_drop_down", "");
|
|
expect(focusedMonth).eq("Aug");
|
|
});
|
|
|
|
it("selects a focused year", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-05-23T00:00:00.000Z"
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
const toggleButton = (await screen.findAllByRole("button"))![1];
|
|
await user.click(toggleButton);
|
|
|
|
// Get year dropdown.
|
|
const yearDropdown = screen.getByRole("combobox", {
|
|
name: "Select a year",
|
|
});
|
|
|
|
const [monthMenu, yearMenu] = await screen.findAllByRole("listbox");
|
|
|
|
expectMenuToBeClosed(yearMenu);
|
|
expectMenuToBeClosed(monthMenu);
|
|
|
|
// Make sure the selected item matched the default value.
|
|
let focusedYear = yearDropdown.textContent?.replace("arrow_drop_down", "");
|
|
expect(focusedYear).eq("2023");
|
|
|
|
await user.click(yearDropdown);
|
|
expectMenuToBeOpen(yearMenu);
|
|
expectMenuToBeClosed(monthMenu);
|
|
|
|
// Select a year option.
|
|
const option: HTMLLIElement = screen.getByRole("option", {
|
|
name: "2024",
|
|
});
|
|
await user.click(option);
|
|
|
|
expectMenuToBeClosed(yearMenu);
|
|
expectMenuToBeClosed(monthMenu);
|
|
|
|
// Make sure focused month has properly updated.
|
|
focusedYear = yearDropdown.textContent?.replace("arrow_drop_down", "");
|
|
expect(focusedYear).eq("2024");
|
|
});
|
|
|
|
it("renders only cell within the focused month", async () => {
|
|
const user = userEvent.setup();
|
|
const defaultValue = new Date("2023-05-23T00:00:00.000Z");
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue={defaultValue.toISOString()}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
// Open the calendar.
|
|
const toggleButton = (await screen.findAllByRole("button"))![1];
|
|
await user.click(toggleButton);
|
|
|
|
// Get all grid-cells from the calendar.
|
|
const gridCells = await screen.findAllByRole("gridcell");
|
|
let visibleValues: Array<Date> = [];
|
|
// Make sure that all visible grid-cells are within the focused month.
|
|
gridCells.forEach((gridCell) => {
|
|
try {
|
|
const button = within(gridCell).getByRole("button");
|
|
const value = new Date(
|
|
button.getAttribute("aria-label")!.replace("selected", ""),
|
|
);
|
|
expect(button).not.toBeDisabled();
|
|
expect(defaultValue.getMonth() === value.getMonth());
|
|
visibleValues = [value, ...visibleValues];
|
|
} catch (e: any) {
|
|
// Make sure outside grid-cells render any button element, even disabled.
|
|
expect(e.message).contains(
|
|
'Unable to find an accessible element with the role "button"',
|
|
);
|
|
}
|
|
});
|
|
// Make sure we get as many visible values as days in the focused month.
|
|
expect(visibleValues.length).eq(31);
|
|
});
|
|
|
|
it("navigate previous focused month with keyboard", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-05-01T00:00:00.000Z"
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
|
|
// Open calendar.
|
|
const toggleButton = (await screen.findAllByRole("button"))![1];
|
|
await user.click(toggleButton);
|
|
|
|
// Navigate to the previous month using keyboard.
|
|
// default value is the cell of the calendar grid.
|
|
// Thus, navigating one cell up, changes the focused month.
|
|
await user.keyboard("{ArrowUp}");
|
|
expectFocusedMonthToBeEqual("Apr");
|
|
|
|
// Reopen the calendar menu to reset the focused month.
|
|
await user.click(toggleButton);
|
|
await user.click(toggleButton);
|
|
|
|
expectFocusedMonthToBeEqual("May");
|
|
|
|
await user.keyboard("{ArrowLeft}");
|
|
expectFocusedMonthToBeEqual("Apr");
|
|
});
|
|
|
|
it("navigate next focused month with keyboard", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
defaultValue="2023-05-31T00:00:00.000Z"
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
|
|
const toggleButton = (await screen.findAllByRole("button"))![1];
|
|
await user.click(toggleButton);
|
|
|
|
await user.keyboard("{ArrowDown}");
|
|
expectFocusedMonthToBeEqual("Jun");
|
|
|
|
// Reopen the calendar menu to reset the focused month.
|
|
await user.click(toggleButton);
|
|
await user.click(toggleButton);
|
|
|
|
expectFocusedMonthToBeEqual("May");
|
|
|
|
// Navigate to the previous month using keyboard.
|
|
// default value is the last cell of the calendar grid.
|
|
// Thus, navigating one cell right, changes the focused month.
|
|
await user.keyboard("{ArrowRight}");
|
|
expectFocusedMonthToBeEqual("Jun");
|
|
});
|
|
|
|
it("uses the locale props calendar system", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
locale="hi-IN-u-ca-indian"
|
|
defaultValue="2023-06-25T00:00:00.000Z"
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
|
|
const input = (await screen.findAllByRole("button"))[0];
|
|
|
|
// Toggle button opens the calendar.
|
|
await user.click(input);
|
|
expectCalendarToBeOpen();
|
|
expectDateFieldToBeDisplayed();
|
|
|
|
// Make sure dateField is in the right locale
|
|
const dateFieldContent = screen.getByRole("presentation").textContent;
|
|
expect(dateFieldContent).eq("4/4/1945 शक");
|
|
|
|
// Make sure month is in the right locale
|
|
const focusedMonth = screen
|
|
.getByRole("combobox", {
|
|
name: "Select a month",
|
|
})!
|
|
.textContent?.replace("arrow_drop_down", "");
|
|
expect(focusedMonth).eq("आषाढ़");
|
|
|
|
// Make sure year is in the right locale
|
|
const focusedYear = screen
|
|
.getByRole("combobox", {
|
|
name: "Select a year",
|
|
})!
|
|
.textContent?.replace("arrow_drop_down", "");
|
|
expect(focusedYear).eq("1945 शक");
|
|
|
|
// Make sure weekdays are in the right locale
|
|
screen.getByText("रवि");
|
|
screen.getByText("सोम");
|
|
screen.getByText("मंगल");
|
|
screen.getByText("बुध");
|
|
screen.getByText("गुरु");
|
|
screen.getByText("शुक्र");
|
|
screen.getByText("शनि");
|
|
});
|
|
|
|
it("uses the cunningham provider props calendar systems", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider currentLocale="fr-FR">
|
|
<DatePicker
|
|
label="Pick a date"
|
|
defaultValue="2023-06-25T00:00:00.000Z"
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
|
|
const input = (await screen.findAllByRole("button"))[0];
|
|
|
|
// Toggle button opens the calendar.
|
|
await user.click(input);
|
|
expectCalendarToBeOpen();
|
|
expectDateFieldToBeDisplayed();
|
|
|
|
// Make sure dateField is in the right locale
|
|
const dateFieldContent = screen.getByRole("presentation").textContent;
|
|
expect(dateFieldContent).eq("25/06/2023");
|
|
|
|
// Make sure month is in the right locale
|
|
const focusedMonth = screen
|
|
.getByRole("combobox", {
|
|
name: "Sélectionner un mois",
|
|
})!
|
|
.textContent?.replace("arrow_drop_down", "");
|
|
expect(focusedMonth).eq("juin");
|
|
|
|
// Make sure year is in the right locale
|
|
const focusedYear = screen
|
|
.getByRole("combobox", {
|
|
name: "Sélectionner une année",
|
|
})!
|
|
.textContent?.replace("arrow_drop_down", "");
|
|
expect(focusedYear).eq("2023");
|
|
|
|
// Make sure weekdays are in the right locale
|
|
screen.getByText("lun.");
|
|
screen.getByText("mar.");
|
|
screen.getByText("mer.");
|
|
screen.getByText("jeu.");
|
|
screen.getByText("ven.");
|
|
screen.getByText("sam.");
|
|
screen.getByText("dim.");
|
|
});
|
|
|
|
it("makes sure the locale props override the cunningham provider calendar system", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider currentLocale="fr-FR">
|
|
<DatePicker
|
|
label="Pick a date"
|
|
defaultValue="2023-06-25T00:00:00.000Z"
|
|
locale="hi-IN-u-ca-indian"
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
|
|
const input = (await screen.findAllByRole("button"))[0];
|
|
|
|
// Toggle button opens the calendar.
|
|
await user.click(input);
|
|
expectCalendarToBeOpen();
|
|
expectDateFieldToBeDisplayed();
|
|
|
|
// Make sure dateField is in the right locale
|
|
const dateFieldContent = screen.getByRole("presentation").textContent;
|
|
expect(dateFieldContent).eq("4/4/1945 शक");
|
|
|
|
// Make sure month is in the right locale
|
|
// And aria-label uses the right translation.
|
|
const focusedMonth = screen
|
|
.getByRole("combobox", {
|
|
name: "Sélectionner un mois",
|
|
})!
|
|
.textContent?.replace("arrow_drop_down", "");
|
|
expect(focusedMonth).eq("आषाढ़");
|
|
|
|
// Make sure year is in the right locale
|
|
// And aria-label uses the right translation.
|
|
const focusedYear = screen
|
|
.getByRole("combobox", {
|
|
name: "Sélectionner une année",
|
|
})!
|
|
.textContent?.replace("arrow_drop_down", "");
|
|
expect(focusedYear).eq("1945 शक");
|
|
|
|
// Make sure weekdays are in the right locale
|
|
screen.getByText("रवि");
|
|
screen.getByText("सोम");
|
|
screen.getByText("मंगल");
|
|
screen.getByText("बुध");
|
|
screen.getByText("गुरु");
|
|
screen.getByText("शुक्र");
|
|
screen.getByText("शनि");
|
|
});
|
|
|
|
it("has a wrong locale props", async () => {
|
|
vi.spyOn(console, "error").mockImplementation(() => undefined);
|
|
expect(() =>
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker label="Pick a date" locale="111" />
|
|
</CunninghamProvider>,
|
|
),
|
|
).toThrow("Incorrect locale information provided");
|
|
});
|
|
|
|
it("keeps time component while selecting a date", async () => {
|
|
const user = userEvent.setup();
|
|
const Wrapper = () => {
|
|
const [value, setValue] = useState<string | null>(
|
|
"2023-04-25T12:00:00.000Z",
|
|
);
|
|
return (
|
|
<CunninghamProvider>
|
|
<div>
|
|
<div>Value = {value}|</div>
|
|
<Button onClick={() => setValue(null)}>Clear</Button>
|
|
<DatePicker
|
|
label="Pick a date"
|
|
name="datepicker"
|
|
value={value}
|
|
onChange={(e: string | null) => setValue(e)}
|
|
/>
|
|
</div>
|
|
</CunninghamProvider>
|
|
);
|
|
};
|
|
render(<Wrapper />);
|
|
|
|
// Make sure initial value is printed.
|
|
screen.getByText("Value = 2023-04-25T12:00:00.000Z|");
|
|
|
|
// Make sure value is initially rendered in the date field component.
|
|
const dateFieldContent = screen.getByRole("presentation").textContent;
|
|
expectDatesToBeEqual("2023-04-25", dateFieldContent);
|
|
|
|
// Open the calendar grid.
|
|
const toggleButton = (await screen.findAllByRole("button"))![1];
|
|
await user.click(toggleButton);
|
|
expectCalendarToBeOpen();
|
|
|
|
const gridCell = within(
|
|
screen.getByRole("gridcell", { name: "12" }),
|
|
).getByRole("button")!;
|
|
|
|
// Select a new value in the calendar grid.
|
|
await user.click(gridCell);
|
|
expectCalendarToBeClosed();
|
|
|
|
// Make sure value is selected, with the same time as the initial value.
|
|
screen.getByText(`Value = 2023-04-12T12:00:00.000Z|`);
|
|
});
|
|
|
|
it("renders with className", async () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker label="Date" className="my-custom-class" />
|
|
</CunninghamProvider>,
|
|
);
|
|
expect(
|
|
document.querySelector(".c__field.my-custom-class"),
|
|
).toBeInTheDocument();
|
|
});
|
|
|
|
describe("classic variant", () => {
|
|
it("renders with classic variant", async () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker label="Pick a date" variant="classic" />
|
|
</CunninghamProvider>,
|
|
);
|
|
// In classic mode, label is rendered outside the wrapper with its own class
|
|
expect(
|
|
document.querySelector(".c__date-picker__label"),
|
|
).toBeInTheDocument();
|
|
|
|
expect(
|
|
document.querySelector(".c__date-picker--classic"),
|
|
).toBeInTheDocument();
|
|
});
|
|
|
|
it("label is always static in classic variant", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker label="Pick a date" variant="classic" />
|
|
</CunninghamProvider>,
|
|
);
|
|
|
|
const label = screen.getByText("Pick a date");
|
|
|
|
// In classic variant, label is outside the wrapper with c__date-picker__label class
|
|
expect(label.classList.contains("c__date-picker__label")).toBe(true);
|
|
|
|
// Open calendar
|
|
const toggleButton = (await screen.findAllByRole("button"))![1];
|
|
await user.click(toggleButton);
|
|
|
|
// Label should still have the same class
|
|
expect(label.classList.contains("c__date-picker__label")).toBe(true);
|
|
});
|
|
|
|
it("defaults to floating variant", () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker label="Pick a date" />
|
|
</CunninghamProvider>,
|
|
);
|
|
expect(
|
|
document.querySelector(".c__date-picker--classic"),
|
|
).not.toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe("hideLabel", () => {
|
|
it("hides label visually but keeps it accessible in floating variant", () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker label="Pick a date" hideLabel />
|
|
</CunninghamProvider>,
|
|
);
|
|
// Label should be visually hidden via LabelledBox
|
|
const label = screen.getByText("Pick a date");
|
|
expect(label.closest("label")).toHaveClass("c__offscreen");
|
|
});
|
|
|
|
it("hides label visually but keeps it accessible in classic variant", () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<DatePicker label="Pick a date" variant="classic" hideLabel />
|
|
</CunninghamProvider>,
|
|
);
|
|
// Label should be visually hidden with c__offscreen class
|
|
const label = screen.getByText("Pick a date");
|
|
expect(label).toHaveClass("c__offscreen");
|
|
// The visible label class should not be present
|
|
expect(
|
|
document.querySelector(".c__date-picker__label:not(.c__offscreen)"),
|
|
).not.toBeInTheDocument();
|
|
});
|
|
});
|
|
});
|