react-day-picker 9.6.2 → 9.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/dist/cjs/DayPicker.js +14 -2
  2. package/dist/cjs/DayPicker.js.map +1 -1
  3. package/dist/cjs/classes/DateLib.js +4 -4
  4. package/dist/cjs/classes/DateLib.js.map +1 -1
  5. package/dist/esm/DayPicker.js +14 -2
  6. package/dist/esm/DayPicker.js.map +1 -1
  7. package/dist/esm/classes/DateLib.js +1 -1
  8. package/dist/esm/classes/DateLib.js.map +1 -1
  9. package/package.json +2 -4
  10. package/src/.eslintignore +1 -0
  11. package/src/.eslintrc.cjs +27 -0
  12. package/src/DayPicker.test.tsx +199 -0
  13. package/src/DayPicker.tsx +629 -0
  14. package/src/UI.ts +365 -0
  15. package/src/classes/CalendarDay.test.ts +17 -0
  16. package/src/classes/CalendarDay.ts +61 -0
  17. package/src/classes/CalendarMonth.test.ts +28 -0
  18. package/src/classes/CalendarMonth.ts +15 -0
  19. package/src/classes/CalendarWeek.test.ts +21 -0
  20. package/src/classes/CalendarWeek.ts +13 -0
  21. package/src/classes/DateLib.ts +615 -0
  22. package/src/classes/index.ts +4 -0
  23. package/src/components/Button.tsx +13 -0
  24. package/src/components/CaptionLabel.tsx +13 -0
  25. package/src/components/Chevron.tsx +42 -0
  26. package/src/components/Day.tsx +28 -0
  27. package/src/components/DayButton.tsx +29 -0
  28. package/src/components/Dropdown.tsx +71 -0
  29. package/src/components/DropdownNav.tsx +13 -0
  30. package/src/components/Footer.tsx +13 -0
  31. package/src/components/Month.tsx +24 -0
  32. package/src/components/MonthCaption.tsx +23 -0
  33. package/src/components/MonthGrid.tsx +13 -0
  34. package/src/components/Months.tsx +13 -0
  35. package/src/components/MonthsDropdown.tsx +16 -0
  36. package/src/components/Nav.tsx +90 -0
  37. package/src/components/NextMonthButton.tsx +18 -0
  38. package/src/components/Option.tsx +13 -0
  39. package/src/components/PreviousMonthButton.tsx +20 -0
  40. package/src/components/Root.tsx +19 -0
  41. package/src/components/Select.tsx +13 -0
  42. package/src/components/Week.tsx +20 -0
  43. package/src/components/WeekNumber.tsx +21 -0
  44. package/src/components/WeekNumberHeader.tsx +15 -0
  45. package/src/components/Weekday.tsx +13 -0
  46. package/src/components/Weekdays.tsx +17 -0
  47. package/src/components/Weeks.tsx +13 -0
  48. package/src/components/YearsDropdown.tsx +16 -0
  49. package/src/components/custom-components.tsx +26 -0
  50. package/src/formatters/formatCaption.test.ts +27 -0
  51. package/src/formatters/formatCaption.ts +23 -0
  52. package/src/formatters/formatDay.test.ts +7 -0
  53. package/src/formatters/formatDay.ts +16 -0
  54. package/src/formatters/formatMonthDropdown.test.ts +19 -0
  55. package/src/formatters/formatMonthDropdown.ts +15 -0
  56. package/src/formatters/formatWeekNumber.test.ts +5 -0
  57. package/src/formatters/formatWeekNumber.ts +13 -0
  58. package/src/formatters/formatWeekNumberHeader.ts +10 -0
  59. package/src/formatters/formatWeekdayName.test.ts +15 -0
  60. package/src/formatters/formatWeekdayName.ts +16 -0
  61. package/src/formatters/formatYearDropdown.test.ts +7 -0
  62. package/src/formatters/formatYearDropdown.ts +21 -0
  63. package/src/formatters/index.ts +7 -0
  64. package/src/helpers/broadcastCalendar.test.ts +43 -0
  65. package/src/helpers/calculateFocusTarget.ts +51 -0
  66. package/src/helpers/endOfBroadcastWeek.test.ts +25 -0
  67. package/src/helpers/endOfBroadcastWeek.ts +16 -0
  68. package/src/helpers/getBroadcastWeeksInMonth.test.ts +23 -0
  69. package/src/helpers/getBroadcastWeeksInMonth.ts +31 -0
  70. package/src/helpers/getClassNamesForModifiers.ts +26 -0
  71. package/src/helpers/getComponents.ts +11 -0
  72. package/src/helpers/getDataAttributes.test.tsx +48 -0
  73. package/src/helpers/getDataAttributes.tsx +21 -0
  74. package/src/helpers/getDates.test.ts +190 -0
  75. package/src/helpers/getDates.ts +64 -0
  76. package/src/helpers/getDays.test.ts +30 -0
  77. package/src/helpers/getDays.ts +16 -0
  78. package/src/helpers/getDefaultClassNames.test.ts +47 -0
  79. package/src/helpers/getDefaultClassNames.ts +33 -0
  80. package/src/helpers/getDisplayMonths.test.ts +44 -0
  81. package/src/helpers/getDisplayMonths.ts +20 -0
  82. package/src/helpers/getFocusableDate.ts +59 -0
  83. package/src/helpers/getFormatters.test.ts +48 -0
  84. package/src/helpers/getFormatters.ts +19 -0
  85. package/src/helpers/getInitialMonth.test.ts +79 -0
  86. package/src/helpers/getInitialMonth.ts +41 -0
  87. package/src/helpers/getLabels.ts +10 -0
  88. package/src/helpers/getMonthOptions.test.ts +226 -0
  89. package/src/helpers/getMonthOptions.ts +37 -0
  90. package/src/helpers/getMonths.test.ts +88 -0
  91. package/src/helpers/getMonths.ts +90 -0
  92. package/src/helpers/getNavMonth.test.ts +253 -0
  93. package/src/helpers/getNavMonth.ts +70 -0
  94. package/src/helpers/getNextFocus.test.tsx +99 -0
  95. package/src/helpers/getNextFocus.tsx +67 -0
  96. package/src/helpers/getNextMonth.test.ts +101 -0
  97. package/src/helpers/getNextMonth.ts +45 -0
  98. package/src/helpers/getPossibleFocusDate.test.ts +144 -0
  99. package/src/helpers/getPreviousMonth.test.ts +77 -0
  100. package/src/helpers/getPreviousMonth.ts +40 -0
  101. package/src/helpers/getStyleForModifiers.test.ts +92 -0
  102. package/src/helpers/getStyleForModifiers.ts +21 -0
  103. package/src/helpers/getWeekdays.test.ts +44 -0
  104. package/src/helpers/getWeekdays.ts +29 -0
  105. package/src/helpers/getWeeks.test.ts +30 -0
  106. package/src/helpers/getWeeks.ts +9 -0
  107. package/src/helpers/getYearOptions.test.ts +46 -0
  108. package/src/helpers/getYearOptions.ts +34 -0
  109. package/src/helpers/index.ts +2 -0
  110. package/src/helpers/startOfBroadcastWeek.test.ts +24 -0
  111. package/src/helpers/startOfBroadcastWeek.ts +19 -0
  112. package/src/helpers/useControlledValue.test.ts +45 -0
  113. package/src/helpers/useControlledValue.ts +33 -0
  114. package/src/index.ts +15 -0
  115. package/src/jalali.tsx +2 -0
  116. package/src/labels/index.ts +12 -0
  117. package/src/labels/labelDayButton.test.ts +41 -0
  118. package/src/labels/labelDayButton.ts +31 -0
  119. package/src/labels/labelGrid.test.ts +7 -0
  120. package/src/labels/labelGrid.ts +23 -0
  121. package/src/labels/labelGridcell.test.ts +7 -0
  122. package/src/labels/labelGridcell.ts +22 -0
  123. package/src/labels/labelMonthDropdown.test.ts +5 -0
  124. package/src/labels/labelMonthDropdown.ts +12 -0
  125. package/src/labels/labelNav.test.ts +5 -0
  126. package/src/labels/labelNav.ts +10 -0
  127. package/src/labels/labelNext.test.ts +5 -0
  128. package/src/labels/labelNext.ts +13 -0
  129. package/src/labels/labelPrevious.test.ts +5 -0
  130. package/src/labels/labelPrevious.ts +13 -0
  131. package/src/labels/labelWeekNumber.test.ts +5 -0
  132. package/src/labels/labelWeekNumber.ts +15 -0
  133. package/src/labels/labelWeekNumberHeader.test.ts +5 -0
  134. package/src/labels/labelWeekNumberHeader.ts +12 -0
  135. package/src/labels/labelWeekday.test.ts +15 -0
  136. package/src/labels/labelWeekday.ts +16 -0
  137. package/src/labels/labelYearDropdown.test.ts +5 -0
  138. package/src/labels/labelYearDropdown.ts +12 -0
  139. package/src/locale.ts +1 -0
  140. package/src/persian.tsx +86 -0
  141. package/src/selection/useMulti.test.tsx +41 -0
  142. package/src/selection/useMulti.tsx +74 -0
  143. package/src/selection/useRange.test.tsx +154 -0
  144. package/src/selection/useRange.tsx +73 -0
  145. package/src/selection/useSingle.test.tsx +38 -0
  146. package/src/selection/useSingle.tsx +69 -0
  147. package/src/types/deprecated.ts +230 -0
  148. package/src/types/index.ts +4 -0
  149. package/src/types/props.test.tsx +71 -0
  150. package/src/types/props.ts +675 -0
  151. package/src/types/selection.ts +57 -0
  152. package/src/types/shared.ts +442 -0
  153. package/src/useAnimation.test.tsx +134 -0
  154. package/src/useAnimation.ts +203 -0
  155. package/src/useCalendar.ts +178 -0
  156. package/src/useDayPicker.test.tsx +142 -0
  157. package/src/useDayPicker.ts +93 -0
  158. package/src/useFocus.ts +87 -0
  159. package/src/useGetModifiers.test.tsx +154 -0
  160. package/src/useGetModifiers.tsx +122 -0
  161. package/src/useSelection.ts +26 -0
  162. package/src/utc.tsx +10 -0
  163. package/src/utils/addToRange.test.ts +117 -0
  164. package/src/utils/addToRange.ts +87 -0
  165. package/src/utils/dateMatchModifiers.test.ts +120 -0
  166. package/src/utils/dateMatchModifiers.ts +88 -0
  167. package/src/utils/index.ts +7 -0
  168. package/src/utils/rangeContainsDayOfWeek.test.ts +48 -0
  169. package/src/utils/rangeContainsDayOfWeek.ts +35 -0
  170. package/src/utils/rangeContainsModifiers.test.ts +230 -0
  171. package/src/utils/rangeContainsModifiers.ts +125 -0
  172. package/src/utils/rangeIncludesDate.test.ts +46 -0
  173. package/src/utils/rangeIncludesDate.ts +43 -0
  174. package/src/utils/rangeOverlaps.test.ts +60 -0
  175. package/src/utils/rangeOverlaps.ts +22 -0
  176. package/src/utils/typeguards.test.ts +83 -0
  177. package/src/utils/typeguards.ts +70 -0
