1 | import React from "react";
|
2 |
|
3 | import { addMonths, startOfDay, startOfMonth } from "date-fns";
|
4 | import { defaultLocale } from "react-day-picker";
|
5 |
|
6 | import {
|
7 | activeElement,
|
8 | dateButton,
|
9 | grid,
|
10 | nav,
|
11 | previousButton
|
12 | } from "@/test/elements";
|
13 | import { fireEvent, render, screen } from "@/test/render";
|
14 | import { user } from "@/test/user";
|
15 |
|
16 | import { DayPicker } from "./DayPicker";
|
17 | import { MonthProps } from "./components/Month";
|
18 | import { MonthsProps } from "./components/Months";
|
19 | import { labelGrid } from "./labels";
|
20 |
|
21 | const testId = "test";
|
22 | const dayPicker = () => screen.getByTestId(testId);
|
23 |
|
24 | test("should render a date picker component", () => {
|
25 | render(<DayPicker data-testid={testId} />);
|
26 | expect(dayPicker()).toBeInTheDocument();
|
27 | });
|
28 |
|
29 | test("render the navigation and month grids", () => {
|
30 | render(<DayPicker data-testid={testId} />);
|
31 |
|
32 | expect(nav()).toBeInTheDocument();
|
33 | expect(grid()).toBeInTheDocument();
|
34 | });
|
35 |
|
36 | test("apply classnames and style according to props", () => {
|
37 | render(
|
38 | <DayPicker
|
39 | data-testid={testId}
|
40 | className="custom-class"
|
41 | numberOfMonths={2}
|
42 | showWeekNumber
|
43 | style={{ color: "red" }}
|
44 | />
|
45 | );
|
46 |
|
47 | expect(dayPicker()).toHaveClass("rdp-root");
|
48 | expect(dayPicker()).toHaveClass("custom-class");
|
49 | expect(dayPicker()).toHaveStyle({ color: "red" });
|
50 | });
|
51 |
|
52 | test("use custom components", () => {
|
53 | render(
|
54 | <DayPicker
|
55 | data-testid={testId}
|
56 | components={{
|
57 | Nav: () => <div>Custom Navigation</div>,
|
58 | Month: (props: MonthProps) => <div>Custom Month</div>,
|
59 | Months: (props: MonthsProps) => (
|
60 | <div {...props}>
|
61 | Custom Months<div>{props.children}</div>
|
62 | </div>
|
63 | ),
|
64 | Footer: () => <div>Custom Footer</div>
|
65 | }}
|
66 | footer="Footer"
|
67 | />
|
68 | );
|
69 |
|
70 | expect(dayPicker()).toHaveTextContent("Custom Navigation");
|
71 | expect(dayPicker()).toHaveTextContent("Custom Months");
|
72 | expect(dayPicker()).toHaveTextContent("Custom Month");
|
73 | expect(dayPicker()).toHaveTextContent("Custom Footer");
|
74 | });
|
75 |
|
76 | describe("when the date picker is focused", () => {
|
77 | test("focus the previous button", async () => {
|
78 | render(<DayPicker />);
|
79 | await user.tab();
|
80 | expect(activeElement()).toBe(previousButton());
|
81 | });
|
82 |
|
83 | test("on RTL, focus the previous button", async () => {
|
84 | render(<DayPicker dir="rtl" />);
|
85 | await user.tab();
|
86 | expect(activeElement()).toBe(previousButton());
|
87 | });
|
88 | });
|
89 |
|
90 | describe("when the grid is focused", () => {
|
91 | const today = new Date();
|
92 |
|
93 | beforeAll(() => jest.setSystemTime(today));
|
94 | afterAll(() => jest.useRealTimers());
|
95 |
|
96 | test("should focus the today's date", async () => {
|
97 | render(<DayPicker mode="single" today={today} />);
|
98 | await user.tab();
|
99 | await user.tab();
|
100 | await user.tab();
|
101 | expect(activeElement()).toBe(dateButton(today));
|
102 | });
|
103 | describe("when the today’s date is disabled", () => {
|
104 | test("should focus the first day of the month", async () => {
|
105 | render(<DayPicker mode="single" disabled={today} />);
|
106 | await user.tab();
|
107 | await user.tab();
|
108 | await user.tab();
|
109 | expect(activeElement()).toBe(dateButton(startOfMonth(today)));
|
110 | });
|
111 | });
|
112 | });
|
113 |
|
114 | describe("when a day is mouse entered", () => {
|
115 | const handleDayMouseEnter = jest.fn();
|
116 | const handleDayMouseLeave = jest.fn();
|
117 | const today = startOfDay(new Date());
|
118 | beforeEach(async () => {
|
119 | render(
|
120 | <DayPicker
|
121 | today={today}
|
122 | defaultMonth={today}
|
123 | mode="single"
|
124 | onDayMouseEnter={handleDayMouseEnter}
|
125 | onDayMouseLeave={handleDayMouseLeave}
|
126 | />
|
127 | );
|
128 | fireEvent.mouseEnter(dateButton(today));
|
129 | fireEvent.mouseLeave(dateButton(today));
|
130 | });
|
131 | test("should call the event handler", async () => {
|
132 | expect(handleDayMouseEnter).toHaveBeenCalled();
|
133 | expect(handleDayMouseLeave).toHaveBeenCalled();
|
134 | });
|
135 | });
|
136 |
|
137 | describe("when the `month` is changed programmatically", () => {
|
138 | test("should update the calendar to reflect the new month", async () => {
|
139 | const initialMonth = new Date(2023, 0, 1);
|
140 | const newMonth = new Date(2023, 1, 1);
|
141 | const { rerender } = render(
|
142 | <DayPicker month={initialMonth} mode="single" />
|
143 | );
|
144 | expect(grid("January 2023")).toBeInTheDocument();
|
145 | rerender(<DayPicker month={newMonth} mode="single" />);
|
146 | expect(grid("February 2023")).toBeInTheDocument();
|
147 | });
|
148 | });
|
149 |
|
150 | describe("when the `startMonth` is changed programmatically", () => {
|
151 | test("should update the calendar to reflect the new month", async () => {
|
152 | const initialStartMonth = new Date();
|
153 | const newStartMonth = addMonths(new Date(), 2);
|
154 | const { rerender } = render(
|
155 | <DayPicker startMonth={initialStartMonth} mode="single" />
|
156 | );
|
157 | expect(grid(labelGrid(initialStartMonth))).toBeInTheDocument();
|
158 | rerender(<DayPicker startMonth={newStartMonth} mode="single" />);
|
159 | expect(grid(labelGrid(newStartMonth))).toBeInTheDocument();
|
160 | });
|
161 | });
|
162 |
|
163 | test("extends the default locale", () => {
|
164 | render(
|
165 | <DayPicker
|
166 | month={new Date(2024, 0)}
|
167 | locale={{
|
168 | localize: {
|
169 | ...defaultLocale.localize,
|
170 | month: () => "bar"
|
171 | }
|
172 | }}
|
173 | />
|
174 | );
|
175 |
|
176 | expect(grid("bar 2024")).toBeInTheDocument();
|
177 | });
|