react-day-picker 8.0.1 → 8.0.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 (218) hide show
  1. package/README.md +3 -2
  2. package/dist/components/Caption/Caption.d.ts +2 -2
  3. package/dist/components/CaptionDropdowns/CaptionDropdowns.d.ts +5 -0
  4. package/dist/components/CaptionDropdowns/index.d.ts +1 -0
  5. package/dist/components/CaptionNavigation/CaptionNavigation.d.ts +5 -0
  6. package/dist/components/CaptionNavigation/index.d.ts +1 -0
  7. package/dist/components/Head/utils/getWeekdays.d.ts +4 -2
  8. package/dist/components/Table/utils/daysToMonthWeeks.d.ts +1 -1
  9. package/dist/components/Table/utils/getMonthWeeks.d.ts +1 -1
  10. package/dist/contexts/DayPicker/formatters/formatCaption.d.ts +1 -1
  11. package/dist/contexts/DayPicker/formatters/formatDay.d.ts +1 -1
  12. package/dist/contexts/DayPicker/formatters/formatMonthCaption.d.ts +1 -1
  13. package/dist/contexts/DayPicker/formatters/formatWeekdayName.d.ts +1 -1
  14. package/dist/index.d.ts +2 -0
  15. package/dist/index.esm.js +145 -96
  16. package/dist/index.esm.js.map +1 -1
  17. package/dist/index.js +272 -196
  18. package/dist/index.js.map +1 -1
  19. package/dist/types/DayPickerBase.d.ts +5 -1
  20. package/dist/types/Labels.d.ts +1 -1
  21. package/package.json +15 -14
  22. package/src/DayPicker.tsx +113 -0
  23. package/src/components/Button/Button.test.tsx +47 -0
  24. package/src/components/Button/Button.tsx +36 -0
  25. package/src/components/Button/index.ts +1 -0
  26. package/src/components/Caption/Caption.test.tsx +86 -0
  27. package/src/components/Caption/Caption.tsx +54 -0
  28. package/src/components/Caption/index.ts +1 -0
  29. package/src/components/CaptionDropdowns/CaptionDropdowns.test.tsx +123 -0
  30. package/src/components/CaptionDropdowns/CaptionDropdowns.tsx +43 -0
  31. package/src/components/CaptionDropdowns/index.ts +1 -0
  32. package/src/components/CaptionLabel/CaptionLabel.test.tsx +29 -0
  33. package/src/components/CaptionLabel/CaptionLabel.tsx +32 -0
  34. package/src/components/CaptionLabel/index.ts +1 -0
  35. package/src/components/CaptionNavigation/CaptionNavigation.test.tsx +172 -0
  36. package/src/components/CaptionNavigation/CaptionNavigation.tsx +63 -0
  37. package/src/components/CaptionNavigation/index.ts +1 -0
  38. package/src/components/Day/Day.test.tsx +84 -0
  39. package/src/components/Day/Day.tsx +30 -0
  40. package/src/components/Day/index.ts +1 -0
  41. package/src/components/DayContent/DayContent.test.tsx +51 -0
  42. package/src/components/DayContent/DayContent.tsx +36 -0
  43. package/src/components/DayContent/index.ts +1 -0
  44. package/src/components/Dropdown/Dropdown.test.tsx +73 -0
  45. package/src/components/Dropdown/Dropdown.tsx +56 -0
  46. package/src/components/Dropdown/index.ts +1 -0
  47. package/src/components/Footer/Footer.test.tsx +29 -0
  48. package/src/components/Footer/Footer.tsx +20 -0
  49. package/src/components/Footer/index.ts +1 -0
  50. package/src/components/Head/Head.test.tsx +117 -0
  51. package/src/components/Head/Head.tsx +51 -0
  52. package/src/components/Head/index.ts +1 -0
  53. package/src/components/Head/utils/getWeekdays.test.ts +36 -0
  54. package/src/components/Head/utils/getWeekdays.ts +22 -0
  55. package/src/components/Head/utils/index.ts +1 -0
  56. package/src/components/IconDropdown/IconDropdown.test.tsx +20 -0
  57. package/src/components/IconDropdown/IconDropdown.tsx +24 -0
  58. package/src/components/IconDropdown/index.ts +1 -0
  59. package/src/components/IconLeft/IconLeft.test.tsx +20 -0
  60. package/src/components/IconLeft/IconLeft.tsx +18 -0
  61. package/src/components/IconLeft/index.ts +1 -0
  62. package/src/components/IconRight/IconRight.test.tsx +20 -0
  63. package/src/components/IconRight/IconRight.tsx +17 -0
  64. package/src/components/IconRight/index.ts +1 -0
  65. package/src/components/Month/Month.test.tsx +216 -0
  66. package/src/components/Month/Month.tsx +53 -0
  67. package/src/components/Month/index.ts +1 -0
  68. package/src/components/MonthsDropdown/MonthsDropdown.test.tsx +99 -0
  69. package/src/components/MonthsDropdown/MonthsDropdown.tsx +75 -0
  70. package/src/components/MonthsDropdown/index.ts +1 -0
  71. package/src/components/Navigation/Navigation.test.tsx +129 -0
  72. package/src/components/Navigation/Navigation.tsx +102 -0
  73. package/src/components/Navigation/index.ts +1 -0
  74. package/src/components/Root/Root.test.tsx +123 -0
  75. package/src/components/Root/Root.tsx +58 -0
  76. package/src/components/Root/index.ts +1 -0
  77. package/src/components/Row/Row.test.tsx +69 -0
  78. package/src/components/Row/Row.tsx +51 -0
  79. package/src/components/Row/index.ts +1 -0
  80. package/src/components/Table/Table.test.tsx +42 -0
  81. package/src/components/Table/Table.tsx +60 -0
  82. package/src/components/Table/__snapshots__/Table.test.tsx.snap +1453 -0
  83. package/src/components/Table/index.ts +1 -0
  84. package/src/components/Table/utils/daysToMonthWeeks.ts +47 -0
  85. package/src/components/Table/utils/getMonthWeeks.test.ts +68 -0
  86. package/src/components/Table/utils/getMonthWeeks.ts +55 -0
  87. package/src/components/WeekNumber/WeekNumber.test.tsx +46 -0
  88. package/src/components/WeekNumber/WeekNumber.tsx +58 -0
  89. package/src/components/WeekNumber/__snapshots__/WeekNumber.test.tsx.snap +11 -0
  90. package/src/components/WeekNumber/index.ts +1 -0
  91. package/src/components/YearsDropdown/YearsDropdown.test.tsx +98 -0
  92. package/src/components/YearsDropdown/YearsDropdown.tsx +76 -0
  93. package/src/components/YearsDropdown/index.ts +1 -0
  94. package/src/contexts/DayPicker/DayPickerContext.tsx +156 -0
  95. package/src/contexts/DayPicker/defaultClassNames.ts +58 -0
  96. package/src/contexts/DayPicker/defaultContextValue.ts +37 -0
  97. package/src/contexts/DayPicker/formatters/formatCaption.test.ts +15 -0
  98. package/src/contexts/DayPicker/formatters/formatCaption.ts +12 -0
  99. package/src/contexts/DayPicker/formatters/formatDay.test.ts +7 -0
  100. package/src/contexts/DayPicker/formatters/formatDay.ts +9 -0
  101. package/src/contexts/DayPicker/formatters/formatMonthCaption.test.ts +15 -0
  102. package/src/contexts/DayPicker/formatters/formatMonthCaption.ts +12 -0
  103. package/src/contexts/DayPicker/formatters/formatWeekNumber.test.ts +5 -0
  104. package/src/contexts/DayPicker/formatters/formatWeekNumber.ts +6 -0
  105. package/src/contexts/DayPicker/formatters/formatWeekdayName.test.ts +15 -0
  106. package/src/contexts/DayPicker/formatters/formatWeekdayName.ts +12 -0
  107. package/src/contexts/DayPicker/formatters/formatYearCaption.test.ts +7 -0
  108. package/src/contexts/DayPicker/formatters/formatYearCaption.ts +11 -0
  109. package/src/contexts/DayPicker/formatters/index.ts +6 -0
  110. package/src/contexts/DayPicker/index.ts +2 -0
  111. package/src/contexts/DayPicker/labels/index.ts +7 -0
  112. package/src/contexts/DayPicker/labels/labelDay.test.ts +7 -0
  113. package/src/contexts/DayPicker/labels/labelDay.ts +10 -0
  114. package/src/contexts/DayPicker/labels/labelMonthDropdown.test.ts +5 -0
  115. package/src/contexts/DayPicker/labels/labelMonthDropdown.ts +6 -0
  116. package/src/contexts/DayPicker/labels/labelNext.test.ts +5 -0
  117. package/src/contexts/DayPicker/labels/labelNext.ts +8 -0
  118. package/src/contexts/DayPicker/labels/labelPrevious.test.ts +5 -0
  119. package/src/contexts/DayPicker/labels/labelPrevious.ts +8 -0
  120. package/src/contexts/DayPicker/labels/labelWeekNumber.test.ts +5 -0
  121. package/src/contexts/DayPicker/labels/labelWeekNumber.ts +8 -0
  122. package/src/contexts/DayPicker/labels/labelWeekday.test.ts +15 -0
  123. package/src/contexts/DayPicker/labels/labelWeekday.ts +10 -0
  124. package/src/contexts/DayPicker/labels/labelYearDropdown.test.ts +5 -0
  125. package/src/contexts/DayPicker/labels/labelYearDropdown.ts +6 -0
  126. package/src/contexts/DayPicker/useDayPicker.test.ts +297 -0
  127. package/src/contexts/DayPicker/useDayPicker.ts +17 -0
  128. package/src/contexts/DayPicker/utils/index.ts +1 -0
  129. package/src/contexts/DayPicker/utils/parseFromToProps.test.ts +47 -0
  130. package/src/contexts/DayPicker/utils/parseFromToProps.ts +32 -0
  131. package/src/contexts/Focus/FocusContext.tsx +174 -0
  132. package/src/contexts/Focus/index.ts +2 -0
  133. package/src/contexts/Focus/useFocusContext.test.ts +183 -0
  134. package/src/contexts/Focus/useFocusContext.ts +12 -0
  135. package/src/contexts/Focus/utils/getInitialFocusTarget.test.tsx +12 -0
  136. package/src/contexts/Focus/utils/getInitialFocusTarget.tsx +44 -0
  137. package/src/contexts/Modifiers/ModifiersContext.tsx +44 -0
  138. package/src/contexts/Modifiers/index.ts +2 -0
  139. package/src/contexts/Modifiers/useModifiers.test.ts +46 -0
  140. package/src/contexts/Modifiers/useModifiers.ts +17 -0
  141. package/src/contexts/Modifiers/utils/getActiveModifiers.test.ts +53 -0
  142. package/src/contexts/Modifiers/utils/getActiveModifiers.ts +33 -0
  143. package/src/contexts/Modifiers/utils/getCustomModifiers.test.ts +14 -0
  144. package/src/contexts/Modifiers/utils/getCustomModifiers.ts +14 -0
  145. package/src/contexts/Modifiers/utils/getInternalModifiers.test.ts +146 -0
  146. package/src/contexts/Modifiers/utils/getInternalModifiers.ts +58 -0
  147. package/src/contexts/Modifiers/utils/isDateInRange.test.ts +28 -0
  148. package/src/contexts/Modifiers/utils/isDateInRange.ts +27 -0
  149. package/src/contexts/Modifiers/utils/isMatch.test.ts +92 -0
  150. package/src/contexts/Modifiers/utils/isMatch.ts +76 -0
  151. package/src/contexts/Modifiers/utils/matcherToArray.test.ts +22 -0
  152. package/src/contexts/Modifiers/utils/matcherToArray.ts +14 -0
  153. package/src/contexts/Navigation/NavigationContext.tsx +84 -0
  154. package/src/contexts/Navigation/index.ts +2 -0
  155. package/src/contexts/Navigation/useNavigation.test.ts +126 -0
  156. package/src/contexts/Navigation/useNavigation.ts +12 -0
  157. package/src/contexts/Navigation/useNavigationState.test.ts +36 -0
  158. package/src/contexts/Navigation/useNavigationState.ts +25 -0
  159. package/src/contexts/Navigation/utils/getDisplayMonths.ts +31 -0
  160. package/src/contexts/Navigation/utils/getInitialMonth.test.ts +56 -0
  161. package/src/contexts/Navigation/utils/getInitialMonth.ts +24 -0
  162. package/src/contexts/Navigation/utils/getNextMonth.test.ts +75 -0
  163. package/src/contexts/Navigation/utils/getNextMonth.ts +45 -0
  164. package/src/contexts/Navigation/utils/getPreviousMonth.test.ts +55 -0
  165. package/src/contexts/Navigation/utils/getPreviousMonth.ts +44 -0
  166. package/src/contexts/RootProvider.tsx +37 -0
  167. package/src/contexts/SelectMultiple/SelectMultipleContext.tsx +135 -0
  168. package/src/contexts/SelectMultiple/index.ts +2 -0
  169. package/src/contexts/SelectMultiple/useSelectMultiple.test.ts +191 -0
  170. package/src/contexts/SelectMultiple/useSelectMultiple.ts +17 -0
  171. package/src/contexts/SelectRange/SelectRangeContext.tsx +158 -0
  172. package/src/contexts/SelectRange/index.ts +2 -0
  173. package/src/contexts/SelectRange/useSelectRange.test.ts +282 -0
  174. package/src/contexts/SelectRange/useSelectRange.ts +15 -0
  175. package/src/contexts/SelectRange/utils/addToRange.test.ts +119 -0
  176. package/src/contexts/SelectRange/utils/addToRange.ts +43 -0
  177. package/src/contexts/SelectSingle/SelectSingleContext.tsx +80 -0
  178. package/src/contexts/SelectSingle/index.ts +2 -0
  179. package/src/contexts/SelectSingle/useSelectSingle.test.ts +81 -0
  180. package/src/contexts/SelectSingle/useSelectSingle.ts +17 -0
  181. package/src/hooks/useActiveModifiers/index.ts +1 -0
  182. package/src/hooks/useActiveModifiers/useActiveModifiers.test.tsx +36 -0
  183. package/src/hooks/useActiveModifiers/useActiveModifiers.tsx +18 -0
  184. package/src/hooks/useControlledValue/index.ts +1 -0
  185. package/src/hooks/useControlledValue/useControlledValue.test.ts +68 -0
  186. package/src/hooks/useControlledValue/useControlledValue.ts +24 -0
  187. package/src/hooks/useDayEventHandlers/index.ts +1 -0
  188. package/src/hooks/useDayEventHandlers/useDayEventHandlers.test.tsx +213 -0
  189. package/src/hooks/useDayEventHandlers/useDayEventHandlers.tsx +195 -0
  190. package/src/hooks/useDayRender/index.ts +1 -0
  191. package/src/hooks/useDayRender/useDayRender.test.tsx +304 -0
  192. package/src/hooks/useDayRender/useDayRender.tsx +123 -0
  193. package/src/hooks/useDayRender/utils/getDayClassNames.test.ts +63 -0
  194. package/src/hooks/useDayRender/utils/getDayClassNames.ts +32 -0
  195. package/src/hooks/useDayRender/utils/getDayStyle.ts +19 -0
  196. package/src/hooks/useInput/index.ts +1 -0
  197. package/src/hooks/useInput/useInput.ts +175 -0
  198. package/src/hooks/useInput/utils/isValidDate.tsx +4 -0
  199. package/src/hooks/useSelectedDays/index.ts +1 -0
  200. package/src/hooks/useSelectedDays/useSelectedDays.test.ts +72 -0
  201. package/src/hooks/useSelectedDays/useSelectedDays.ts +32 -0
  202. package/src/index.ts +43 -0
  203. package/src/style.css +310 -0
  204. package/src/style.css.d.ts +38 -0
  205. package/src/types/DayPickerBase.ts +267 -0
  206. package/src/types/DayPickerDefault.ts +15 -0
  207. package/src/types/DayPickerMultiple.ts +26 -0
  208. package/src/types/DayPickerRange.ts +27 -0
  209. package/src/types/DayPickerSingle.ts +24 -0
  210. package/src/types/EventHandlers.ts +87 -0
  211. package/src/types/Formatters.ts +29 -0
  212. package/src/types/Labels.ts +36 -0
  213. package/src/types/Matchers.ts +106 -0
  214. package/src/types/Modifiers.ts +62 -0
  215. package/src/types/Styles.ts +108 -0
  216. package/tsconfig.json +24 -0
  217. package/CHANGELOG.md +0 -26
  218. package/dist/react-day-picker.min.js +0 -1
