react-day-picker 9.6.2 → 9.6.4

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 (181) hide show
  1. package/dist/cjs/DayPicker.js +15 -3
  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/cjs/useAnimation.js +74 -42
  6. package/dist/cjs/useAnimation.js.map +1 -1
  7. package/dist/esm/DayPicker.js +15 -3
  8. package/dist/esm/DayPicker.js.map +1 -1
  9. package/dist/esm/classes/DateLib.js +1 -1
  10. package/dist/esm/classes/DateLib.js.map +1 -1
  11. package/dist/esm/useAnimation.js +74 -42
  12. package/dist/esm/useAnimation.js.map +1 -1
  13. package/package.json +2 -4
  14. package/src/.eslintignore +1 -0
  15. package/src/.eslintrc.cjs +27 -0
  16. package/src/DayPicker.test.tsx +199 -0
  17. package/src/DayPicker.tsx +630 -0
  18. package/src/UI.ts +365 -0
  19. package/src/classes/CalendarDay.test.ts +17 -0
  20. package/src/classes/CalendarDay.ts +61 -0
  21. package/src/classes/CalendarMonth.test.ts +28 -0
  22. package/src/classes/CalendarMonth.ts +15 -0
  23. package/src/classes/CalendarWeek.test.ts +21 -0
  24. package/src/classes/CalendarWeek.ts +13 -0
  25. package/src/classes/DateLib.ts +615 -0
  26. package/src/classes/index.ts +4 -0
  27. package/src/components/Button.tsx +13 -0
  28. package/src/components/CaptionLabel.tsx +13 -0
  29. package/src/components/Chevron.tsx +42 -0
  30. package/src/components/Day.tsx +28 -0
  31. package/src/components/DayButton.tsx +29 -0
  32. package/src/components/Dropdown.tsx +71 -0
  33. package/src/components/DropdownNav.tsx +13 -0
  34. package/src/components/Footer.tsx +13 -0
  35. package/src/components/Month.tsx +24 -0
  36. package/src/components/MonthCaption.tsx +23 -0
  37. package/src/components/MonthGrid.tsx +13 -0
  38. package/src/components/Months.tsx +13 -0
  39. package/src/components/MonthsDropdown.tsx +16 -0
  40. package/src/components/Nav.tsx +90 -0
  41. package/src/components/NextMonthButton.tsx +18 -0
  42. package/src/components/Option.tsx +13 -0
  43. package/src/components/PreviousMonthButton.tsx +20 -0
  44. package/src/components/Root.tsx +19 -0
  45. package/src/components/Select.tsx +13 -0
  46. package/src/components/Week.tsx +20 -0
  47. package/src/components/WeekNumber.tsx +21 -0
  48. package/src/components/WeekNumberHeader.tsx +15 -0
  49. package/src/components/Weekday.tsx +13 -0
  50. package/src/components/Weekdays.tsx +17 -0
  51. package/src/components/Weeks.tsx +13 -0
  52. package/src/components/YearsDropdown.tsx +16 -0
  53. package/src/components/custom-components.tsx +26 -0
  54. package/src/formatters/formatCaption.test.ts +27 -0
  55. package/src/formatters/formatCaption.ts +23 -0
  56. package/src/formatters/formatDay.test.ts +7 -0
  57. package/src/formatters/formatDay.ts +16 -0
  58. package/src/formatters/formatMonthDropdown.test.ts +19 -0
  59. package/src/formatters/formatMonthDropdown.ts +15 -0
  60. package/src/formatters/formatWeekNumber.test.ts +5 -0
  61. package/src/formatters/formatWeekNumber.ts +13 -0
  62. package/src/formatters/formatWeekNumberHeader.ts +10 -0
  63. package/src/formatters/formatWeekdayName.test.ts +15 -0
  64. package/src/formatters/formatWeekdayName.ts +16 -0
  65. package/src/formatters/formatYearDropdown.test.ts +7 -0
  66. package/src/formatters/formatYearDropdown.ts +21 -0
  67. package/src/formatters/index.ts +7 -0
  68. package/src/helpers/broadcastCalendar.test.ts +43 -0
  69. package/src/helpers/calculateFocusTarget.ts +51 -0
  70. package/src/helpers/endOfBroadcastWeek.test.ts +25 -0
  71. package/src/helpers/endOfBroadcastWeek.ts +16 -0
  72. package/src/helpers/getBroadcastWeeksInMonth.test.ts +23 -0
  73. package/src/helpers/getBroadcastWeeksInMonth.ts +31 -0
  74. package/src/helpers/getClassNamesForModifiers.ts +26 -0
  75. package/src/helpers/getComponents.ts +11 -0
  76. package/src/helpers/getDataAttributes.test.tsx +48 -0
  77. package/src/helpers/getDataAttributes.tsx +21 -0
  78. package/src/helpers/getDates.test.ts +190 -0
  79. package/src/helpers/getDates.ts +64 -0
  80. package/src/helpers/getDays.test.ts +30 -0
  81. package/src/helpers/getDays.ts +16 -0
  82. package/src/helpers/getDefaultClassNames.test.ts +47 -0
  83. package/src/helpers/getDefaultClassNames.ts +33 -0
  84. package/src/helpers/getDisplayMonths.test.ts +44 -0
  85. package/src/helpers/getDisplayMonths.ts +20 -0
  86. package/src/helpers/getFocusableDate.ts +59 -0
  87. package/src/helpers/getFormatters.test.ts +48 -0
  88. package/src/helpers/getFormatters.ts +19 -0
  89. package/src/helpers/getInitialMonth.test.ts +79 -0
  90. package/src/helpers/getInitialMonth.ts +41 -0
  91. package/src/helpers/getLabels.ts +10 -0
  92. package/src/helpers/getMonthOptions.test.ts +226 -0
  93. package/src/helpers/getMonthOptions.ts +37 -0
  94. package/src/helpers/getMonths.test.ts +88 -0
  95. package/src/helpers/getMonths.ts +90 -0
  96. package/src/helpers/getNavMonth.test.ts +253 -0
  97. package/src/helpers/getNavMonth.ts +70 -0
  98. package/src/helpers/getNextFocus.test.tsx +99 -0
  99. package/src/helpers/getNextFocus.tsx +67 -0
  100. package/src/helpers/getNextMonth.test.ts +101 -0
  101. package/src/helpers/getNextMonth.ts +45 -0
  102. package/src/helpers/getPossibleFocusDate.test.ts +144 -0
  103. package/src/helpers/getPreviousMonth.test.ts +77 -0
  104. package/src/helpers/getPreviousMonth.ts +40 -0
  105. package/src/helpers/getStyleForModifiers.test.ts +92 -0
  106. package/src/helpers/getStyleForModifiers.ts +21 -0
  107. package/src/helpers/getWeekdays.test.ts +44 -0
  108. package/src/helpers/getWeekdays.ts +29 -0
  109. package/src/helpers/getWeeks.test.ts +30 -0
  110. package/src/helpers/getWeeks.ts +9 -0
  111. package/src/helpers/getYearOptions.test.ts +46 -0
  112. package/src/helpers/getYearOptions.ts +34 -0
  113. package/src/helpers/index.ts +2 -0
  114. package/src/helpers/startOfBroadcastWeek.test.ts +24 -0
  115. package/src/helpers/startOfBroadcastWeek.ts +19 -0
  116. package/src/helpers/useControlledValue.test.ts +45 -0
  117. package/src/helpers/useControlledValue.ts +33 -0
  118. package/src/index.ts +15 -0
  119. package/src/jalali.tsx +2 -0
  120. package/src/labels/index.ts +12 -0
  121. package/src/labels/labelDayButton.test.ts +41 -0
  122. package/src/labels/labelDayButton.ts +31 -0
  123. package/src/labels/labelGrid.test.ts +7 -0
  124. package/src/labels/labelGrid.ts +23 -0
  125. package/src/labels/labelGridcell.test.ts +7 -0
  126. package/src/labels/labelGridcell.ts +22 -0
  127. package/src/labels/labelMonthDropdown.test.ts +5 -0
  128. package/src/labels/labelMonthDropdown.ts +12 -0
  129. package/src/labels/labelNav.test.ts +5 -0
  130. package/src/labels/labelNav.ts +10 -0
  131. package/src/labels/labelNext.test.ts +5 -0
  132. package/src/labels/labelNext.ts +13 -0
  133. package/src/labels/labelPrevious.test.ts +5 -0
  134. package/src/labels/labelPrevious.ts +13 -0
  135. package/src/labels/labelWeekNumber.test.ts +5 -0
  136. package/src/labels/labelWeekNumber.ts +15 -0
  137. package/src/labels/labelWeekNumberHeader.test.ts +5 -0
  138. package/src/labels/labelWeekNumberHeader.ts +12 -0
  139. package/src/labels/labelWeekday.test.ts +15 -0
  140. package/src/labels/labelWeekday.ts +16 -0
  141. package/src/labels/labelYearDropdown.test.ts +5 -0
  142. package/src/labels/labelYearDropdown.ts +12 -0
  143. package/src/locale.ts +1 -0
  144. package/src/persian.tsx +86 -0
  145. package/src/selection/useMulti.test.tsx +41 -0
  146. package/src/selection/useMulti.tsx +74 -0
  147. package/src/selection/useRange.test.tsx +154 -0
  148. package/src/selection/useRange.tsx +73 -0
  149. package/src/selection/useSingle.test.tsx +38 -0
  150. package/src/selection/useSingle.tsx +69 -0
  151. package/src/types/deprecated.ts +230 -0
  152. package/src/types/index.ts +4 -0
  153. package/src/types/props.test.tsx +71 -0
  154. package/src/types/props.ts +675 -0
  155. package/src/types/selection.ts +57 -0
  156. package/src/types/shared.ts +442 -0
  157. package/src/useAnimation.test.tsx +190 -0
  158. package/src/useAnimation.ts +236 -0
  159. package/src/useCalendar.ts +178 -0
  160. package/src/useDayPicker.test.tsx +142 -0
  161. package/src/useDayPicker.ts +93 -0
  162. package/src/useFocus.ts +87 -0
  163. package/src/useGetModifiers.test.tsx +154 -0
  164. package/src/useGetModifiers.tsx +122 -0
  165. package/src/useSelection.ts +26 -0
  166. package/src/utc.tsx +10 -0
  167. package/src/utils/addToRange.test.ts +117 -0
  168. package/src/utils/addToRange.ts +87 -0
  169. package/src/utils/dateMatchModifiers.test.ts +120 -0
  170. package/src/utils/dateMatchModifiers.ts +88 -0
  171. package/src/utils/index.ts +7 -0
  172. package/src/utils/rangeContainsDayOfWeek.test.ts +48 -0
  173. package/src/utils/rangeContainsDayOfWeek.ts +35 -0
  174. package/src/utils/rangeContainsModifiers.test.ts +230 -0
  175. package/src/utils/rangeContainsModifiers.ts +125 -0
  176. package/src/utils/rangeIncludesDate.test.ts +46 -0
  177. package/src/utils/rangeIncludesDate.ts +43 -0
  178. package/src/utils/rangeOverlaps.test.ts +60 -0
  179. package/src/utils/rangeOverlaps.ts +22 -0
  180. package/src/utils/typeguards.test.ts +83 -0
  181. package/src/utils/typeguards.ts +70 -0
