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,30 @@
1
+ import { CalendarDay, CalendarWeek, CalendarMonth } from "../classes";
2
+
3
+ import { getDays } from "./getDays";
4
+
5
+ const days1 = [
6
+ new CalendarDay(new Date(2023, 1, 12), new Date(2023, 1, 1)),
7
+ new CalendarDay(new Date(2023, 1, 13), new Date(2023, 1, 1))
8
+ ];
9
+ const days2 = [
10
+ new CalendarDay(new Date(2023, 1, 13), new Date(2023, 1, 1)),
11
+ new CalendarDay(new Date(2023, 1, 14), new Date(2023, 1, 1))
12
+ ];
13
+ const days3 = [
14
+ new CalendarDay(new Date(2023, 2, 12), new Date(2023, 2, 1)),
15
+ new CalendarDay(new Date(2023, 2, 13), new Date(2023, 2, 1))
16
+ ];
17
+ const days4 = [
18
+ new CalendarDay(new Date(2023, 2, 13), new Date(2023, 2, 1)),
19
+ new CalendarDay(new Date(2023, 2, 14), new Date(2023, 2, 1))
20
+ ];
21
+ const weeks1 = [new CalendarWeek(1, days1), new CalendarWeek(2, days2)];
22
+ const weeks2 = [new CalendarWeek(3, days3), new CalendarWeek(4, days4)];
23
+ const months = [
24
+ new CalendarMonth(days1[0].date, weeks1),
25
+ new CalendarMonth(days1[0].date, weeks2)
26
+ ];
27
+
28
+ it("should return all the days belonging to the calendar by merging the days in the weeks for each month", () => {
29
+ expect(getDays(months)).toEqual([...days1, ...days2, ...days3, ...days4]);
30
+ });
@@ -0,0 +1,16 @@
1
+ import type { CalendarDay, CalendarMonth } from "../classes/index.js";
2
+
3
+ /**
4
+ * Returns all the days belonging to the calendar by merging the days in the
5
+ * weeks for each month.
6
+ */
7
+ export function getDays(calendarMonths: CalendarMonth[]) {
8
+ const initialDays: CalendarDay[] = [];
9
+ return calendarMonths.reduce((days, month) => {
10
+ const initialDays: CalendarDay[] = [];
11
+ const weekDays: CalendarDay[] = month.weeks.reduce((weekDays, week) => {
12
+ return [...weekDays, ...week.days];
13
+ }, initialDays);
14
+ return [...days, ...weekDays];
15
+ }, initialDays);
16
+ }
@@ -0,0 +1,47 @@
1
+ import { getDefaultClassNames } from "./getDefaultClassNames";
2
+
3
+ test("should return the default classnames", () => {
4
+ expect(getDefaultClassNames()).toStrictEqual({
5
+ button_next: "rdp-button_next",
6
+ button_previous: "rdp-button_previous",
7
+ root: "rdp-root",
8
+ caption_label: "rdp-caption_label",
9
+ chevron: "rdp-chevron",
10
+ day: "rdp-day",
11
+ day_button: "rdp-day_button",
12
+ disabled: "rdp-disabled",
13
+ dropdowns: "rdp-dropdowns",
14
+ dropdown_root: "rdp-dropdown_root",
15
+ dropdown: "rdp-dropdown",
16
+ focused: "rdp-focused",
17
+ footer: "rdp-footer",
18
+ hidden: "rdp-hidden",
19
+ month_caption: "rdp-month_caption",
20
+ month_grid: "rdp-month_grid",
21
+ month: "rdp-month",
22
+ weeks: "rdp-weeks",
23
+ months_dropdown: "rdp-months_dropdown",
24
+ months: "rdp-months",
25
+ nav: "rdp-nav",
26
+ outside: "rdp-outside",
27
+ range_end: "rdp-range_end",
28
+ range_middle: "rdp-range_middle",
29
+ range_start: "rdp-range_start",
30
+ selected: "rdp-selected",
31
+ today: "rdp-today",
32
+ week_number: "rdp-week_number",
33
+ week_number_header: "rdp-week_number_header",
34
+ week: "rdp-week",
35
+ weekday: "rdp-weekday",
36
+ weekdays: "rdp-weekdays",
37
+ years_dropdown: "rdp-years_dropdown",
38
+ caption_after_enter: "rdp-caption_after_enter",
39
+ caption_after_exit: "rdp-caption_after_exit",
40
+ caption_before_enter: "rdp-caption_before_enter",
41
+ caption_before_exit: "rdp-caption_before_exit",
42
+ weeks_after_enter: "rdp-weeks_after_enter",
43
+ weeks_after_exit: "rdp-weeks_after_exit",
44
+ weeks_before_enter: "rdp-weeks_before_enter",
45
+ weeks_before_exit: "rdp-weeks_before_exit"
46
+ });
47
+ });
@@ -0,0 +1,33 @@
1
+ import { UI, DayFlag, SelectionState, Animation } from "../UI.js";
2
+ import type { ClassNames } from "../types/index.js";
3
+
4
+ /**
5
+ * Get the default class names for the UI elements.
6
+ *
7
+ * @group Utilities
8
+ */
9
+ export function getDefaultClassNames(): ClassNames {
10
+ const classNames: Partial<Required<ClassNames>> = {};
11
+
12
+ for (const key in UI) {
13
+ classNames[UI[key as keyof typeof UI]] =
14
+ `rdp-${UI[key as keyof typeof UI]}`;
15
+ }
16
+
17
+ for (const key in DayFlag) {
18
+ classNames[DayFlag[key as keyof typeof DayFlag]] =
19
+ `rdp-${DayFlag[key as keyof typeof DayFlag]}`;
20
+ }
21
+
22
+ for (const key in SelectionState) {
23
+ classNames[SelectionState[key as keyof typeof SelectionState]] =
24
+ `rdp-${SelectionState[key as keyof typeof SelectionState]}`;
25
+ }
26
+
27
+ for (const key in Animation) {
28
+ classNames[Animation[key as keyof typeof Animation]] =
29
+ `rdp-${Animation[key as keyof typeof Animation]}`;
30
+ }
31
+
32
+ return classNames as Required<ClassNames>;
33
+ }
@@ -0,0 +1,44 @@
1
+ import { defaultDateLib } from "../classes/DateLib";
2
+
3
+ import { getDisplayMonths } from "./getDisplayMonths";
4
+
5
+ describe("getDisplayMonths", () => {
6
+ it("should return the months to display in the calendar", () => {
7
+ const firstMonth = new Date(2020, 0);
8
+ const expectedResult = [new Date(2020, 0)];
9
+ const result = getDisplayMonths(firstMonth, undefined, {}, defaultDateLib);
10
+ expect(result).toEqual(expectedResult);
11
+ });
12
+
13
+ it("should return the months to display in the calendar when the number of months is greater than 1", () => {
14
+ const firstMonth = new Date(2020, 0);
15
+ const expectedResult = [
16
+ new Date(2020, 0),
17
+ new Date(2020, 1),
18
+ new Date(2020, 2)
19
+ ];
20
+ const result = getDisplayMonths(
21
+ firstMonth,
22
+ undefined,
23
+ {
24
+ numberOfMonths: 3
25
+ },
26
+ defaultDateLib
27
+ );
28
+ expect(result).toEqual(expectedResult);
29
+ });
30
+
31
+ it("should return the months to display in the calendar when passing a max date", () => {
32
+ const firstMonth = new Date(2020, 0);
33
+ const expectedResult = [new Date(2020, 0), new Date(2020, 1)];
34
+ const result = getDisplayMonths(
35
+ firstMonth,
36
+ new Date(2020, 1, 10),
37
+ {
38
+ numberOfMonths: 3
39
+ },
40
+ defaultDateLib
41
+ );
42
+ expect(result).toEqual(expectedResult);
43
+ });
44
+ });
@@ -0,0 +1,20 @@
1
+ import type { DateLib } from "../classes/DateLib.js";
2
+ import type { DayPickerProps } from "../types/index.js";
3
+
4
+ export function getDisplayMonths(
5
+ firstDisplayedMonth: Date,
6
+ calendarEndMonth: Date | undefined,
7
+ props: Pick<DayPickerProps, "numberOfMonths">,
8
+ dateLib: DateLib
9
+ ) {
10
+ const { numberOfMonths = 1 } = props;
11
+ const months: Date[] = [];
12
+ for (let i = 0; i < numberOfMonths; i++) {
13
+ const month = dateLib.addMonths(firstDisplayedMonth, i);
14
+ if (calendarEndMonth && month > calendarEndMonth) {
15
+ break;
16
+ }
17
+ months.push(month);
18
+ }
19
+ return months;
20
+ }
@@ -0,0 +1,59 @@
1
+ import type { DateLib } from "../classes/DateLib.js";
2
+ import type {
3
+ DayPickerProps,
4
+ MoveFocusBy,
5
+ MoveFocusDir
6
+ } from "../types/index.js";
7
+
8
+ /** Return the next date that should be focused. */
9
+ export function getFocusableDate(
10
+ moveBy: MoveFocusBy,
11
+ moveDir: MoveFocusDir,
12
+ refDate: Date,
13
+ navStart: Date | undefined,
14
+ navEnd: Date | undefined,
15
+ props: Pick<DayPickerProps, "ISOWeek" | "broadcastCalendar">,
16
+ dateLib: DateLib
17
+ ): Date {
18
+ const { ISOWeek, broadcastCalendar } = props;
19
+ const {
20
+ addDays,
21
+ addMonths,
22
+ addWeeks,
23
+ addYears,
24
+ endOfBroadcastWeek,
25
+ endOfISOWeek,
26
+ endOfWeek,
27
+ max,
28
+ min,
29
+ startOfBroadcastWeek,
30
+ startOfISOWeek,
31
+ startOfWeek
32
+ } = dateLib;
33
+ const moveFns = {
34
+ day: addDays,
35
+ week: addWeeks,
36
+ month: addMonths,
37
+ year: addYears,
38
+ startOfWeek: (date: Date) =>
39
+ broadcastCalendar
40
+ ? startOfBroadcastWeek(date, dateLib)
41
+ : ISOWeek
42
+ ? startOfISOWeek(date)
43
+ : startOfWeek(date),
44
+ endOfWeek: (date: Date) =>
45
+ broadcastCalendar
46
+ ? endOfBroadcastWeek(date, dateLib)
47
+ : ISOWeek
48
+ ? endOfISOWeek(date)
49
+ : endOfWeek(date)
50
+ };
51
+
52
+ let focusableDate = moveFns[moveBy](refDate, moveDir === "after" ? 1 : -1);
53
+ if (moveDir === "before" && navStart) {
54
+ focusableDate = max([navStart, focusableDate]);
55
+ } else if (moveDir === "after" && navEnd) {
56
+ focusableDate = min([navEnd, focusableDate]);
57
+ }
58
+ return focusableDate;
59
+ }
@@ -0,0 +1,48 @@
1
+ import * as defaultFormatters from "../formatters";
2
+
3
+ import { getFormatters } from "./getFormatters";
4
+
5
+ const customFormattersMock = {
6
+ formatMonthCaption: jest.fn(),
7
+ formatYearCaption: jest.fn()
8
+ };
9
+
10
+ test("returns default formatters when custom formatters are not provided", () => {
11
+ const formatters = getFormatters(undefined);
12
+ expect(formatters).toEqual(defaultFormatters);
13
+ });
14
+
15
+ test("merges custom formatters with default formatters", () => {
16
+ const formatters = getFormatters(customFormattersMock);
17
+ expect(formatters).toEqual(expect.objectContaining(customFormattersMock));
18
+ });
19
+
20
+ test("assigns `formatMonthCaption` to `formatCaption` if `formatCaption` is not defined", () => {
21
+ const result = getFormatters({ formatMonthCaption: () => "customMonth" });
22
+ expect(result.formatCaption(new Date(), {})).toBe("customMonth");
23
+ expect(result.formatMonthCaption(new Date(), {})).toBe("customMonth");
24
+ });
25
+
26
+ test("does not overwrite `formatCaption` if already defined", () => {
27
+ const result = getFormatters({
28
+ formatMonthCaption: () => "customMonth",
29
+ formatCaption: () => "customCaption"
30
+ });
31
+ expect(result.formatCaption(new Date(), {})).toBe("customCaption");
32
+ expect(result.formatMonthCaption(new Date(), {})).toBe("customMonth");
33
+ });
34
+
35
+ test("assigns `formatYearCaption` to `formatYearDropdown` if `formatYearDropdown` is not defined", () => {
36
+ const result = getFormatters({ formatYearCaption: () => "customYear" });
37
+ expect(result.formatYearDropdown(new Date(2000, 0))).toBe("customYear");
38
+ expect(result.formatYearCaption(new Date(2000, 0))).toBe("customYear");
39
+ });
40
+
41
+ test("does not overwrite `formatYearDropdown` if already defined", () => {
42
+ const result = getFormatters({
43
+ formatYearCaption: () => "customYear",
44
+ formatYearDropdown: () => "customDropdown"
45
+ });
46
+ expect(result.formatYearDropdown(new Date())).toBe("customDropdown");
47
+ expect(result.formatYearCaption(new Date())).toBe("customYear");
48
+ });
@@ -0,0 +1,19 @@
1
+ import * as defaultFormatters from "../formatters/index.js";
2
+ import type { DayPickerProps } from "../types/index.js";
3
+
4
+ /** Return the formatters from the props merged with the default formatters. */
5
+ export function getFormatters(customFormatters: DayPickerProps["formatters"]) {
6
+ if (customFormatters?.formatMonthCaption && !customFormatters.formatCaption) {
7
+ customFormatters.formatCaption = customFormatters.formatMonthCaption;
8
+ }
9
+ if (
10
+ customFormatters?.formatYearCaption &&
11
+ !customFormatters.formatYearDropdown
12
+ ) {
13
+ customFormatters.formatYearDropdown = customFormatters.formatYearCaption;
14
+ }
15
+ return {
16
+ ...defaultFormatters,
17
+ ...customFormatters
18
+ };
19
+ }
@@ -0,0 +1,79 @@
1
+ import { addMonths, isSameDay, isSameMonth, startOfMonth } from "date-fns";
2
+
3
+ import { defaultDateLib } from "../classes/DateLib";
4
+
5
+ import { getInitialMonth } from "./getInitialMonth";
6
+
7
+ it("return start of month", () => {
8
+ const month = new Date(2010, 11, 12);
9
+ const initialMonth = getInitialMonth({ month }, defaultDateLib);
10
+ expect(isSameDay(initialMonth, startOfMonth(month))).toBe(true);
11
+ });
12
+
13
+ describe("when no startMonth or endMonth is given", () => {
14
+ const month = new Date(2010, 11, 12);
15
+ const defaultMonth = new Date(2011, 11, 12);
16
+ const today = new Date(2012, 11, 12);
17
+ describe("when month is in context", () => {
18
+ it("return that month", () => {
19
+ const initialMonth = getInitialMonth(
20
+ { month, defaultMonth, today },
21
+ defaultDateLib
22
+ );
23
+ expect(isSameMonth(initialMonth, month)).toBe(true);
24
+ });
25
+ });
26
+ describe("when defaultMonth is in context and no month is given", () => {
27
+ it("return that month", () => {
28
+ const initialMonth = getInitialMonth(
29
+ { defaultMonth, today },
30
+ defaultDateLib
31
+ );
32
+ expect(isSameMonth(initialMonth, defaultMonth)).toBe(true);
33
+ });
34
+ });
35
+ describe("when no month or defaultMonth", () => {
36
+ it("return the today month", () => {
37
+ const initialMonth = getInitialMonth({ today }, defaultDateLib);
38
+ expect(isSameMonth(initialMonth, today)).toBe(true);
39
+ });
40
+ });
41
+ });
42
+
43
+ describe("when startMonth is given and is after the default initial month", () => {
44
+ it("return the startMonth", () => {
45
+ const month = new Date(2010, 11, 12);
46
+ const startMonth = addMonths(month, 1);
47
+ const initialMonth = getInitialMonth(
48
+ { month, numberOfMonths: 3, startMonth },
49
+ defaultDateLib
50
+ );
51
+ expect(isSameMonth(initialMonth, startMonth)).toBe(true);
52
+ });
53
+ });
54
+
55
+ describe("when endMonth is given", () => {
56
+ describe("when endMonth is before the default initial month", () => {
57
+ const month = new Date(2010, 11, 12);
58
+ const endMonth = addMonths(month, -2);
59
+ describe("when the number of month is 1", () => {
60
+ it("return the endMonth", () => {
61
+ const initialMonth = getInitialMonth(
62
+ { month, endMonth },
63
+ defaultDateLib
64
+ );
65
+ expect(isSameMonth(initialMonth, endMonth)).toBe(true);
66
+ });
67
+ });
68
+ describe("when the number of month is 3", () => {
69
+ it("return the endMonth plus the number of months", () => {
70
+ const initialMonth = getInitialMonth(
71
+ { month, numberOfMonths: 3, endMonth },
72
+ defaultDateLib
73
+ );
74
+ const expectedMonth = addMonths(endMonth, -1 * (3 - 1));
75
+ expect(isSameMonth(initialMonth, expectedMonth)).toBe(true);
76
+ });
77
+ });
78
+ });
79
+ });
@@ -0,0 +1,41 @@
1
+ import { type DateLib } from "../classes/DateLib.js";
2
+ import { type DayPickerProps } from "../types/props.js";
3
+
4
+ /** Return the start month based on the props passed to DayPicker. */
5
+ export function getInitialMonth(
6
+ props: Pick<
7
+ DayPickerProps,
8
+ | "fromYear"
9
+ | "toYear"
10
+ | "startMonth"
11
+ | "endMonth"
12
+ | "month"
13
+ | "defaultMonth"
14
+ | "today"
15
+ | "numberOfMonths"
16
+ | "timeZone"
17
+ >,
18
+ dateLib: DateLib
19
+ ): Date {
20
+ const {
21
+ month,
22
+ defaultMonth,
23
+ today = dateLib.today(),
24
+ numberOfMonths = 1,
25
+ endMonth,
26
+ startMonth
27
+ } = props;
28
+ let initialMonth = month || defaultMonth || today;
29
+ const { differenceInCalendarMonths, addMonths, startOfMonth } = dateLib;
30
+
31
+ // Fix the initialMonth if is after the endMonth
32
+ if (endMonth && differenceInCalendarMonths(endMonth, initialMonth) < 0) {
33
+ const offset = -1 * (numberOfMonths - 1);
34
+ initialMonth = addMonths(endMonth, offset);
35
+ }
36
+ // Fix the initialMonth if is before the startMonth
37
+ if (startMonth && differenceInCalendarMonths(initialMonth, startMonth) < 0) {
38
+ initialMonth = startMonth;
39
+ }
40
+ return startOfMonth(initialMonth);
41
+ }
@@ -0,0 +1,10 @@
1
+ import * as defaultLabels from "../labels/index.js";
2
+ import type { DayPickerProps, Labels } from "../types/index.js";
3
+
4
+ /** Return the formatters from the props merged with the default formatters. */
5
+ export function getLabels(customLabels: DayPickerProps["labels"]): Labels {
6
+ return {
7
+ ...defaultLabels,
8
+ ...customLabels
9
+ };
10
+ }
@@ -0,0 +1,226 @@
1
+ import { defaultDateLib } from "../classes/DateLib";
2
+ import * as formatters from "../formatters/index.js";
3
+
4
+ import { getMonthOptions } from "./getMonthOptions";
5
+
6
+ describe("return correct dropdown options", () => {
7
+ test("when navStart and navEnd are defined", () => {
8
+ const displayMonth = new Date(2022, 0, 1);
9
+ const startMonth = new Date(2022, 0, 1);
10
+ const endMonth = new Date(2022, 11, 31);
11
+ const result = getMonthOptions(
12
+ displayMonth,
13
+ startMonth,
14
+ endMonth,
15
+ formatters,
16
+ defaultDateLib
17
+ );
18
+
19
+ expect(result).toEqual([
20
+ { value: 0, label: "January", disabled: false },
21
+ { value: 1, label: "February", disabled: false },
22
+ { value: 2, label: "March", disabled: false },
23
+ { value: 3, label: "April", disabled: false },
24
+ { value: 4, label: "May", disabled: false },
25
+ { value: 5, label: "June", disabled: false },
26
+ { value: 6, label: "July", disabled: false },
27
+ { value: 7, label: "August", disabled: false },
28
+ { value: 8, label: "September", disabled: false },
29
+ { value: 9, label: "October", disabled: false },
30
+ { value: 10, label: "November", disabled: false },
31
+ { value: 11, label: "December", disabled: false }
32
+ ]);
33
+ });
34
+
35
+ test("when navStart and navEnd are undefined", () => {
36
+ const displayMonth = new Date(2022, 0, 1);
37
+ const result = getMonthOptions(
38
+ displayMonth,
39
+ undefined,
40
+ undefined,
41
+ formatters,
42
+ defaultDateLib
43
+ );
44
+
45
+ expect(result).toEqual([
46
+ { value: 0, label: "January", disabled: false },
47
+ { value: 1, label: "February", disabled: false },
48
+ { value: 2, label: "March", disabled: false },
49
+ { value: 3, label: "April", disabled: false },
50
+ { value: 4, label: "May", disabled: false },
51
+ { value: 5, label: "June", disabled: false },
52
+ { value: 6, label: "July", disabled: false },
53
+ { value: 7, label: "August", disabled: false },
54
+ { value: 8, label: "September", disabled: false },
55
+ { value: 9, label: "October", disabled: false },
56
+ { value: 10, label: "November", disabled: false },
57
+ { value: 11, label: "December", disabled: false }
58
+ ]);
59
+ });
60
+
61
+ test("when less than 12 months between start and end month", () => {
62
+ const displayMonth = new Date(2024, 9);
63
+ const startMonth = new Date(2024, 9);
64
+ const endMonth = new Date(2025, 5);
65
+ const result = getMonthOptions(
66
+ displayMonth,
67
+ startMonth,
68
+ endMonth,
69
+ formatters,
70
+ defaultDateLib
71
+ );
72
+
73
+ expect(result).toEqual([
74
+ { value: 0, label: "January", disabled: true },
75
+ { value: 1, label: "February", disabled: true },
76
+ { value: 2, label: "March", disabled: true },
77
+ { value: 3, label: "April", disabled: true },
78
+ { value: 4, label: "May", disabled: true },
79
+ { value: 5, label: "June", disabled: true },
80
+ { value: 6, label: "July", disabled: true },
81
+ { value: 7, label: "August", disabled: true },
82
+ { value: 8, label: "September", disabled: true },
83
+ { value: 9, label: "October", disabled: false },
84
+ { value: 10, label: "November", disabled: false },
85
+ { value: 11, label: "December", disabled: false }
86
+ ]);
87
+ });
88
+
89
+ test("return undefined when navStart is undefined", () => {
90
+ const displayMonth = new Date(2022, 0, 1);
91
+ const endMonth = new Date(2022, 6, 31);
92
+ const result = getMonthOptions(
93
+ displayMonth,
94
+ undefined,
95
+ endMonth,
96
+ formatters,
97
+ defaultDateLib
98
+ );
99
+
100
+ expect(result).toEqual([
101
+ { value: 0, label: "January", disabled: false },
102
+ { value: 1, label: "February", disabled: false },
103
+ { value: 2, label: "March", disabled: false },
104
+ { value: 3, label: "April", disabled: false },
105
+ { value: 4, label: "May", disabled: false },
106
+ { value: 5, label: "June", disabled: false },
107
+ { value: 6, label: "July", disabled: false },
108
+ { value: 7, label: "August", disabled: true },
109
+ { value: 8, label: "September", disabled: true },
110
+ { value: 9, label: "October", disabled: true },
111
+ { value: 10, label: "November", disabled: true },
112
+ { value: 11, label: "December", disabled: true }
113
+ ]);
114
+ });
115
+
116
+ test("return undefined when navEnd is undefined", () => {
117
+ const displayMonth = new Date(2022, 6, 1);
118
+ const startMonth = new Date(2022, 3, 1);
119
+ const result = getMonthOptions(
120
+ displayMonth,
121
+ startMonth,
122
+ undefined,
123
+ formatters,
124
+ defaultDateLib
125
+ );
126
+
127
+ expect(result).toEqual([
128
+ { value: 0, label: "January", disabled: true },
129
+ { value: 1, label: "February", disabled: true },
130
+ { value: 2, label: "March", disabled: true },
131
+ { value: 3, label: "April", disabled: false },
132
+ { value: 4, label: "May", disabled: false },
133
+ { value: 5, label: "June", disabled: false },
134
+ { value: 6, label: "July", disabled: false },
135
+ { value: 7, label: "August", disabled: false },
136
+ { value: 8, label: "September", disabled: false },
137
+ { value: 9, label: "October", disabled: false },
138
+ { value: 10, label: "November", disabled: false },
139
+ { value: 11, label: "December", disabled: false }
140
+ ]);
141
+ });
142
+
143
+ test("when navStart is after displayMonth", () => {
144
+ const displayMonth = new Date(2022, 0, 1);
145
+ const startMonth = new Date(2022, 2, 1);
146
+ const endMonth = new Date(2022, 11, 31);
147
+ const result = getMonthOptions(
148
+ displayMonth,
149
+ startMonth,
150
+ endMonth,
151
+ formatters,
152
+ defaultDateLib
153
+ );
154
+
155
+ expect(result).toEqual([
156
+ { value: 0, label: "January", disabled: true },
157
+ { value: 1, label: "February", disabled: true },
158
+ { value: 2, label: "March", disabled: false },
159
+ { value: 3, label: "April", disabled: false },
160
+ { value: 4, label: "May", disabled: false },
161
+ { value: 5, label: "June", disabled: false },
162
+ { value: 6, label: "July", disabled: false },
163
+ { value: 7, label: "August", disabled: false },
164
+ { value: 8, label: "September", disabled: false },
165
+ { value: 9, label: "October", disabled: false },
166
+ { value: 10, label: "November", disabled: false },
167
+ { value: 11, label: "December", disabled: false }
168
+ ]);
169
+ });
170
+
171
+ test("when navEnd is before displayMonth", () => {
172
+ const displayMonth = new Date(2022, 6, 1);
173
+ const startMonth = new Date(2022, 0, 1);
174
+ const endMonth = new Date(2022, 5, 30);
175
+ const result = getMonthOptions(
176
+ displayMonth,
177
+ startMonth,
178
+ endMonth,
179
+ formatters,
180
+ defaultDateLib
181
+ );
182
+
183
+ expect(result).toEqual([
184
+ { value: 0, label: "January", disabled: false },
185
+ { value: 1, label: "February", disabled: false },
186
+ { value: 2, label: "March", disabled: false },
187
+ { value: 3, label: "April", disabled: false },
188
+ { value: 4, label: "May", disabled: false },
189
+ { value: 5, label: "June", disabled: false },
190
+ { value: 6, label: "July", disabled: true },
191
+ { value: 7, label: "August", disabled: true },
192
+ { value: 8, label: "September", disabled: true },
193
+ { value: 9, label: "October", disabled: true },
194
+ { value: 10, label: "November", disabled: true },
195
+ { value: 11, label: "December", disabled: true }
196
+ ]);
197
+ });
198
+
199
+ test("when navStart and navEnd are within the same year", () => {
200
+ const displayMonth = new Date(2022, 0, 1);
201
+ const startMonth = new Date(2022, 2, 1);
202
+ const endMonth = new Date(2022, 8, 30);
203
+ const result = getMonthOptions(
204
+ displayMonth,
205
+ startMonth,
206
+ endMonth,
207
+ formatters,
208
+ defaultDateLib
209
+ );
210
+
211
+ expect(result).toEqual([
212
+ { value: 0, label: "January", disabled: true },
213
+ { value: 1, label: "February", disabled: true },
214
+ { value: 2, label: "March", disabled: false },
215
+ { value: 3, label: "April", disabled: false },
216
+ { value: 4, label: "May", disabled: false },
217
+ { value: 5, label: "June", disabled: false },
218
+ { value: 6, label: "July", disabled: false },
219
+ { value: 7, label: "August", disabled: false },
220
+ { value: 8, label: "September", disabled: false },
221
+ { value: 9, label: "October", disabled: true },
222
+ { value: 10, label: "November", disabled: true },
223
+ { value: 11, label: "December", disabled: true }
224
+ ]);
225
+ });
226
+ });