@@ -0,0 +1,47 @@
1
+ import { parseFromToProps } from 'contexts/DayPicker/utils';
2
+
3
+ describe('when "fromMonth" is passed in', () => {
4
+ const fromMonth = new Date(2021, 4, 3);
5
+ const expectedFromDate = new Date(2021, 4, 1);
6
+ const { fromDate } = parseFromToProps({ fromMonth });
7
+ test('"fromDate" should be the start of that month', () => {
8
+ expect(fromDate).toEqual(expectedFromDate);
9
+ });
10
+ describe('when "fromYear" is passed in', () => {
11
+ test('"fromDate" should be the start of that month', () => {
12
+ expect(fromDate).toEqual(expectedFromDate);
13
+ });
14
+ });
15
+ });
16
+
17
+ describe('when "fromYear" is passed in', () => {
18
+ const fromYear = 2021;
19
+ const expectedFromDate = new Date(2021, 0, 1);
20
+ const { fromDate } = parseFromToProps({ fromYear });
21
+ test('"fromDate" should be the start of that year', () => {
22
+ expect(fromDate).toEqual(expectedFromDate);
23
+ });
24
+ });
25
+
26
+ describe('when "toMonth" is passed in', () => {
27
+ const toMonth = new Date(2021, 4, 3);
28
+ const expectedToDate = new Date(2021, 4, 31);
29
+ const { toDate } = parseFromToProps({ toMonth });
30
+ test('"toDate" should be the end of that month', () => {
31
+ expect(toDate).toEqual(expectedToDate);
32
+ });
33
+ describe('when "fromYear" is passed in', () => {
34
+ test('"toDate" should be the end of that month', () => {
35
+ expect(toDate).toEqual(expectedToDate);
36
+ });
37
+ });
38
+ });
39
+
40
+ describe('when "toYear" is passed in', () => {
41
+ const toYear = 2021;
42
+ const expectedToDate = new Date(2021, 11, 31);
43
+ const { toDate } = parseFromToProps({ toYear });
44
+ test('"toDate" should be the end of that year', () => {
45
+ expect(toDate).toEqual(expectedToDate);
46
+ });
47
+ });
@@ -0,0 +1,32 @@
1
+ import endOfMonth from 'date-fns/endOfMonth';
2
+ import startOfDay from 'date-fns/startOfDay';
3
+ import startOfMonth from 'date-fns/startOfMonth';
4
+
5
+ import { DayPickerBase } from 'types/DayPickerBase';
6
+
7
+ /** Return the `fromDate` and `toDate` prop values values parsing the DayPicker props. */
8
+ export function parseFromToProps(
9
+ props: Pick<
10
+ DayPickerBase,
11
+ 'fromYear' | 'toYear' | 'fromDate' | 'toDate' | 'fromMonth' | 'toMonth'
12
+ >
13
+ ): { fromDate: Date | undefined; toDate: Date | undefined } {
14
+ const { fromYear, toYear, fromMonth, toMonth } = props;
15
+ let { fromDate, toDate } = props;
16
+
17
+ if (fromMonth) {
18
+ fromDate = startOfMonth(fromMonth);
19
+ } else if (fromYear) {
20
+ fromDate = new Date(fromYear, 0, 1);
21
+ }
22
+ if (toMonth) {
23
+ toDate = endOfMonth(toMonth);
24
+ } else if (toYear) {
25
+ toDate = new Date(toYear, 11, 31);
26
+ }
27
+
28
+ return {
29
+ fromDate: fromDate ? startOfDay(fromDate) : undefined,
30
+ toDate: toDate ? startOfDay(toDate) : undefined
31
+ };
32
+ }
@@ -0,0 +1,174 @@
1
+ import React, { createContext, ReactNode, useState } from 'react';
2
+
3
+ import addDays from 'date-fns/addDays';
4
+ import addMonths from 'date-fns/addMonths';
5
+ import addWeeks from 'date-fns/addWeeks';
6
+ import addYears from 'date-fns/addYears';
7
+ import endOfWeek from 'date-fns/endOfWeek';
8
+ import startOfWeek from 'date-fns/startOfWeek';
9
+
10
+ import { useModifiers } from '../Modifiers';
11
+ import { useNavigation } from '../Navigation';
12
+ import { getInitialFocusTarget } from './utils/getInitialFocusTarget';
13
+
14
+ /** Represents the value of the [[NavigationContext]]. */
15
+ export type FocusContextValue = {
16
+ /** The day currently focused. */
17
+ focusedDay: Date | undefined;
18
+ /** Day that will be focused. */
19
+ focusTarget: Date | undefined;
20
+ /** Focus a day. */
21
+ focus: (day: Date) => void;
22
+ /** Blur the focused day. */
23
+ blur: () => void;
24
+ /** Focus the day after the focused day. */
25
+ focusDayAfter: () => void;
26
+ /** Focus the day before the focused day. */
27
+ focusDayBefore: () => void;
28
+ /** Focus the day in the week before the focused day. */
29
+ focusWeekBefore: () => void;
30
+ /** Focus the day in the week after the focused day. */
31
+ focusWeekAfter: () => void;
32
+ /* Focus the day in the month before the focused day. */
33
+ focusMonthBefore: () => void;
34
+ /* Focus the day in the month after the focused day. */
35
+ focusMonthAfter: () => void;
36
+ /* Focus the day in the year before the focused day. */
37
+ focusYearBefore: () => void;
38
+ /* Focus the day in the year after the focused day. */
39
+ focusYearAfter: () => void;
40
+ /* Focus the day at the start of the week of the focused day. */
41
+ focusStartOfWeek: () => void;
42
+ /* Focus the day at the end of the week of focused day. */
43
+ focusEndOfWeek: () => void;
44
+ };
45
+
46
+ /**
47
+ * The Focus context shares details about the focused day for the keyboard
48
+ *
49
+ * Access this context from the [[useFocus]] hook.
50
+ */
51
+ export const FocusContext = createContext<FocusContextValue | undefined>(
52
+ undefined
53
+ );
54
+
55
+ /** The provider for the [[FocusContext]]. */
56
+ export function FocusProvider(props: { children: ReactNode }): JSX.Element {
57
+ const navigation = useNavigation();
58
+ const modifiers = useModifiers();
59
+
60
+ const [focusedDay, setFocusedDay] = useState<Date | undefined>();
61
+ const [lastFocused, setLastFocused] = useState<Date | undefined>();
62
+
63
+ const initialFocusTarget = getInitialFocusTarget(
64
+ navigation.displayMonths,
65
+ modifiers
66
+ );
67
+
68
+ // TODO: cleanup and test obscure code below
69
+ const focusTarget =
70
+ focusedDay ?? (lastFocused && navigation.isDateDisplayed(lastFocused))
71
+ ? lastFocused
72
+ : initialFocusTarget;
73
+
74
+ const blur = () => {
75
+ setLastFocused(focusedDay);
76
+ setFocusedDay(undefined);
77
+ };
78
+ const focus = (date: Date) => {
79
+ setFocusedDay(date);
80
+ };
81
+
82
+ const focusDayBefore = () => {
83
+ if (!focusedDay) return;
84
+ const before = addDays(focusedDay, -1);
85
+ focus(before);
86
+ navigation.goToDate(before, focusedDay);
87
+ };
88
+ const focusDayAfter = () => {
89
+ if (!focusedDay) return;
90
+ const after = addDays(focusedDay, 1);
91
+ focus(after);
92
+ navigation.goToDate(after, focusedDay);
93
+ };
94
+ const focusWeekBefore = () => {
95
+ if (!focusedDay) return;
96
+ const up = addWeeks(focusedDay, -1);
97
+ focus(up);
98
+ navigation.goToDate(up, focusedDay);
99
+ };
100
+ const focusWeekAfter = () => {
101
+ if (!focusedDay) return;
102
+ const down = addWeeks(focusedDay, 1);
103
+ focus(down);
104
+ navigation.goToDate(down, focusedDay);
105
+ };
106
+
107
+ const focusStartOfWeek = (): void => {
108
+ if (!focusedDay) return;
109
+ const dayToFocus = startOfWeek(focusedDay);
110
+ navigation.goToDate(dayToFocus, focusedDay);
111
+ focus(dayToFocus);
112
+ };
113
+
114
+ const focusEndOfWeek = (): void => {
115
+ if (!focusedDay) return;
116
+ const dayToFocus = endOfWeek(focusedDay);
117
+ navigation.goToDate(dayToFocus, focusedDay);
118
+ focus(dayToFocus);
119
+ };
120
+
121
+ const focusMonthBefore = (): void => {
122
+ if (!focusedDay) return;
123
+
124
+ const monthBefore = addMonths(focusedDay, -1);
125
+ navigation.goToDate(monthBefore, focusedDay);
126
+ focus(monthBefore);
127
+ };
128
+
129
+ const focusMonthAfter = () => {
130
+ if (!focusedDay) return;
131
+ const monthAfter = addMonths(focusedDay, 1);
132
+ navigation.goToDate(monthAfter, focusedDay);
133
+ focus(monthAfter);
134
+ };
135
+
136
+ const focusYearBefore = () => {
137
+ if (!focusedDay) return;
138
+
139
+ const yearBefore = addYears(focusedDay, -1);
140
+ navigation.goToDate(yearBefore, focusedDay);
141
+ focus(yearBefore);
142
+ };
143
+
144
+ const focusYearAfter = () => {
145
+ if (!focusedDay) return;
146
+
147
+ const yearAfter = addYears(focusedDay, 1);
148
+ navigation.goToDate(yearAfter, focusedDay);
149
+ focus(yearAfter);
150
+ };
151
+
152
+ const value: FocusContextValue = {
153
+ focusedDay,
154
+ focusTarget,
155
+ blur,
156
+ focus,
157
+ focusDayAfter,
158
+ focusDayBefore,
159
+ focusWeekAfter,
160
+ focusWeekBefore,
161
+ focusMonthBefore,
162
+ focusMonthAfter,
163
+ focusYearBefore,
164
+ focusYearAfter,
165
+ focusStartOfWeek,
166
+ focusEndOfWeek
167
+ };
168
+
169
+ return (
170
+ <FocusContext.Provider value={value}>
171
+ {props.children}
172
+ </FocusContext.Provider>
173
+ );
174
+ }
@@ -0,0 +1,2 @@
1
+ export * from './FocusContext';
2
+ export * from './useFocusContext';
@@ -0,0 +1,183 @@
1
+ import { act, RenderResult } from '@testing-library/react-hooks';
2
+ import {
3
+ addDays,
4
+ addMonths,
5
+ addWeeks,
6
+ addYears,
7
+ endOfWeek,
8
+ startOfWeek
9
+ } from 'date-fns';
10
+
11
+ import { customRenderHook } from 'test/render';
12
+ import { freezeBeforeAll } from 'test/utils';
13
+
14
+ import { FocusContextValue, useFocusContext } from 'contexts/Focus';
15
+
16
+ let renderResult: RenderResult<FocusContextValue>;
17
+
18
+ const today = new Date(2021, 11, 8); // make sure is in the middle of the week for the complete test
19
+ freezeBeforeAll(today);
20
+
21
+ function setup() {
22
+ const { result } = customRenderHook(() => useFocusContext());
23
+ renderResult = result;
24
+ }
25
+
26
+ type HookFunction =
27
+ | 'focusDayAfter'
28
+ | 'focusDayBefore'
29
+ | 'focusWeekAfter'
30
+ | 'focusWeekBefore'
31
+ | 'focusMonthBefore'
32
+ | 'focusMonthAfter'
33
+ | 'focusYearBefore'
34
+ | 'focusYearAfter'
35
+ | 'focusStartOfWeek'
36
+ | 'focusEndOfWeek';
37
+
38
+ beforeEach(() => {
39
+ setup();
40
+ });
41
+
42
+ test('`focusedDay` should be undefined', () => {
43
+ expect(renderResult.current.focusedDay).toBeUndefined();
44
+ });
45
+
46
+ const tests: Array<HookFunction> = [
47
+ 'focusDayAfter',
48
+ 'focusDayBefore',
49
+ 'focusWeekAfter',
50
+ 'focusWeekBefore',
51
+ 'focusMonthBefore',
52
+ 'focusMonthAfter',
53
+ 'focusYearBefore',
54
+ 'focusYearAfter',
55
+ 'focusStartOfWeek',
56
+ 'focusEndOfWeek'
57
+ ];
58
+ describe.each(tests)('when calling %s', (fn: HookFunction) => {
59
+ beforeEach(() => {
60
+ renderResult.current[fn];
61
+ });
62
+ test('`focusedDay` should be undefined', () => {
63
+ expect(renderResult.current.focusedDay).toBeUndefined();
64
+ });
65
+ });
66
+
67
+ describe('when a day is focused', () => {
68
+ const day = today;
69
+ beforeEach(() => {
70
+ act(() => renderResult.current.focus(day));
71
+ });
72
+ test('should set the focused day', () => {
73
+ expect(renderResult.current.focusedDay).toEqual(day);
74
+ });
75
+ describe('when "focusDayBefore" is called', () => {
76
+ const dayBefore = addDays(day, -1);
77
+ beforeEach(() => {
78
+ act(() => renderResult.current.focusDayBefore());
79
+ });
80
+ test('should focus the day before', () => {
81
+ expect(renderResult.current.focusedDay).toEqual(dayBefore);
82
+ });
83
+ test.todo('should call the navigation goToDate');
84
+ });
85
+ describe('when "focusDayAfter" is called', () => {
86
+ beforeEach(() => {
87
+ act(() => renderResult.current.focusDayAfter());
88
+ });
89
+ test('should focus the day after', () => {
90
+ const dayAfter = addDays(day, 1);
91
+ expect(renderResult.current.focusedDay).toEqual(dayAfter);
92
+ });
93
+ test.todo('should call the navigation goToDate');
94
+ });
95
+ describe('when "focusWeekBefore" is called', () => {
96
+ beforeEach(() => {
97
+ act(() => renderResult.current.focusWeekBefore());
98
+ });
99
+ test('should focus the day in the previous week', () => {
100
+ const prevWeek = addWeeks(day, -1);
101
+ expect(renderResult.current.focusedDay).toEqual(prevWeek);
102
+ });
103
+ test.todo('should call the navigation goToDate');
104
+ });
105
+ describe('when "focusWeekAfter" is called', () => {
106
+ beforeEach(() => {
107
+ act(() => renderResult.current.focusWeekAfter());
108
+ });
109
+ test('should focus the day in the next week', () => {
110
+ const nextWeek = addWeeks(day, 1);
111
+ expect(renderResult.current.focusedDay).toEqual(nextWeek);
112
+ });
113
+ test.todo('should call the navigation goToDate');
114
+ });
115
+ describe('when "focusStartOfWeek" is called', () => {
116
+ beforeEach(() => {
117
+ act(() => renderResult.current.focusStartOfWeek());
118
+ });
119
+ test('should focus the first day of the week', () => {
120
+ const firstDayOfWeek = startOfWeek(day);
121
+ expect(renderResult.current.focusedDay).toEqual(firstDayOfWeek);
122
+ });
123
+ test.todo('should call the navigation goToDate');
124
+ });
125
+ describe('when "focusEndOfWeek" is called', () => {
126
+ beforeEach(() => {
127
+ act(() => renderResult.current.focusEndOfWeek());
128
+ });
129
+ test('should focus the last day of the week', () => {
130
+ const lastDayOfWeek = endOfWeek(day);
131
+ expect(renderResult.current.focusedDay).toEqual(lastDayOfWeek);
132
+ });
133
+ test.todo('should call the navigation goToDate');
134
+ });
135
+ describe('when "focusMonthBefore" is called', () => {
136
+ beforeEach(() => {
137
+ act(() => renderResult.current.focusMonthBefore());
138
+ });
139
+ test('should focus the day in the month before', () => {
140
+ const monthBefore = addMonths(day, -1);
141
+ expect(renderResult.current.focusedDay).toEqual(monthBefore);
142
+ });
143
+ test.todo('should call the navigation goToDate');
144
+ });
145
+ describe('when "focusMonthAfter" is called', () => {
146
+ beforeEach(() => {
147
+ act(() => renderResult.current.focusMonthAfter());
148
+ });
149
+ test('should focus the day in the month after', () => {
150
+ const monthAfter = addMonths(day, 1);
151
+ expect(renderResult.current.focusedDay).toEqual(monthAfter);
152
+ });
153
+ test.todo('should call the navigation goToDate');
154
+ });
155
+ describe('when "focusYearBefore" is called', () => {
156
+ beforeEach(() => {
157
+ act(() => renderResult.current.focusYearBefore());
158
+ });
159
+ test('should focus the day in the year before', () => {
160
+ const prevYear = addYears(day, -1);
161
+ expect(renderResult.current.focusedDay).toEqual(prevYear);
162
+ });
163
+ test.todo('should call the navigation goToDate');
164
+ });
165
+ describe('when "focusYearAfter" is called', () => {
166
+ beforeEach(() => {
167
+ act(() => renderResult.current.focusYearAfter());
168
+ });
169
+ test('should focus the day in the year after', () => {
170
+ const nextYear = addYears(day, 1);
171
+ expect(renderResult.current.focusedDay).toEqual(nextYear);
172
+ });
173
+ test.todo('should call the navigation goToDate');
174
+ });
175
+ describe('when blur is called', () => {
176
+ beforeEach(() => {
177
+ act(() => renderResult.current.blur());
178
+ });
179
+ test('`focusedDay` should be undefined', () => {
180
+ expect(renderResult.current.focusedDay).toBeUndefined();
181
+ });
182
+ });
183
+ });
@@ -0,0 +1,12 @@
1
+ import { useContext } from 'react';
2
+
3
+ import { FocusContext, FocusContextValue } from './FocusContext';
4
+
5
+ /** Hook to access the [[FocusContext]]. */
6
+ export function useFocusContext(): FocusContextValue {
7
+ const context = useContext(FocusContext);
8
+ if (!context) {
9
+ throw new Error('useFocusContext must be used within a FocusProvider');
10
+ }
11
+ return context;
12
+ }
@@ -0,0 +1,12 @@
1
+ describe('when a day is selected', () => {
2
+ test.todo('should return the selected day');
3
+ });
4
+
5
+ describe('when a day is not selected', () => {
6
+ describe('when today is in the calendar', () => {
7
+ test.todo('should return the today date');
8
+ });
9
+ describe('when today is not in the calendar', () => {
10
+ test.todo('should return the first focusable day');
11
+ });
12
+ });
@@ -0,0 +1,44 @@
1
+ import addDays from 'date-fns/addDays';
2
+ import endOfMonth from 'date-fns/endOfMonth';
3
+ import startOfMonth from 'date-fns/startOfMonth';
4
+
5
+ import { getActiveModifiers } from 'contexts/Modifiers';
6
+ import { Modifiers } from 'types/Modifiers';
7
+
8
+ /** Returns the day that should be the target of the focus when DayPicker is rendered the first time. */
9
+ export function getInitialFocusTarget(
10
+ displayMonths: Date[],
11
+ modifiers: Modifiers
12
+ ) {
13
+ const firstDayInMonth = startOfMonth(displayMonths[0]);
14
+ const lastDayInMonth = endOfMonth(displayMonths[displayMonths.length - 1]);
15
+
16
+ // TODO: cleanup code
17
+ let firstFocusableDay;
18
+ let today;
19
+ let date = firstDayInMonth;
20
+
21
+ while (date <= lastDayInMonth) {
22
+ const activeModifiers = getActiveModifiers(date, modifiers);
23
+ const isFocusable = !activeModifiers.disabled && !activeModifiers.hidden;
24
+ if (!isFocusable) {
25
+ date = addDays(date, 1);
26
+ continue;
27
+ }
28
+ if (activeModifiers.selected) {
29
+ return date;
30
+ }
31
+ if (activeModifiers.today && !today) {
32
+ today = date;
33
+ }
34
+ if (!firstFocusableDay) {
35
+ firstFocusableDay = date;
36
+ }
37
+ date = addDays(date, 1);
38
+ }
39
+ if (today) {
40
+ return today;
41
+ } else {
42
+ return firstFocusableDay;
43
+ }
44
+ }
@@ -0,0 +1,44 @@
1
+ import React, { createContext, ReactNode } from 'react';
2
+
3
+ import { useDayPicker } from 'contexts/DayPicker';
4
+ import { useSelectMultiple } from 'contexts/SelectMultiple';
5
+ import { useSelectRange } from 'contexts/SelectRange';
6
+ import { CustomModifiers, InternalModifiers, Modifiers } from 'types/Modifiers';
7
+
8
+ import { getCustomModifiers } from './utils/getCustomModifiers';
9
+ import { getInternalModifiers } from './utils/getInternalModifiers';
10
+
11
+ /** The Modifiers context store the modifiers used in DayPicker. To access the value of this context, use [[useModifiers]]. */
12
+ export const ModifiersContext = createContext<Modifiers | undefined>(undefined);
13
+
14
+ export type ModifiersProviderProps = {
15
+ children: ReactNode;
16
+ };
17
+
18
+ /** Provide the value for the [[ModifiersContext]]. */
19
+ export function ModifiersProvider(props: ModifiersProviderProps): JSX.Element {
20
+ const dayPicker = useDayPicker();
21
+ const selectMultiple = useSelectMultiple();
22
+ const selectRange = useSelectRange();
23
+
24
+ const internalModifiers: InternalModifiers = getInternalModifiers(
25
+ dayPicker,
26
+ selectMultiple,
27
+ selectRange
28
+ );
29
+
30
+ const customModifiers: CustomModifiers = getCustomModifiers(
31
+ dayPicker.modifiers
32
+ );
33
+
34
+ const modifiers: Modifiers = {
35
+ ...internalModifiers,
36
+ ...customModifiers
37
+ };
38
+
39
+ return (
40
+ <ModifiersContext.Provider value={modifiers}>
41
+ {props.children}
42
+ </ModifiersContext.Provider>
43
+ );
44
+ }
@@ -0,0 +1,2 @@
1
+ export * from './useModifiers';
2
+ export * from './utils/getActiveModifiers';
@@ -0,0 +1,46 @@
1
+ import { RenderResult } from '@testing-library/react-hooks';
2
+ import { DayPickerProps } from 'DayPicker';
3
+
4
+ import { customRenderHook } from 'test/render';
5
+
6
+ import { useModifiers } from 'contexts/Modifiers';
7
+ import { DayModifiers, InternalModifier, Modifiers } from 'types/Modifiers';
8
+
9
+ let renderResult: RenderResult<Modifiers>;
10
+
11
+ function setup(dayPickerProps: DayPickerProps) {
12
+ const { result } = customRenderHook(() => useModifiers(), dayPickerProps);
13
+ renderResult = result;
14
+ }
15
+
16
+ const internalModifiers = Object.values(InternalModifier);
17
+
18
+ describe('when rendered with custom modifiers', () => {
19
+ const modifier = new Date(2018, 11, 12);
20
+ const dayModifiers: DayModifiers = {
21
+ foo: modifier,
22
+ today: modifier,
23
+ outside: modifier,
24
+ disabled: modifier,
25
+ selected: modifier,
26
+ hidden: modifier,
27
+ range_start: modifier,
28
+ range_end: modifier,
29
+ range_middle: modifier
30
+ };
31
+ beforeEach(() => {
32
+ setup({ modifiers: dayModifiers });
33
+ });
34
+
35
+ test('should return the custom modifiers', () => {
36
+ expect(renderResult.current.foo).toEqual([dayModifiers.foo]);
37
+ });
38
+ test.each(internalModifiers)(
39
+ 'should override the %s internal modifier',
40
+ (internalModifier) => {
41
+ expect(renderResult.current[internalModifier]).toEqual([
42
+ dayModifiers[internalModifier]
43
+ ]);
44
+ }
45
+ );
46
+ });
@@ -0,0 +1,17 @@
1
+ import { useContext } from 'react';
2
+
3
+ import { Modifiers } from 'types/Modifiers';
4
+
5
+ import { ModifiersContext } from './ModifiersContext';
6
+
7
+ /**
8
+ * Return the modifiers used by DayPicker.
9
+ *
10
+ * Requires to be wrapped into [[ModifiersProvider]]. */
11
+ export function useModifiers(): Modifiers {
12
+ const context = useContext(ModifiersContext);
13
+ if (!context) {
14
+ throw new Error('useModifiers must be used within a ModifiersProvider');
15
+ }
16
+ return context;
17
+ }
@@ -0,0 +1,53 @@
1
+ import { addMonths } from 'date-fns';
2
+
3
+ import {
4
+ InternalModifier,
5
+ InternalModifiers,
6
+ Modifiers
7
+ } from 'types/Modifiers';
8
+
9
+ import { getActiveModifiers } from './getActiveModifiers';
10
+
11
+ const day = new Date();
12
+
13
+ const internalModifiers: InternalModifiers = {
14
+ [InternalModifier.Outside]: [],
15
+ [InternalModifier.Disabled]: [],
16
+ [InternalModifier.Selected]: [],
17
+ [InternalModifier.Hidden]: [],
18
+ [InternalModifier.Today]: [],
19
+ [InternalModifier.RangeStart]: [],
20
+ [InternalModifier.RangeEnd]: [],
21
+ [InternalModifier.RangeMiddle]: []
22
+ };
23
+ describe('when the day matches a modifier', () => {
24
+ const modifiers: Modifiers = {
25
+ ...internalModifiers,
26
+ foo: [day]
27
+ };
28
+ const result = getActiveModifiers(day, modifiers);
29
+ test('should return the modifier as active', () => {
30
+ expect(result.foo).toBe(true);
31
+ });
32
+ });
33
+ describe('when the day does not match a modifier', () => {
34
+ const modifiers: Modifiers = {
35
+ ...internalModifiers,
36
+ foo: []
37
+ };
38
+ const result = getActiveModifiers(day, modifiers);
39
+ test('should not return the modifier as active', () => {
40
+ expect(result.foo).toBeUndefined();
41
+ });
42
+ });
43
+
44
+ describe('when the day is not in the same display month', () => {
45
+ const modifiers: Modifiers = {
46
+ ...internalModifiers
47
+ };
48
+ const displayMonth = addMonths(day, 1);
49
+ const result = getActiveModifiers(day, modifiers, displayMonth);
50
+ test('should not return the modifier as active', () => {
51
+ expect(result.outside).toBe(true);
52
+ });
53
+ });