@@ -0,0 +1,101 @@
1
+ import { addMonths, isSameMonth } from "date-fns";
2
+
3
+ import { defaultDateLib } from "../classes/DateLib";
4
+
5
+ import { getNextMonth } from "./getNextMonth";
6
+
7
+ const startingMonth = new Date(2020, 4, 31);
8
+
9
+ describe("when number of months is 1", () => {
10
+ describe("when the navigation is disabled", () => {
11
+ it("the next month is undefined", () => {
12
+ const result = getNextMonth(
13
+ startingMonth,
14
+ undefined,
15
+ {
16
+ disableNavigation: true
17
+ },
18
+ defaultDateLib
19
+ );
20
+ expect(result).toBe(undefined);
21
+ });
22
+ });
23
+ describe("when in the navigable range", () => {
24
+ const endMonth = addMonths(startingMonth, 3);
25
+ it("the next month is not undefined", () => {
26
+ const result = getNextMonth(startingMonth, endMonth, {}, defaultDateLib);
27
+ const expectedNextMonth = addMonths(startingMonth, 1);
28
+ expect(result && isSameMonth(result, expectedNextMonth)).toBeTruthy();
29
+ });
30
+ });
31
+ describe("when not in the navigable range", () => {
32
+ const endMonth = startingMonth;
33
+ it("the next month is undefined", () => {
34
+ const result = getNextMonth(startingMonth, endMonth, {}, defaultDateLib);
35
+ expect(result).toBe(undefined);
36
+ });
37
+ });
38
+ });
39
+ describe("when displaying 3 months", () => {
40
+ const numberOfMonths = 3;
41
+ describe("when the navigation is paged", () => {
42
+ const pagedNavigation = true;
43
+ it("the next month is 3 months ahead", () => {
44
+ const result = getNextMonth(
45
+ startingMonth,
46
+ undefined,
47
+ {
48
+ numberOfMonths,
49
+ pagedNavigation
50
+ },
51
+ defaultDateLib
52
+ );
53
+ const expectedNextMonth = addMonths(startingMonth, 3);
54
+ expect(result && isSameMonth(result, expectedNextMonth)).toBeTruthy();
55
+ });
56
+ describe("when the to-date is ahead less than 3 months", () => {
57
+ it("the next month is undefined", () => {
58
+ const result = getNextMonth(
59
+ startingMonth,
60
+ addMonths(startingMonth, 1),
61
+ {
62
+ numberOfMonths,
63
+ pagedNavigation
64
+ },
65
+ defaultDateLib
66
+ );
67
+ expect(result).toBe(undefined);
68
+ });
69
+ });
70
+ });
71
+ describe("when the navigation is not paged", () => {
72
+ const pagedNavigation = false;
73
+ it("the next month is 1 months ahead", () => {
74
+ const result = getNextMonth(
75
+ startingMonth,
76
+ undefined,
77
+ {
78
+ numberOfMonths,
79
+ pagedNavigation
80
+ },
81
+ defaultDateLib
82
+ );
83
+ const expectedNextMonth = addMonths(startingMonth, 1);
84
+ expect(result && isSameMonth(result, expectedNextMonth)).toBeTruthy();
85
+ });
86
+ describe("when the to-date is ahead less than 3 months", () => {
87
+ it("the next month is undefined", () => {
88
+ const result = getNextMonth(
89
+ startingMonth,
90
+ addMonths(startingMonth, 2),
91
+ {
92
+ numberOfMonths,
93
+ pagedNavigation
94
+ },
95
+ defaultDateLib
96
+ );
97
+ expect(result).toBe(undefined);
98
+ });
99
+ });
100
+ });
101
+ });
@@ -0,0 +1,45 @@
1
+ import type { DateLib } from "../classes/DateLib.js";
2
+ import type { DayPickerProps } from "../types/index.js";
3
+
4
+ /**
5
+ * Return the next month the user can navigate to according to the given
6
+ * options.
7
+ *
8
+ * Please note that the next month is not always the next calendar month:
9
+ *
10
+ * - If after the `calendarEndMonth` range, is `undefined`;
11
+ * - If the navigation is paged , is the number of months displayed ahead.
12
+ */
13
+ export function getNextMonth(
14
+ firstDisplayedMonth: Date,
15
+ calendarEndMonth: Date | undefined,
16
+ options: Pick<
17
+ DayPickerProps,
18
+ "numberOfMonths" | "pagedNavigation" | "disableNavigation"
19
+ >,
20
+ dateLib: DateLib
21
+ ): Date | undefined {
22
+ if (options.disableNavigation) {
23
+ return undefined;
24
+ }
25
+ const { pagedNavigation, numberOfMonths = 1 } = options;
26
+ const { startOfMonth, addMonths, differenceInCalendarMonths } = dateLib;
27
+ const offset = pagedNavigation ? numberOfMonths : 1;
28
+ const month = startOfMonth(firstDisplayedMonth);
29
+
30
+ if (!calendarEndMonth) {
31
+ return addMonths(month, offset);
32
+ }
33
+
34
+ const monthsDiff = differenceInCalendarMonths(
35
+ calendarEndMonth,
36
+ firstDisplayedMonth
37
+ );
38
+
39
+ if (monthsDiff < numberOfMonths) {
40
+ return undefined;
41
+ }
42
+
43
+ // Jump forward as the number of months when paged navigation
44
+ return addMonths(month, offset);
45
+ }
@@ -0,0 +1,144 @@
1
+ import type { Locale } from "date-fns";
2
+ // Adjust the import path
3
+ import {
4
+ addDays,
5
+ addWeeks,
6
+ addMonths,
7
+ addYears,
8
+ startOfISOWeek,
9
+ endOfISOWeek,
10
+ startOfWeek,
11
+ endOfWeek
12
+ } from "date-fns";
13
+
14
+ import { DateLib } from "../classes/DateLib";
15
+ import type { DayPickerProps, MoveFocusBy, MoveFocusDir } from "../types";
16
+
17
+ import { getFocusableDate } from "./getFocusableDate";
18
+
19
+ const focusedDate = new Date(2023, 0, 1); // Jan 1, 2023
20
+ const options: Pick<DayPickerProps, "ISOWeek"> = {
21
+ ISOWeek: false
22
+ };
23
+ const dateLib = new DateLib({
24
+ weekStartsOn: 0 // Sunday
25
+ });
26
+
27
+ const calendarStartMonth = new Date(2022, 0, 1); // Jan 1, 2022
28
+ const calendarEndMonth = new Date(2024, 0, 1); // Jan 1, 2024
29
+
30
+ const testCases: {
31
+ moveBy: MoveFocusBy;
32
+ moveDir: MoveFocusDir;
33
+ expectedFn: (date: Date | number, amount: number) => Date;
34
+ }[] = [
35
+ { moveBy: "day", moveDir: "after", expectedFn: addDays },
36
+ { moveBy: "day", moveDir: "before", expectedFn: addDays },
37
+ { moveBy: "month", moveDir: "after", expectedFn: addMonths },
38
+ { moveBy: "month", moveDir: "before", expectedFn: addMonths },
39
+ { moveBy: "week", moveDir: "after", expectedFn: addWeeks },
40
+ { moveBy: "week", moveDir: "before", expectedFn: addWeeks },
41
+ { moveBy: "year", moveDir: "after", expectedFn: addYears },
42
+ { moveBy: "year", moveDir: "before", expectedFn: addYears }
43
+ ];
44
+
45
+ testCases.forEach(({ moveBy, moveDir, expectedFn }) => {
46
+ test(`should move ${moveDir} by ${moveBy}`, () => {
47
+ const expectedDate = expectedFn(focusedDate, moveDir === "after" ? 1 : -1);
48
+ const result = getFocusableDate(
49
+ moveBy,
50
+ moveDir,
51
+ focusedDate,
52
+ calendarStartMonth,
53
+ calendarEndMonth,
54
+ options,
55
+ dateLib
56
+ );
57
+ expect(result).toEqual(expectedDate);
58
+ });
59
+ });
60
+
61
+ const weekTestCases: {
62
+ moveBy: MoveFocusBy;
63
+ moveDir: MoveFocusDir;
64
+ expectedFn: (
65
+ date: number | Date,
66
+ options?:
67
+ | {
68
+ locale?: Locale | undefined;
69
+ weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined;
70
+ }
71
+ | undefined
72
+ ) => Date;
73
+ }[] = [
74
+ { moveBy: "endOfWeek", moveDir: "after", expectedFn: endOfWeek },
75
+ { moveBy: "startOfWeek", moveDir: "after", expectedFn: startOfWeek }
76
+ ];
77
+
78
+ weekTestCases.forEach(({ moveBy, moveDir, expectedFn }) => {
79
+ test(`should move ${moveDir} by ${moveBy}`, () => {
80
+ const expectedDate = expectedFn(focusedDate);
81
+ const result = getFocusableDate(
82
+ moveBy,
83
+ moveDir,
84
+ focusedDate,
85
+ calendarStartMonth,
86
+ calendarEndMonth,
87
+ options,
88
+ dateLib
89
+ );
90
+
91
+ expect(result).toEqual(expectedDate);
92
+ });
93
+ });
94
+
95
+ const ISOWeekTestCases: {
96
+ moveBy: MoveFocusBy;
97
+ moveDir: MoveFocusDir;
98
+ expectedFn: (date: Date | number) => Date;
99
+ }[] = [
100
+ { moveBy: "endOfWeek", moveDir: "after", expectedFn: endOfISOWeek },
101
+ { moveBy: "startOfWeek", moveDir: "after", expectedFn: startOfISOWeek }
102
+ ];
103
+
104
+ ISOWeekTestCases.forEach(({ moveBy, moveDir, expectedFn }) => {
105
+ test(`should move ${moveDir} by ${moveBy} when ISOWeek is true`, () => {
106
+ const expectedDate = expectedFn(focusedDate);
107
+ const result = getFocusableDate(
108
+ moveBy,
109
+ moveDir,
110
+ focusedDate,
111
+ calendarStartMonth,
112
+ calendarEndMonth,
113
+ { ...options, ISOWeek: true },
114
+ dateLib
115
+ );
116
+ expect(result).toEqual(expectedDate);
117
+ });
118
+ });
119
+
120
+ test("should not move before startMonth", () => {
121
+ const result = getFocusableDate(
122
+ "day",
123
+ "before",
124
+ new Date(2022, 0, 2),
125
+ calendarStartMonth,
126
+ calendarEndMonth,
127
+ options,
128
+ dateLib
129
+ );
130
+ expect(result).toEqual(calendarStartMonth);
131
+ });
132
+
133
+ test("should not move after endMonth", () => {
134
+ const result = getFocusableDate(
135
+ "day",
136
+ "after",
137
+ new Date(2023, 11, 31),
138
+ calendarStartMonth,
139
+ calendarEndMonth,
140
+ options,
141
+ dateLib
142
+ );
143
+ expect(result).toEqual(calendarEndMonth);
144
+ });
@@ -0,0 +1,77 @@
1
+ import { defaultDateLib } from "../classes/DateLib";
2
+
3
+ import { getPreviousMonth } from "./getPreviousMonth";
4
+
5
+ it("should return undefined if navigation is disabled", () => {
6
+ const firstDisplayedMonth = new Date(2022, 0, 1); // January 2022
7
+ const calendarStartMonth = new Date(2022, 0, 1); // January 2022
8
+ const props = {
9
+ disableNavigation: true,
10
+ pagedNavigation: false,
11
+ numberOfMonths: 1
12
+ };
13
+
14
+ const result = getPreviousMonth(
15
+ firstDisplayedMonth,
16
+ calendarStartMonth,
17
+ props,
18
+ defaultDateLib
19
+ );
20
+
21
+ expect(result).toBeUndefined();
22
+ });
23
+
24
+ it("should return the previous month if startMonth is not provided", () => {
25
+ const firstDisplayedMonth = new Date(2022, 1, 1); // February 2022
26
+ const props = {
27
+ disableNavigation: false,
28
+ pagedNavigation: false,
29
+ numberOfMonths: 1
30
+ };
31
+
32
+ const result = getPreviousMonth(
33
+ firstDisplayedMonth,
34
+ undefined,
35
+ props,
36
+ defaultDateLib
37
+ );
38
+
39
+ expect(result).toEqual(new Date(2022, 0, 1)); // January 2022
40
+ });
41
+
42
+ it("should return undefined if the previous month is before the startMonth", () => {
43
+ const firstDisplayedMonth = new Date(2022, 0, 1); // January 2022
44
+ const calendarStartMonth = new Date(2022, 0, 1); // January 2022
45
+ const props = {
46
+ disableNavigation: false,
47
+ pagedNavigation: false,
48
+ numberOfMonths: 1
49
+ };
50
+ const result = getPreviousMonth(
51
+ firstDisplayedMonth,
52
+ calendarStartMonth,
53
+ props,
54
+ defaultDateLib
55
+ );
56
+ expect(result).toBeUndefined();
57
+ });
58
+
59
+ it("should return the correct previous month when pagedNavigation is true", () => {
60
+ const firstDisplayedMonth = new Date(2022, 2, 1); // March 2022
61
+ const calendarStartMonth = new Date(2022, 0, 1); // January 2022
62
+ const props = {
63
+ disableNavigation: false,
64
+ pagedNavigation: true,
65
+ numberOfMonths: 2,
66
+ startMonth: new Date(2022, 0, 1)
67
+ };
68
+
69
+ const result = getPreviousMonth(
70
+ firstDisplayedMonth,
71
+ calendarStartMonth,
72
+ props,
73
+ defaultDateLib
74
+ );
75
+
76
+ expect(result).toEqual(new Date(2022, 0, 1)); // January 2022
77
+ });
@@ -0,0 +1,40 @@
1
+ import type { DateLib } from "../classes/DateLib.js";
2
+ import type { DayPickerProps } from "../types/index.js";
3
+
4
+ /**
5
+ * Return the next previous the user can navigate to, according to the given
6
+ * options.
7
+ *
8
+ * Please note that the previous month is not always the previous calendar
9
+ * month:
10
+ *
11
+ * - If before the `calendarStartMonth` date, is `undefined`;
12
+ * - If the navigation is paged, is the number of months displayed before.
13
+ */
14
+ export function getPreviousMonth(
15
+ firstDisplayedMonth: Date,
16
+ calendarStartMonth: Date | undefined,
17
+ options: Pick<
18
+ DayPickerProps,
19
+ "numberOfMonths" | "pagedNavigation" | "disableNavigation"
20
+ >,
21
+ dateLib: DateLib
22
+ ): Date | undefined {
23
+ if (options.disableNavigation) {
24
+ return undefined;
25
+ }
26
+ const { pagedNavigation, numberOfMonths } = options;
27
+ const { startOfMonth, addMonths, differenceInCalendarMonths } = dateLib;
28
+ const offset = pagedNavigation ? (numberOfMonths ?? 1) : 1;
29
+ const month = startOfMonth(firstDisplayedMonth);
30
+ if (!calendarStartMonth) {
31
+ return addMonths(month, -offset);
32
+ }
33
+ const monthsDiff = differenceInCalendarMonths(month, calendarStartMonth);
34
+
35
+ if (monthsDiff <= 0) {
36
+ return undefined;
37
+ }
38
+
39
+ return addMonths(month, -offset);
40
+ }
@@ -0,0 +1,92 @@
1
+ import type { CSSProperties } from "react";
2
+
3
+ // Update the path as needed
4
+ import type { Modifiers, ModifiersStyles } from "../types";
5
+
6
+ import { getStyleForModifiers } from "./getStyleForModifiers";
7
+
8
+ const defaultModifiers: Modifiers = {
9
+ disabled: false,
10
+ hidden: false,
11
+ outside: false,
12
+ range_end: false,
13
+ range_middle: false,
14
+ range_start: false,
15
+ selected: false,
16
+ focusable: false,
17
+ focused: false,
18
+ today: false
19
+ };
20
+
21
+ test("applies modifier styles to the base style", () => {
22
+ const dayModifiers: Modifiers = {
23
+ ...defaultModifiers,
24
+ selected: true,
25
+ disabled: false
26
+ };
27
+ const modifiersStyles: Partial<ModifiersStyles> = {
28
+ selected: { backgroundColor: "blue", color: "white" }
29
+ };
30
+ const expectedStyle: CSSProperties = {
31
+ ...modifiersStyles.selected
32
+ };
33
+
34
+ const style = getStyleForModifiers(dayModifiers, {}, modifiersStyles);
35
+
36
+ expect(style).toEqual(expectedStyle);
37
+ });
38
+
39
+ test("ignores modifiers that are not active", () => {
40
+ const dayModifiers: Modifiers = {
41
+ ...defaultModifiers,
42
+ selected: false,
43
+ disabled: true
44
+ };
45
+ const modifiersStyles: Partial<ModifiersStyles> = {
46
+ disabled: { opacity: 0.5 }
47
+ };
48
+
49
+ const style = getStyleForModifiers(dayModifiers, {}, modifiersStyles);
50
+
51
+ expect(style).toEqual({ opacity: 0.5 }); // should not have applied the disabled style
52
+ });
53
+
54
+ test("combines multiple active modifier styles", () => {
55
+ const dayModifiers: Modifiers = {
56
+ ...defaultModifiers,
57
+ selected: true,
58
+ highlighted: true
59
+ };
60
+ const modifiersStyles: Partial<ModifiersStyles> = {
61
+ selected: { backgroundColor: "blue" },
62
+ highlighted: { borderColor: "yellow" }
63
+ };
64
+ const expectedStyle: CSSProperties = {
65
+ ...modifiersStyles.selected,
66
+ ...modifiersStyles.highlighted
67
+ };
68
+
69
+ const style = getStyleForModifiers(dayModifiers, {}, modifiersStyles);
70
+
71
+ expect(style).toEqual(expectedStyle);
72
+ });
73
+
74
+ test("applies the most recent modifier style when there are conflicts", () => {
75
+ const dayModifiers: Modifiers = {
76
+ ...defaultModifiers,
77
+ selected: true,
78
+ highlighted: true
79
+ };
80
+ const modifiersStyles: Partial<ModifiersStyles> = {
81
+ selected: { backgroundColor: "blue", color: "red" }, // 'color' should be overridden.
82
+ highlighted: { backgroundColor: "yellow", color: "green" }
83
+ };
84
+ const expectedStyle: CSSProperties = {
85
+ backgroundColor: "yellow", // from 'highlighted'
86
+ color: "green" // from 'highlighted', overriding 'selected'
87
+ };
88
+
89
+ const style = getStyleForModifiers(dayModifiers, {}, modifiersStyles);
90
+
91
+ expect(style).toEqual(expectedStyle);
92
+ });
@@ -0,0 +1,21 @@
1
+ import type { CSSProperties } from "react";
2
+
3
+ import { UI } from "../UI.js";
4
+ import type { Modifiers, ModifiersStyles, Styles } from "../types/index.js";
5
+
6
+ export function getStyleForModifiers(
7
+ dayModifiers: Modifiers,
8
+ styles: Partial<Styles> = {},
9
+ modifiersStyles: Partial<ModifiersStyles> = {}
10
+ ) {
11
+ let style: CSSProperties = { ...styles?.[UI.Day] };
12
+ Object.entries(dayModifiers)
13
+ .filter(([, active]) => active === true)
14
+ .forEach(([modifier]) => {
15
+ style = {
16
+ ...style,
17
+ ...modifiersStyles?.[modifier]
18
+ };
19
+ });
20
+ return style;
21
+ }
@@ -0,0 +1,44 @@
1
+ import { es } from "date-fns/locale/es";
2
+
3
+ import { DateLib, defaultDateLib } from "../classes/DateLib";
4
+
5
+ import { getWeekdays } from "./getWeekdays";
6
+
7
+ let result: Date[];
8
+
9
+ describe("when rendered without a locale", () => {
10
+ beforeEach(() => {
11
+ result = getWeekdays(defaultDateLib);
12
+ });
13
+ test("should return 7 days", () => {
14
+ expect(result).toHaveLength(7);
15
+ });
16
+ test("should return Sunday as first day", () => {
17
+ expect(result[0]).toBeSunday();
18
+ });
19
+ });
20
+
21
+ describe.each<0 | 1 | 2 | 3 | 4 | 5 | 6>([0, 1, 2, 3, 4, 5, 6])(
22
+ "when week start on %s",
23
+ (weekStartsOn) => {
24
+ beforeEach(() => {
25
+ result = getWeekdays(new DateLib({ locale: es, weekStartsOn }));
26
+ });
27
+ test("the first date should be weekStartsOn", () => {
28
+ expect(result[0].getDay()).toBe(weekStartsOn);
29
+ });
30
+ }
31
+ );
32
+
33
+ describe("when using ISO week", () => {
34
+ beforeEach(() => {
35
+ result = getWeekdays(
36
+ new DateLib({ locale: es, weekStartsOn: 3 }),
37
+ true,
38
+ undefined
39
+ );
40
+ });
41
+ test("should return Monday as first day", () => {
42
+ expect(result[0]).toBeMonday();
43
+ });
44
+ });
@@ -0,0 +1,29 @@
1
+ import { DateLib } from "../classes/DateLib.js";
2
+
3
+ /**
4
+ * Generate a series of 7 days, starting from the week, to use for formatting
5
+ * the weekday names (Monday, Tuesday, etc.).
6
+ */
7
+ export function getWeekdays(
8
+ /** The date library. */
9
+ dateLib: DateLib,
10
+ /** Use ISOWeek instead of locale/ */
11
+ ISOWeek?: boolean | undefined,
12
+ /** @since 9.4.0 */
13
+ broadcastCalendar?: boolean | undefined
14
+ ): Date[] {
15
+ const today = dateLib.today();
16
+
17
+ const start = broadcastCalendar
18
+ ? dateLib.startOfBroadcastWeek(today, dateLib)
19
+ : ISOWeek
20
+ ? dateLib.startOfISOWeek(today)
21
+ : dateLib.startOfWeek(today);
22
+
23
+ const days: Date[] = [];
24
+ for (let i = 0; i < 7; i++) {
25
+ const day = dateLib.addDays(start, i);
26
+ days.push(day);
27
+ }
28
+ return days;
29
+ }
@@ -0,0 +1,30 @@
1
+ import { CalendarDay, CalendarMonth, CalendarWeek } from "../classes";
2
+
3
+ import { getWeeks } from "./getWeeks";
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(getWeeks(months)).toEqual([...weeks1, ...weeks2]);
30
+ });
@@ -0,0 +1,9 @@
1
+ import type { CalendarMonth, CalendarWeek } from "../classes/index.js";
2
+
3
+ /** Returns an array of calendar weeks from an array of calendar months. */
4
+ export function getWeeks(months: CalendarMonth[]) {
5
+ const initialWeeks: CalendarWeek[] = [];
6
+ return months.reduce((weeks, month) => {
7
+ return [...weeks, ...month.weeks];
8
+ }, initialWeeks);
9
+ }
@@ -0,0 +1,46 @@
1
+ import { defaultDateLib } from "../classes/DateLib";
2
+
3
+ import { getFormatters } from "./getFormatters";
4
+ import { getYearOptions } from "./getYearOptions";
5
+
6
+ test("return undefined if navStart or navEnd are not set", () => {
7
+ const formatters = getFormatters({
8
+ formatYearDropdown: (date: Date) => `${date.getFullYear()}`
9
+ });
10
+ const result1 = getYearOptions(
11
+ undefined,
12
+ new Date(2022, 11, 31),
13
+ formatters,
14
+ defaultDateLib
15
+ );
16
+ const result2 = getYearOptions(
17
+ new Date(2022, 0, 1),
18
+ undefined,
19
+ formatters,
20
+ defaultDateLib
21
+ );
22
+
23
+ expect(result1).toBeUndefined();
24
+ expect(result2).toBeUndefined();
25
+ });
26
+
27
+ test("return correct dropdown options", () => {
28
+ const startMonth = new Date(2022, 0, 1); // January 2022
29
+ const endMonth = new Date(2024, 11, 31); // December 2024
30
+ const formatters = getFormatters({
31
+ formatYearDropdown: (date: Date) => `${date.getFullYear()}`
32
+ });
33
+
34
+ const result = getYearOptions(
35
+ startMonth,
36
+ endMonth,
37
+ formatters,
38
+ defaultDateLib
39
+ );
40
+
41
+ expect(result).toEqual([
42
+ { value: 2022, label: "2022", disabled: false },
43
+ { value: 2023, label: "2023", disabled: false },
44
+ { value: 2024, label: "2024", disabled: false }
45
+ ]);
46
+ });