@@ -0,0 +1,34 @@
1
+ import type { DateLib } from "../classes/DateLib.js";
2
+ import { DropdownOption } from "../components/Dropdown.js";
3
+ import type { Formatters } from "../types/index.js";
4
+
5
+ /** Return the years to show in the dropdown. */
6
+ export function getYearOptions(
7
+ navStart: Date | undefined,
8
+ navEnd: Date | undefined,
9
+ formatters: Pick<Formatters, "formatYearDropdown">,
10
+ dateLib: DateLib
11
+ ): DropdownOption[] | undefined {
12
+ if (!navStart) return undefined;
13
+ if (!navEnd) return undefined;
14
+ const { startOfYear, endOfYear, addYears, getYear, isBefore, isSameYear } =
15
+ dateLib;
16
+ const firstNavYear = startOfYear(navStart);
17
+ const lastNavYear = endOfYear(navEnd);
18
+ const years: Date[] = [];
19
+
20
+ let year = firstNavYear;
21
+ while (isBefore(year, lastNavYear) || isSameYear(year, lastNavYear)) {
22
+ years.push(year);
23
+ year = addYears(year, 1);
24
+ }
25
+
26
+ return years.map((year) => {
27
+ const label = formatters.formatYearDropdown(year, dateLib);
28
+ return {
29
+ value: getYear(year),
30
+ label,
31
+ disabled: false
32
+ };
33
+ });
34
+ }
@@ -0,0 +1,2 @@
1
+ // Only export helpers that can be useful to other developers.
2
+ export * from "./getDefaultClassNames.js";
@@ -0,0 +1,24 @@
1
+ import { defaultDateLib } from "../classes";
2
+
3
+ import { startOfBroadcastWeek } from "./startOfBroadcastWeek";
4
+
5
+ describe("startOfBroadcastWeek", () => {
6
+ test.each([
7
+ [new Date(2023, 0, 1), new Date(2022, 11, 26)], // January, Sunday
8
+ [new Date(2023, 1, 1), new Date(2023, 0, 30)], // February, Wednesday
9
+ [new Date(2023, 2, 1), new Date(2023, 1, 27)], // March, Wednesday
10
+ [new Date(2023, 3, 1), new Date(2023, 2, 27)], // April, Saturday
11
+ [new Date(2023, 4, 1), new Date(2023, 4, 1)], // May, Monday
12
+ [new Date(2023, 5, 1), new Date(2023, 4, 29)], // June, Thursday
13
+ [new Date(2023, 6, 1), new Date(2023, 5, 26)], // July, Saturday
14
+ [new Date(2023, 7, 1), new Date(2023, 6, 31)], // August, Tuesday
15
+ [new Date(2023, 8, 1), new Date(2023, 7, 28)], // September, Friday
16
+ [new Date(2023, 9, 1), new Date(2023, 8, 25)], // October, Sunday
17
+ [new Date(2023, 10, 1), new Date(2023, 9, 30)], // November, Wednesday
18
+ [new Date(2023, 11, 1), new Date(2023, 10, 27)] // December, Friday
19
+ ])("startOfBroadcastWeek(%s) should return %s", (inputDate, expectedDate) => {
20
+ expect(startOfBroadcastWeek(inputDate, defaultDateLib)).toEqual(
21
+ expectedDate
22
+ );
23
+ });
24
+ });
@@ -0,0 +1,19 @@
1
+ import type { DateLib } from "../classes/index.js";
2
+
3
+ /**
4
+ * Return the start date of the week in the broadcast calendar.
5
+ *
6
+ * @since 9.4.0
7
+ */
8
+ export function startOfBroadcastWeek(date: Date, dateLib: DateLib): Date {
9
+ const firstOfMonth = dateLib.startOfMonth(date);
10
+ const dayOfWeek = firstOfMonth.getDay();
11
+
12
+ if (dayOfWeek === 1) {
13
+ return firstOfMonth;
14
+ } else if (dayOfWeek === 0) {
15
+ return dateLib.addDays(firstOfMonth, -1 * 6);
16
+ } else {
17
+ return dateLib.addDays(firstOfMonth, -1 * (dayOfWeek - 1));
18
+ }
19
+ }
@@ -0,0 +1,45 @@
1
+ import { act, renderHook } from "@testing-library/react";
2
+
3
+ import { useControlledValue } from "./useControlledValue";
4
+
5
+ describe("when the value is controlled", () => {
6
+ const defaultValue: string | undefined = "foo"; // not controlled
7
+ const controlledValue: string | undefined = "bar"; // now controlled
8
+ test("should return the controlled value", () => {
9
+ const { result } = renderHook(() =>
10
+ useControlledValue<string>(defaultValue, controlledValue)
11
+ );
12
+ expect(result.current[0]).toBe(controlledValue);
13
+ });
14
+ describe("when setting a new value", () => {
15
+ const newValue = "taz";
16
+ test("should return the controlled value instead", async () => {
17
+ const { result } = renderHook(() =>
18
+ useControlledValue<string>(defaultValue, controlledValue)
19
+ );
20
+ act(() => result.current[1](newValue));
21
+ expect(result.current[0]).toBe(controlledValue);
22
+ });
23
+ });
24
+ });
25
+
26
+ describe("when the value is not controlled", () => {
27
+ const defaultValue = "foo";
28
+ const controlledValue = undefined;
29
+ test("should return the value", () => {
30
+ const { result } = renderHook(() =>
31
+ useControlledValue<string>(defaultValue, controlledValue)
32
+ );
33
+ expect(result.current[0]).toBe(defaultValue);
34
+ });
35
+ describe("when setting a new value", () => {
36
+ const newValue = "bar";
37
+ test("should return the new value", async () => {
38
+ const { result } = renderHook(() =>
39
+ useControlledValue<string>(defaultValue, controlledValue)
40
+ );
41
+ act(() => result.current[1](newValue));
42
+ expect(result.current[0]).toBe(newValue);
43
+ });
44
+ });
45
+ });
@@ -0,0 +1,33 @@
1
+ import { useState } from "react";
2
+
3
+ export type DispatchStateAction<T> = React.Dispatch<React.SetStateAction<T>>;
4
+
5
+ /**
6
+ * A custom hook for managing both controlled and uncontrolled component states.
7
+ *
8
+ * @example
9
+ * // Uncontrolled usage
10
+ * const [value, setValue] = useControlledValue(0, undefined);
11
+ *
12
+ * // Controlled usage
13
+ * const [value, setValue] = useControlledValue(0, props.value);
14
+ *
15
+ * @template T - The type of the value.
16
+ * @param {T} defaultValue - The initial value for the uncontrolled state.
17
+ * @param {T | undefined} controlledValue - The value for the controlled state.
18
+ * If undefined, the component will use the uncontrolled state.
19
+ * @returns {[T, DispatchStateAction<T>]} - Returns a tuple where the first
20
+ * element is the current value (either controlled or uncontrolled) and the
21
+ * second element is a setter function to update the value.
22
+ */
23
+ export function useControlledValue<T>(
24
+ defaultValue: T,
25
+ controlledValue: T | undefined
26
+ ): [T, DispatchStateAction<T>] {
27
+ const [uncontrolledValue, setValue] = useState(defaultValue);
28
+
29
+ const value =
30
+ controlledValue === undefined ? uncontrolledValue : controlledValue;
31
+
32
+ return [value, setValue] as [T, DispatchStateAction<T>];
33
+ }
package/src/index.ts ADDED
@@ -0,0 +1,15 @@
1
+ export * from "./DayPicker.js";
2
+ export * from "./types/index.js";
3
+
4
+ export * from "./classes/index.js";
5
+ export * from "./components/custom-components.js";
6
+
7
+ export * from "./formatters/index.js";
8
+ export * from "./helpers/index.js";
9
+ export * from "./labels/index.js";
10
+ export * from "./utils/index.js";
11
+ export * from "./UI.js";
12
+
13
+ export * from "./useDayPicker.js";
14
+
15
+ export { TZDate } from "@date-fns/tz";
package/src/jalali.tsx ADDED
@@ -0,0 +1,2 @@
1
+ /** @deprecated Import from `react-day-picker/persian` instead. */
2
+ export * from "./persian.js";
@@ -0,0 +1,12 @@
1
+ export * from "./labelGrid.js";
2
+ export * from "./labelGridcell.js";
3
+ export * from "./labelDayButton.js";
4
+ export * from "./labelNav.js";
5
+ export * from "./labelGrid.js";
6
+ export * from "./labelMonthDropdown.js";
7
+ export * from "./labelNext.js";
8
+ export * from "./labelPrevious.js";
9
+ export * from "./labelWeekday.js";
10
+ export * from "./labelWeekNumber.js";
11
+ export * from "./labelWeekNumberHeader.js";
12
+ export * from "./labelYearDropdown.js";
@@ -0,0 +1,41 @@
1
+ import { es } from "date-fns/locale/es";
2
+
3
+ import type { Modifiers } from "../types";
4
+
5
+ import { labelDayButton } from "./labelDayButton";
6
+
7
+ const day = new Date(2022, 10, 21);
8
+ const dayModifiers: Modifiers = {
9
+ disabled: false,
10
+ focusable: false,
11
+ focused: false,
12
+ hidden: false,
13
+ outside: false,
14
+ range_end: false,
15
+ range_middle: false,
16
+ range_start: false,
17
+ selected: false,
18
+ today: false
19
+ };
20
+
21
+ describe("when the day is selected", () => {
22
+ test("return the label", () => {
23
+ expect(labelDayButton(day, { ...dayModifiers, selected: true })).toEqual(
24
+ "Monday, November 21st, 2022, selected"
25
+ );
26
+ });
27
+ });
28
+
29
+ describe("when the day is today", () => {
30
+ test("return the label", () => {
31
+ expect(labelDayButton(day, { ...dayModifiers, today: true })).toEqual(
32
+ "Today, Monday, November 21st, 2022"
33
+ );
34
+ });
35
+ });
36
+
37
+ test("should return the localized label", () => {
38
+ expect(labelDayButton(day, dayModifiers, { locale: es })).toEqual(
39
+ "lunes, 21 de noviembre de 2022"
40
+ );
41
+ });
@@ -0,0 +1,31 @@
1
+ import { DateLib, type DateLibOptions } from "../classes/DateLib.js";
2
+ import type { Modifiers } from "../types/index.js";
3
+
4
+ /**
5
+ * The ARIA label for the day button.
6
+ *
7
+ * Use the `modifiers` argument to add additional context to the label, e.g.
8
+ * when a day is selected or is today.
9
+ *
10
+ * @defaultValue The formatted date.
11
+ * @group Labels
12
+ * @see https://daypicker.dev/docs/translation#aria-labels
13
+ */
14
+ export function labelDayButton(
15
+ date: Date,
16
+ /** The modifiers for the day. */
17
+ modifiers: Modifiers,
18
+ options?: DateLibOptions,
19
+ dateLib?: DateLib
20
+ ) {
21
+ let label = (dateLib ?? new DateLib(options)).format(date, "PPPP");
22
+ if (modifiers.today) label = `Today, ${label}`;
23
+ if (modifiers.selected) label = `${label}, selected`;
24
+ return label;
25
+ }
26
+
27
+ /**
28
+ * @ignore
29
+ * @deprecated Use `labelDayButton` instead.
30
+ */
31
+ export const labelDay = labelDayButton;
@@ -0,0 +1,7 @@
1
+ import { labelGrid } from "./labelGrid";
2
+
3
+ const day = new Date(2022, 10, 21);
4
+
5
+ test("return the label", () => {
6
+ expect(labelGrid(day)).toEqual("November 2022");
7
+ });
@@ -0,0 +1,23 @@
1
+ import { DateLib, type DateLibOptions } from "../classes/DateLib.js";
2
+
3
+ /**
4
+ * The ARIA label for the month grid, that will be announced when entering the
5
+ * grid.
6
+ *
7
+ * @defaultValue `LLLL y` (e.g. "November 2022")
8
+ * @group Labels
9
+ * @see https://daypicker.dev/docs/translation#aria-labels
10
+ */
11
+ export function labelGrid(
12
+ date: Date,
13
+ options?: DateLibOptions,
14
+ dateLib?: DateLib
15
+ ) {
16
+ return (dateLib ?? new DateLib(options)).format(date, "LLLL y");
17
+ }
18
+
19
+ /**
20
+ * @ignore
21
+ * @deprecated Use {@link labelGrid} instead.
22
+ */
23
+ export const labelCaption = labelGrid;
@@ -0,0 +1,7 @@
1
+ import { labelGridcell } from "./labelGridcell";
2
+
3
+ const day = new Date(2022, 10, 21);
4
+
5
+ test("return the label", () => {
6
+ expect(labelGridcell(day)).toEqual("Monday, November 21st, 2022");
7
+ });
@@ -0,0 +1,22 @@
1
+ import { DateLib, type DateLibOptions } from "../classes/DateLib.js";
2
+ import type { Modifiers } from "../types/index.js";
3
+
4
+ /**
5
+ * The label for the day gridcell when the calendar is not interactive.
6
+ *
7
+ * @group Labels
8
+ * @see https://daypicker.dev/docs/translation#aria-labels
9
+ */
10
+ export function labelGridcell(
11
+ date: Date,
12
+ /** The modifiers for the day. */
13
+ modifiers?: Modifiers,
14
+ options?: DateLibOptions,
15
+ dateLib?: DateLib
16
+ ) {
17
+ let label = (dateLib ?? new DateLib(options)).format(date, "PPPP");
18
+ if (modifiers?.today) {
19
+ label = `Today, ${label}`;
20
+ }
21
+ return label;
22
+ }
@@ -0,0 +1,5 @@
1
+ import { labelMonthDropdown } from "./labelMonthDropdown";
2
+
3
+ test("should return the label", () => {
4
+ expect(labelMonthDropdown({})).toEqual("Choose the Month");
5
+ });
@@ -0,0 +1,12 @@
1
+ import type { DateLibOptions } from "../classes/DateLib.js";
2
+
3
+ /**
4
+ * The ARIA label for the months dropdown.
5
+ *
6
+ * @defaultValue `"Choose the Month"`
7
+ * @group Labels
8
+ * @see https://daypicker.dev/docs/translation#aria-labels
9
+ */
10
+ export function labelMonthDropdown(options?: DateLibOptions) {
11
+ return "Choose the Month";
12
+ }
@@ -0,0 +1,5 @@
1
+ import { labelNav } from "./labelNav";
2
+
3
+ test("should return the label", () => {
4
+ expect(labelNav()).toEqual("");
5
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The ARIA label for the navigation toolbar.
3
+ *
4
+ * @defaultValue `""`
5
+ * @group Labels
6
+ * @see https://daypicker.dev/docs/translation#aria-labels
7
+ */
8
+ export function labelNav(): string {
9
+ return "";
10
+ }
@@ -0,0 +1,5 @@
1
+ import { labelNext } from "./labelNext";
2
+
3
+ test("should return the label", () => {
4
+ expect(labelNext(new Date())).toEqual("Go to the Next Month");
5
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The ARIA label for next month button.
3
+ *
4
+ * @defaultValue `"Go to the Next Month"`
5
+ * @group Labels
6
+ * @see https://daypicker.dev/docs/translation#aria-labels
7
+ */
8
+ export function labelNext(
9
+ /** `undefined` where there's no next month to navigate to. */
10
+ month: Date | undefined
11
+ ) {
12
+ return "Go to the Next Month";
13
+ }
@@ -0,0 +1,5 @@
1
+ import { labelPrevious } from "./labelPrevious";
2
+
3
+ test("should return the label", () => {
4
+ expect(labelPrevious(new Date())).toEqual("Go to the Previous Month");
5
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The ARIA label for previous month button.
3
+ *
4
+ * @defaultValue `"Go to the Previous Month"`
5
+ * @group Labels
6
+ * @see https://daypicker.dev/docs/translation#aria-labels
7
+ */
8
+ export function labelPrevious(
9
+ /** Undefined where there's no previous month to navigate to. */
10
+ month: Date | undefined
11
+ ) {
12
+ return "Go to the Previous Month";
13
+ }
@@ -0,0 +1,5 @@
1
+ import { labelWeekNumber } from "./labelWeekNumber";
2
+
3
+ test("should return the label", () => {
4
+ expect(labelWeekNumber(2)).toEqual("Week 2");
5
+ });
@@ -0,0 +1,15 @@
1
+ import type { DateLibOptions } from "../classes/DateLib.js";
2
+
3
+ /**
4
+ * The ARIA label for the week number cell (the first cell in the row).
5
+ *
6
+ * @defaultValue `Week ${weekNumber}`
7
+ * @group Labels
8
+ * @see https://daypicker.dev/docs/translation#aria-labels
9
+ */
10
+ export function labelWeekNumber(
11
+ weekNumber: number,
12
+ options?: DateLibOptions
13
+ ): string {
14
+ return `Week ${weekNumber}`;
15
+ }
@@ -0,0 +1,5 @@
1
+ import { labelWeekNumberHeader } from "./labelWeekNumberHeader";
2
+
3
+ test("should return the label", () => {
4
+ expect(labelWeekNumberHeader()).toEqual("Week Number");
5
+ });
@@ -0,0 +1,12 @@
1
+ import type { DateLibOptions } from "../classes/DateLib.js";
2
+
3
+ /**
4
+ * The ARIA label for the week number header element.
5
+ *
6
+ * @defaultValue `"Week Number"`
7
+ * @group Labels
8
+ * @see https://daypicker.dev/docs/translation#aria-labels
9
+ */
10
+ export function labelWeekNumberHeader(options?: DateLibOptions): string {
11
+ return "Week Number";
12
+ }
@@ -0,0 +1,15 @@
1
+ import { es } from "date-fns/locale/es";
2
+
3
+ import { labelWeekday } from "./labelWeekday";
4
+
5
+ const weekDay = new Date(2022, 10, 21);
6
+
7
+ test("should return the formatted weekday name", () => {
8
+ expect(labelWeekday(weekDay)).toEqual("Monday");
9
+ });
10
+
11
+ describe("when a locale is passed in", () => {
12
+ test("should format using the locale", () => {
13
+ expect(labelWeekday(weekDay, { locale: es })).toEqual("lunes");
14
+ });
15
+ });
@@ -0,0 +1,16 @@
1
+ import { DateLib, type DateLibOptions } from "../classes/DateLib.js";
2
+
3
+ /**
4
+ * The ARIA label for the Weekday column header.
5
+ *
6
+ * @defaultValue `"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"`
7
+ * @group Labels
8
+ * @see https://daypicker.dev/docs/translation#aria-labels
9
+ */
10
+ export function labelWeekday(
11
+ date: Date,
12
+ options?: DateLibOptions,
13
+ dateLib?: DateLib
14
+ ): string {
15
+ return (dateLib ?? new DateLib(options)).format(date, "cccc");
16
+ }
@@ -0,0 +1,5 @@
1
+ import { labelYearDropdown } from "./labelYearDropdown";
2
+
3
+ test("should return the label", () => {
4
+ expect(labelYearDropdown()).toEqual("Choose the Year");
5
+ });
@@ -0,0 +1,12 @@
1
+ import type { DateLibOptions } from "../classes/DateLib.js";
2
+
3
+ /**
4
+ * The ARIA label for the years dropdown.
5
+ *
6
+ * @defaultValue `"Choose the Year"`
7
+ * @group Labels
8
+ * @see https://daypicker.dev/docs/translation#aria-labels
9
+ */
10
+ export function labelYearDropdown(options?: DateLibOptions) {
11
+ return "Choose the Year";
12
+ }
package/src/locale.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "date-fns/locale";
@@ -0,0 +1,86 @@
1
+ import React from "react";
2
+
3
+ import * as dateFnsJalali from "date-fns-jalali";
4
+ import { Locale } from "date-fns-jalali";
5
+ import * as locales from "date-fns-jalali/locale";
6
+
7
+ import {
8
+ DateLib,
9
+ DateLibOptions,
10
+ DayPicker as DayPickerComponent
11
+ } from "./index.js";
12
+ import type { DayPickerProps } from "./types/props.js";
13
+
14
+ export const faIR = locales.faIR;
15
+ export const enUS = locales.enUS;
16
+
17
+ /**
18
+ * Render the Persian Calendar.
19
+ *
20
+ * @see https://daypicker.dev/docs/localization#persian-calendar
21
+ */
22
+ export function DayPicker(
23
+ props: DayPickerProps & {
24
+ /**
25
+ * The locale to use in the calendar.
26
+ *
27
+ * @default `faIR`
28
+ */
29
+ locale?: Locale;
30
+ /**
31
+ * The direction of the text in the calendar.
32
+ *
33
+ * @default `rtl`
34
+ */
35
+ dir?: DayPickerProps["dir"];
36
+ /**
37
+ * The date library to use in the calendar.
38
+ *
39
+ * @default `jalaliDateLib` from `date-fns-jalali`
40
+ */
41
+ dateLib?: DayPickerProps["dateLib"];
42
+ /**
43
+ * The numeral system to use when formatting dates.
44
+ *
45
+ * - `latn`: Latin (Western Arabic)
46
+ * - `arab`: Arabic-Indic
47
+ * - `arabext`: Eastern Arabic-Indic (Persian)
48
+ * - `deva`: Devanagari
49
+ * - `beng`: Bengali
50
+ * - `guru`: Gurmukhi
51
+ * - `gujr`: Gujarati
52
+ * - `orya`: Oriya
53
+ * - `tamldec`: Tamil
54
+ * - `telu`: Telugu
55
+ * - `knda`: Kannada
56
+ * - `mlym`: Malayalam
57
+ *
58
+ * @defaultValue `arabext` Eastern Arabic-Indic (Persian)
59
+ * @see https://daypicker.dev/docs/translation#numeral-systems
60
+ */
61
+ numerals?: DayPickerProps["numerals"];
62
+ }
63
+ ) {
64
+ const dateLib = getDateLib({
65
+ locale: props.locale,
66
+ weekStartsOn: props.broadcastCalendar ? 1 : props.weekStartsOn,
67
+ firstWeekContainsDate: props.firstWeekContainsDate,
68
+ useAdditionalWeekYearTokens: props.useAdditionalWeekYearTokens,
69
+ useAdditionalDayOfYearTokens: props.useAdditionalDayOfYearTokens,
70
+ timeZone: props.timeZone
71
+ });
72
+ return (
73
+ <DayPickerComponent
74
+ {...props}
75
+ locale={props.locale ?? faIR}
76
+ numerals={props.numerals ?? "arabext"}
77
+ dir={props.dir ?? "rtl"}
78
+ dateLib={dateLib}
79
+ />
80
+ );
81
+ }
82
+
83
+ /** Returns the date library used in the calendar. */
84
+ export const getDateLib = (options?: DateLibOptions) => {
85
+ return new DateLib(options, dateFnsJalali);
86
+ };
@@ -0,0 +1,41 @@
1
+ import { act, renderHook } from "@/test/render";
2
+
3
+ import { defaultDateLib } from "../classes/DateLib";
4
+ import { DayPickerProps } from "../types";
5
+
6
+ import { useMulti } from "./useMulti";
7
+
8
+ describe("useMulti", () => {
9
+ it("uses the selected value from props when onSelect is provided", () => {
10
+ const mockOnSelect = jest.fn();
11
+ const selectedDates = [new Date(2023, 9, 1), new Date(2023, 9, 2)];
12
+ const props: DayPickerProps = {
13
+ mode: "multiple",
14
+ selected: selectedDates,
15
+ onSelect: mockOnSelect
16
+ };
17
+
18
+ const { result } = renderHook(() => useMulti(props, defaultDateLib));
19
+
20
+ expect(result.current.selected).toBe(selectedDates);
21
+ });
22
+
23
+ it("uses the internally selected value when onSelect is not provided", () => {
24
+ const initialSelectedDates = [new Date(2023, 9, 1), new Date(2023, 9, 2)];
25
+ const props: DayPickerProps = {
26
+ mode: "multiple",
27
+ selected: initialSelectedDates
28
+ };
29
+
30
+ const { result } = renderHook(() => useMulti(props, defaultDateLib));
31
+
32
+ act(() => {
33
+ result.current.select?.(new Date(2023, 9, 3), {}, {} as React.MouseEvent);
34
+ });
35
+
36
+ expect(result.current.selected).toEqual([
37
+ ...initialSelectedDates,
38
+ new Date(2023, 9, 3)
39
+ ]);
40
+ });
41
+ });