react-day-picker 8.0.3 → 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 (196) hide show
  1. package/package.json +13 -11
  2. package/src/DayPicker.tsx +113 -0
  3. package/src/components/Button/Button.test.tsx +47 -0
  4. package/src/components/Button/Button.tsx +36 -0
  5. package/src/components/Button/index.ts +1 -0
  6. package/src/components/Caption/Caption.test.tsx +86 -0
  7. package/src/components/Caption/Caption.tsx +54 -0
  8. package/src/components/Caption/index.ts +1 -0
  9. package/src/components/CaptionDropdowns/CaptionDropdowns.test.tsx +123 -0
  10. package/src/components/CaptionDropdowns/CaptionDropdowns.tsx +43 -0
  11. package/src/components/CaptionDropdowns/index.ts +1 -0
  12. package/src/components/CaptionLabel/CaptionLabel.test.tsx +29 -0
  13. package/src/components/CaptionLabel/CaptionLabel.tsx +32 -0
  14. package/src/components/CaptionLabel/index.ts +1 -0
  15. package/src/components/CaptionNavigation/CaptionNavigation.test.tsx +172 -0
  16. package/src/components/CaptionNavigation/CaptionNavigation.tsx +63 -0
  17. package/src/components/CaptionNavigation/index.ts +1 -0
  18. package/src/components/Day/Day.test.tsx +84 -0
  19. package/src/components/Day/Day.tsx +30 -0
  20. package/src/components/Day/index.ts +1 -0
  21. package/src/components/DayContent/DayContent.test.tsx +51 -0
  22. package/src/components/DayContent/DayContent.tsx +36 -0
  23. package/src/components/DayContent/index.ts +1 -0
  24. package/src/components/Dropdown/Dropdown.test.tsx +73 -0
  25. package/src/components/Dropdown/Dropdown.tsx +56 -0
  26. package/src/components/Dropdown/index.ts +1 -0
  27. package/src/components/Footer/Footer.test.tsx +29 -0
  28. package/src/components/Footer/Footer.tsx +20 -0
  29. package/src/components/Footer/index.ts +1 -0
  30. package/src/components/Head/Head.test.tsx +117 -0
  31. package/src/components/Head/Head.tsx +51 -0
  32. package/src/components/Head/index.ts +1 -0
  33. package/src/components/Head/utils/getWeekdays.test.ts +36 -0
  34. package/src/components/Head/utils/getWeekdays.ts +22 -0
  35. package/src/components/Head/utils/index.ts +1 -0
  36. package/src/components/IconDropdown/IconDropdown.test.tsx +20 -0
  37. package/src/components/IconDropdown/IconDropdown.tsx +24 -0
  38. package/src/components/IconDropdown/index.ts +1 -0
  39. package/src/components/IconLeft/IconLeft.test.tsx +20 -0
  40. package/src/components/IconLeft/IconLeft.tsx +18 -0
  41. package/src/components/IconLeft/index.ts +1 -0
  42. package/src/components/IconRight/IconRight.test.tsx +20 -0
  43. package/src/components/IconRight/IconRight.tsx +17 -0
  44. package/src/components/IconRight/index.ts +1 -0
  45. package/src/components/Month/Month.test.tsx +216 -0
  46. package/src/components/Month/Month.tsx +53 -0
  47. package/src/components/Month/index.ts +1 -0
  48. package/src/components/MonthsDropdown/MonthsDropdown.test.tsx +99 -0
  49. package/src/components/MonthsDropdown/MonthsDropdown.tsx +75 -0
  50. package/src/components/MonthsDropdown/index.ts +1 -0
  51. package/src/components/Navigation/Navigation.test.tsx +129 -0
  52. package/src/components/Navigation/Navigation.tsx +102 -0
  53. package/src/components/Navigation/index.ts +1 -0
  54. package/src/components/Root/Root.test.tsx +123 -0
  55. package/src/components/Root/Root.tsx +58 -0
  56. package/src/components/Root/index.ts +1 -0
  57. package/src/components/Row/Row.test.tsx +69 -0
  58. package/src/components/Row/Row.tsx +51 -0
  59. package/src/components/Row/index.ts +1 -0
  60. package/src/components/Table/Table.test.tsx +42 -0
  61. package/src/components/Table/Table.tsx +60 -0
  62. package/src/components/Table/__snapshots__/Table.test.tsx.snap +1453 -0
  63. package/src/components/Table/index.ts +1 -0
  64. package/src/components/Table/utils/daysToMonthWeeks.ts +47 -0
  65. package/src/components/Table/utils/getMonthWeeks.test.ts +68 -0
  66. package/src/components/Table/utils/getMonthWeeks.ts +55 -0
  67. package/src/components/WeekNumber/WeekNumber.test.tsx +46 -0
  68. package/src/components/WeekNumber/WeekNumber.tsx +58 -0
  69. package/src/components/WeekNumber/__snapshots__/WeekNumber.test.tsx.snap +11 -0
  70. package/src/components/WeekNumber/index.ts +1 -0
  71. package/src/components/YearsDropdown/YearsDropdown.test.tsx +98 -0
  72. package/src/components/YearsDropdown/YearsDropdown.tsx +76 -0
  73. package/src/components/YearsDropdown/index.ts +1 -0
  74. package/src/contexts/DayPicker/DayPickerContext.tsx +156 -0
  75. package/src/contexts/DayPicker/defaultClassNames.ts +58 -0
  76. package/src/contexts/DayPicker/defaultContextValue.ts +37 -0
  77. package/src/contexts/DayPicker/formatters/formatCaption.test.ts +15 -0
  78. package/src/contexts/DayPicker/formatters/formatCaption.ts +12 -0
  79. package/src/contexts/DayPicker/formatters/formatDay.test.ts +7 -0
  80. package/src/contexts/DayPicker/formatters/formatDay.ts +9 -0
  81. package/src/contexts/DayPicker/formatters/formatMonthCaption.test.ts +15 -0
  82. package/src/contexts/DayPicker/formatters/formatMonthCaption.ts +12 -0
  83. package/src/contexts/DayPicker/formatters/formatWeekNumber.test.ts +5 -0
  84. package/src/contexts/DayPicker/formatters/formatWeekNumber.ts +6 -0
  85. package/src/contexts/DayPicker/formatters/formatWeekdayName.test.ts +15 -0
  86. package/src/contexts/DayPicker/formatters/formatWeekdayName.ts +12 -0
  87. package/src/contexts/DayPicker/formatters/formatYearCaption.test.ts +7 -0
  88. package/src/contexts/DayPicker/formatters/formatYearCaption.ts +11 -0
  89. package/src/contexts/DayPicker/formatters/index.ts +6 -0
  90. package/src/contexts/DayPicker/index.ts +2 -0
  91. package/src/contexts/DayPicker/labels/index.ts +7 -0
  92. package/src/contexts/DayPicker/labels/labelDay.test.ts +7 -0
  93. package/src/contexts/DayPicker/labels/labelDay.ts +10 -0
  94. package/src/contexts/DayPicker/labels/labelMonthDropdown.test.ts +5 -0
  95. package/src/contexts/DayPicker/labels/labelMonthDropdown.ts +6 -0
  96. package/src/contexts/DayPicker/labels/labelNext.test.ts +5 -0
  97. package/src/contexts/DayPicker/labels/labelNext.ts +8 -0
  98. package/src/contexts/DayPicker/labels/labelPrevious.test.ts +5 -0
  99. package/src/contexts/DayPicker/labels/labelPrevious.ts +8 -0
  100. package/src/contexts/DayPicker/labels/labelWeekNumber.test.ts +5 -0
  101. package/src/contexts/DayPicker/labels/labelWeekNumber.ts +8 -0
  102. package/src/contexts/DayPicker/labels/labelWeekday.test.ts +15 -0
  103. package/src/contexts/DayPicker/labels/labelWeekday.ts +10 -0
  104. package/src/contexts/DayPicker/labels/labelYearDropdown.test.ts +5 -0
  105. package/src/contexts/DayPicker/labels/labelYearDropdown.ts +6 -0
  106. package/src/contexts/DayPicker/useDayPicker.test.ts +297 -0
  107. package/src/contexts/DayPicker/useDayPicker.ts +17 -0
  108. package/src/contexts/DayPicker/utils/index.ts +1 -0
  109. package/src/contexts/DayPicker/utils/parseFromToProps.test.ts +47 -0
  110. package/src/contexts/DayPicker/utils/parseFromToProps.ts +32 -0
  111. package/src/contexts/Focus/FocusContext.tsx +174 -0
  112. package/src/contexts/Focus/index.ts +2 -0
  113. package/src/contexts/Focus/useFocusContext.test.ts +183 -0
  114. package/src/contexts/Focus/useFocusContext.ts +12 -0
  115. package/src/contexts/Focus/utils/getInitialFocusTarget.test.tsx +12 -0
  116. package/src/contexts/Focus/utils/getInitialFocusTarget.tsx +44 -0
  117. package/src/contexts/Modifiers/ModifiersContext.tsx +44 -0
  118. package/src/contexts/Modifiers/index.ts +2 -0
  119. package/src/contexts/Modifiers/useModifiers.test.ts +46 -0
  120. package/src/contexts/Modifiers/useModifiers.ts +17 -0
  121. package/src/contexts/Modifiers/utils/getActiveModifiers.test.ts +53 -0
  122. package/src/contexts/Modifiers/utils/getActiveModifiers.ts +33 -0
  123. package/src/contexts/Modifiers/utils/getCustomModifiers.test.ts +14 -0
  124. package/src/contexts/Modifiers/utils/getCustomModifiers.ts +14 -0
  125. package/src/contexts/Modifiers/utils/getInternalModifiers.test.ts +146 -0
  126. package/src/contexts/Modifiers/utils/getInternalModifiers.ts +58 -0
  127. package/src/contexts/Modifiers/utils/isDateInRange.test.ts +28 -0
  128. package/src/contexts/Modifiers/utils/isDateInRange.ts +27 -0
  129. package/src/contexts/Modifiers/utils/isMatch.test.ts +92 -0
  130. package/src/contexts/Modifiers/utils/isMatch.ts +76 -0
  131. package/src/contexts/Modifiers/utils/matcherToArray.test.ts +22 -0
  132. package/src/contexts/Modifiers/utils/matcherToArray.ts +14 -0
  133. package/src/contexts/Navigation/NavigationContext.tsx +84 -0
  134. package/src/contexts/Navigation/index.ts +2 -0
  135. package/src/contexts/Navigation/useNavigation.test.ts +126 -0
  136. package/src/contexts/Navigation/useNavigation.ts +12 -0
  137. package/src/contexts/Navigation/useNavigationState.test.ts +36 -0
  138. package/src/contexts/Navigation/useNavigationState.ts +25 -0
  139. package/src/contexts/Navigation/utils/getDisplayMonths.ts +31 -0
  140. package/src/contexts/Navigation/utils/getInitialMonth.test.ts +56 -0
  141. package/src/contexts/Navigation/utils/getInitialMonth.ts +24 -0
  142. package/src/contexts/Navigation/utils/getNextMonth.test.ts +75 -0
  143. package/src/contexts/Navigation/utils/getNextMonth.ts +45 -0
  144. package/src/contexts/Navigation/utils/getPreviousMonth.test.ts +55 -0
  145. package/src/contexts/Navigation/utils/getPreviousMonth.ts +44 -0
  146. package/src/contexts/RootProvider.tsx +37 -0
  147. package/src/contexts/SelectMultiple/SelectMultipleContext.tsx +135 -0
  148. package/src/contexts/SelectMultiple/index.ts +2 -0
  149. package/src/contexts/SelectMultiple/useSelectMultiple.test.ts +191 -0
  150. package/src/contexts/SelectMultiple/useSelectMultiple.ts +17 -0
  151. package/src/contexts/SelectRange/SelectRangeContext.tsx +158 -0
  152. package/src/contexts/SelectRange/index.ts +2 -0
  153. package/src/contexts/SelectRange/useSelectRange.test.ts +282 -0
  154. package/src/contexts/SelectRange/useSelectRange.ts +15 -0
  155. package/src/contexts/SelectRange/utils/addToRange.test.ts +119 -0
  156. package/src/contexts/SelectRange/utils/addToRange.ts +43 -0
  157. package/src/contexts/SelectSingle/SelectSingleContext.tsx +80 -0
  158. package/src/contexts/SelectSingle/index.ts +2 -0
  159. package/src/contexts/SelectSingle/useSelectSingle.test.ts +81 -0
  160. package/src/contexts/SelectSingle/useSelectSingle.ts +17 -0
  161. package/src/hooks/useActiveModifiers/index.ts +1 -0
  162. package/src/hooks/useActiveModifiers/useActiveModifiers.test.tsx +36 -0
  163. package/src/hooks/useActiveModifiers/useActiveModifiers.tsx +18 -0
  164. package/src/hooks/useControlledValue/index.ts +1 -0
  165. package/src/hooks/useControlledValue/useControlledValue.test.ts +68 -0
  166. package/src/hooks/useControlledValue/useControlledValue.ts +24 -0
  167. package/src/hooks/useDayEventHandlers/index.ts +1 -0
  168. package/src/hooks/useDayEventHandlers/useDayEventHandlers.test.tsx +213 -0
  169. package/src/hooks/useDayEventHandlers/useDayEventHandlers.tsx +195 -0
  170. package/src/hooks/useDayRender/index.ts +1 -0
  171. package/src/hooks/useDayRender/useDayRender.test.tsx +304 -0
  172. package/src/hooks/useDayRender/useDayRender.tsx +123 -0
  173. package/src/hooks/useDayRender/utils/getDayClassNames.test.ts +63 -0
  174. package/src/hooks/useDayRender/utils/getDayClassNames.ts +32 -0
  175. package/src/hooks/useDayRender/utils/getDayStyle.ts +19 -0
  176. package/src/hooks/useInput/index.ts +1 -0
  177. package/src/hooks/useInput/useInput.ts +175 -0
  178. package/src/hooks/useInput/utils/isValidDate.tsx +4 -0
  179. package/src/hooks/useSelectedDays/index.ts +1 -0
  180. package/src/hooks/useSelectedDays/useSelectedDays.test.ts +72 -0
  181. package/src/hooks/useSelectedDays/useSelectedDays.ts +32 -0
  182. package/src/index.ts +43 -0
  183. package/src/style.css +310 -0
  184. package/src/style.css.d.ts +38 -0
  185. package/src/types/DayPickerBase.ts +267 -0
  186. package/src/types/DayPickerDefault.ts +15 -0
  187. package/src/types/DayPickerMultiple.ts +26 -0
  188. package/src/types/DayPickerRange.ts +27 -0
  189. package/src/types/DayPickerSingle.ts +24 -0
  190. package/src/types/EventHandlers.ts +87 -0
  191. package/src/types/Formatters.ts +29 -0
  192. package/src/types/Labels.ts +36 -0
  193. package/src/types/Matchers.ts +106 -0
  194. package/src/types/Modifiers.ts +62 -0
  195. package/src/types/Styles.ts +108 -0
  196. package/tsconfig.json +24 -0
@@ -0,0 +1,282 @@
1
+ import { RenderResult } from '@testing-library/react-hooks';
2
+ import {
3
+ addDays,
4
+ addMonths,
5
+ differenceInCalendarDays,
6
+ subDays
7
+ } from 'date-fns';
8
+
9
+ import { customRenderHook } from 'test/render/customRenderHook';
10
+ import { freezeBeforeAll } from 'test/utils';
11
+
12
+ import { isMatch } from 'contexts/Modifiers/utils/isMatch';
13
+ import { DayPickerBase } from 'types/DayPickerBase';
14
+ import { DayPickerRangeProps } from 'types/DayPickerRange';
15
+ import { ActiveModifiers } from 'types/Modifiers';
16
+
17
+ import { SelectRangeContextValue } from './SelectRangeContext';
18
+ import { useSelectRange } from './useSelectRange';
19
+
20
+ const today = new Date(2021, 11, 8);
21
+ freezeBeforeAll(today);
22
+
23
+ let result: RenderResult<SelectRangeContextValue>;
24
+ function setup(dayPickerProps?: DayPickerBase) {
25
+ const view = customRenderHook(useSelectRange, dayPickerProps);
26
+ result = view.result;
27
+ }
28
+
29
+ describe('when is not a multiple select DayPicker', () => {
30
+ beforeAll(() => {
31
+ setup();
32
+ });
33
+ test('the selected day should be undefined', () => {
34
+ expect(result.current.selected).toBeUndefined();
35
+ });
36
+ });
37
+
38
+ const initialProps: DayPickerRangeProps = {
39
+ mode: 'range',
40
+ onDayClick: jest.fn(),
41
+ onSelect: jest.fn()
42
+ };
43
+
44
+ const from = today;
45
+ const to = addDays(today, 6);
46
+ const stubEvent = {} as React.MouseEvent;
47
+
48
+ describe('when no days are selected', () => {
49
+ beforeAll(() => {
50
+ setup(initialProps);
51
+ });
52
+ test('the selected days should be undefined', () => {
53
+ expect(result.current.selected).toBeUndefined();
54
+ });
55
+ describe('when "onDayClick" is called', () => {
56
+ const day = from;
57
+ const activeModifiers = {};
58
+ beforeAll(() => {
59
+ result.current.onDayClick?.(day, activeModifiers, stubEvent);
60
+ });
61
+ afterAll(() => {
62
+ jest.resetAllMocks();
63
+ });
64
+ test('should call the "onDayClick" from the DayPicker props', () => {
65
+ expect(initialProps.onDayClick).toHaveBeenCalledWith(
66
+ day,
67
+ activeModifiers,
68
+ stubEvent
69
+ );
70
+ });
71
+ test('should call "onSelect" with the clicked day as the "from" propx', () => {
72
+ expect(initialProps.onSelect).toHaveBeenCalledWith(
73
+ { from: day, to: undefined },
74
+ day,
75
+ activeModifiers,
76
+ stubEvent
77
+ );
78
+ });
79
+ });
80
+ });
81
+
82
+ describe('when only the "from" day is selected', () => {
83
+ const selected = { from, to: undefined };
84
+ const dayPickerProps: DayPickerRangeProps = {
85
+ ...initialProps,
86
+ selected
87
+ };
88
+ beforeAll(() => {
89
+ setup(dayPickerProps);
90
+ });
91
+ test('should return the "range_start" modifiers with the "from" day', () => {
92
+ expect(result.current.modifiers.range_start).toEqual([from]);
93
+ });
94
+ test('should return the "range_end" modifiers with the "from" day', () => {
95
+ expect(result.current.modifiers.range_end).toEqual([from]);
96
+ });
97
+ test('should not return any "range_middle" modifiers', () => {
98
+ expect(result.current.modifiers.range_middle).toEqual([]);
99
+ });
100
+ });
101
+
102
+ describe('when a complete range of days is selected', () => {
103
+ const selected = { from, to };
104
+ const dayPickerProps: DayPickerRangeProps = {
105
+ ...initialProps,
106
+ selected
107
+ };
108
+ beforeAll(() => {
109
+ setup(dayPickerProps);
110
+ });
111
+ test('should return the "range_start" modifiers with the "from" day', () => {
112
+ expect(result.current.modifiers.range_start).toEqual([from]);
113
+ });
114
+ test('should return the "range_end" modifiers with the "to" day', () => {
115
+ expect(result.current.modifiers.range_end).toEqual([to]);
116
+ });
117
+ test('should return the "range_middle" range modifiers', () => {
118
+ expect(result.current.modifiers.range_middle).toEqual([
119
+ { after: from, before: to }
120
+ ]);
121
+ });
122
+ describe('when "onDayClick" is called with the day before the from day', () => {
123
+ const day = addDays(from, -1);
124
+ const activeModifiers = {};
125
+
126
+ beforeAll(() => {
127
+ result.current.onDayClick?.(day, activeModifiers, stubEvent);
128
+ });
129
+ afterAll(() => {
130
+ jest.resetAllMocks();
131
+ });
132
+ test('should call the "onDayClick" from the DayPicker props', () => {
133
+ expect(dayPickerProps.onDayClick).toHaveBeenCalledWith(
134
+ day,
135
+ activeModifiers,
136
+ stubEvent
137
+ );
138
+ });
139
+ test('should call "onSelect" with the day selected', () => {
140
+ expect(dayPickerProps.onSelect).toHaveBeenCalledWith(
141
+ { from: day, to },
142
+ day,
143
+ activeModifiers,
144
+ stubEvent
145
+ );
146
+ });
147
+ });
148
+ });
149
+
150
+ describe('when the max number of the selected days is reached', () => {
151
+ const selected = { from, to };
152
+ const dayPickerProps: DayPickerRangeProps = {
153
+ ...initialProps,
154
+ selected,
155
+ max: Math.abs(differenceInCalendarDays(to, from))
156
+ };
157
+ beforeAll(() => {
158
+ setup(dayPickerProps);
159
+ });
160
+ test('the days in the range should not be disabled', () => {
161
+ const { disabled } = result.current.modifiers;
162
+ expect(isMatch(from, disabled)).toBe(false);
163
+ expect(isMatch(to, disabled)).toBe(false);
164
+ });
165
+ test('the other days should be disabled', () => {
166
+ const { disabled } = result.current.modifiers;
167
+ expect(isMatch(addMonths(from, 1), disabled)).toBe(true);
168
+ });
169
+ describe('when "onDayClick" is called with a new day', () => {
170
+ const day = addMonths(from, 1);
171
+ const activeModifiers: ActiveModifiers = {};
172
+
173
+ beforeAll(() => {
174
+ result.current.onDayClick?.(day, activeModifiers, stubEvent);
175
+ });
176
+ afterAll(() => {
177
+ jest.resetAllMocks();
178
+ });
179
+ test('should call the "onDayClick" from the DayPicker props', () => {
180
+ expect(dayPickerProps.onDayClick).toHaveBeenCalledWith(
181
+ day,
182
+ activeModifiers,
183
+ stubEvent
184
+ );
185
+ });
186
+ test('should not call onSelect', () => {
187
+ expect(dayPickerProps.onSelect).not.toHaveBeenCalled();
188
+ });
189
+ });
190
+ });
191
+
192
+ describe('when the minimum number of days are selected', () => {
193
+ const selected = { from, to };
194
+ const dayPickerProps: DayPickerRangeProps = {
195
+ ...initialProps,
196
+ selected,
197
+ min: Math.abs(differenceInCalendarDays(to, from))
198
+ };
199
+ beforeAll(() => {
200
+ setup(dayPickerProps);
201
+ });
202
+ describe('when "onDayClick" is called with a day before "from"', () => {
203
+ const day = subDays(from, 1);
204
+ const activeModifiers: ActiveModifiers = { selected: true };
205
+
206
+ beforeAll(() => {
207
+ result.current.onDayClick?.(day, activeModifiers, stubEvent);
208
+ });
209
+ afterAll(() => {
210
+ jest.resetAllMocks();
211
+ });
212
+ test('should call "onSelect" with the day included in the range', () => {
213
+ expect(dayPickerProps.onSelect).toHaveBeenCalledWith(
214
+ { from: day, to },
215
+ day,
216
+ activeModifiers,
217
+ stubEvent
218
+ );
219
+ });
220
+ });
221
+ describe('when "onDayClick" is called with the "from" day', () => {
222
+ const day = from;
223
+ const activeModifiers: ActiveModifiers = { selected: true };
224
+ beforeAll(() => {
225
+ result.current.onDayClick?.(day, activeModifiers, stubEvent);
226
+ });
227
+ afterAll(() => {
228
+ jest.resetAllMocks();
229
+ });
230
+
231
+ test('should call the "onDayClick" from the DayPicker props', () => {
232
+ expect(dayPickerProps.onDayClick).toHaveBeenCalledWith(
233
+ day,
234
+ activeModifiers,
235
+ stubEvent
236
+ );
237
+ });
238
+ test('should call "onSelect" with an undefined range', () => {
239
+ expect(dayPickerProps.onSelect).toHaveBeenCalledWith(
240
+ undefined,
241
+ day,
242
+ activeModifiers,
243
+ stubEvent
244
+ );
245
+ });
246
+ });
247
+
248
+ describe('when "onDayClick" is called with the "to" day', () => {
249
+ const day = to;
250
+ const activeModifiers: ActiveModifiers = { selected: true };
251
+ beforeAll(() => {
252
+ result.current.onDayClick?.(day, activeModifiers, stubEvent);
253
+ });
254
+ afterAll(() => {
255
+ jest.resetAllMocks();
256
+ });
257
+
258
+ test('should call "onSelect" without the "to" in the range', () => {
259
+ expect(dayPickerProps.onSelect).toHaveBeenCalledWith(
260
+ { from: day, to: undefined },
261
+ day,
262
+ activeModifiers,
263
+ stubEvent
264
+ );
265
+ });
266
+ });
267
+
268
+ describe('when "onDayClick" is called with a day before "to"', () => {
269
+ const day = subDays(to, 1);
270
+ const activeModifiers: ActiveModifiers = { selected: true };
271
+
272
+ beforeAll(() => {
273
+ result.current.onDayClick?.(day, activeModifiers, stubEvent);
274
+ });
275
+ afterAll(() => {
276
+ jest.resetAllMocks();
277
+ });
278
+ test('should not call "onSelect"', () => {
279
+ expect(dayPickerProps.onSelect).not.toHaveBeenCalled();
280
+ });
281
+ });
282
+ });
@@ -0,0 +1,15 @@
1
+ import { useContext } from 'react';
2
+
3
+ import {
4
+ SelectRangeContext,
5
+ SelectRangeContextValue
6
+ } from './SelectRangeContext';
7
+
8
+ /** Hook to access the [[SelectRangeContext]]. */
9
+ export function useSelectRange(): SelectRangeContextValue {
10
+ const context = useContext(SelectRangeContext);
11
+ if (!context) {
12
+ throw new Error('useSelectRange must be used within a SelectRangeProvider');
13
+ }
14
+ return context;
15
+ }
@@ -0,0 +1,119 @@
1
+ import { addDays, subDays } from 'date-fns';
2
+
3
+ import { DateRange } from 'types/Matchers';
4
+
5
+ import { addToRange } from './addToRange';
6
+
7
+ describe('when no "from" is the range', () => {
8
+ const range = { from: undefined };
9
+ const day = new Date();
10
+ let result: DateRange | undefined;
11
+ beforeAll(() => {
12
+ result = addToRange(day, range);
13
+ });
14
+ test('should set "from" as the given day', () => {
15
+ expect(result).toEqual({ from: day, to: undefined });
16
+ });
17
+ });
18
+
19
+ describe('when no "to" is the range', () => {
20
+ const day = new Date();
21
+ const range = { from: day, to: undefined };
22
+ describe('and the day is the same as the "from" day', () => {
23
+ let result: DateRange | undefined;
24
+ beforeAll(() => {
25
+ result = addToRange(day, range);
26
+ });
27
+ test('should return it in the range', () => {
28
+ expect(result).toEqual({ from: day, to: day });
29
+ });
30
+ });
31
+ describe('and the day is before "from" day', () => {
32
+ const day = subDays(range.from, 1);
33
+ let result: DateRange | undefined;
34
+ beforeAll(() => {
35
+ result = addToRange(day, range);
36
+ });
37
+ test('should set the day as the "from" range', () => {
38
+ expect(result).toEqual({ from: day, to: range.from });
39
+ });
40
+ });
41
+ describe('and the day is after the "from" day', () => {
42
+ const day = addDays(range.from, 1);
43
+ let result: DateRange | undefined;
44
+ beforeAll(() => {
45
+ result = addToRange(day, range);
46
+ });
47
+ test('should set the day as the "to" date', () => {
48
+ expect(result).toEqual({ from: range.from, to: day });
49
+ });
50
+ });
51
+ });
52
+
53
+ describe('when "from", "to" and "day" are the same', () => {
54
+ const day = new Date();
55
+ const range = { from: day, to: day };
56
+ let result: DateRange | undefined;
57
+ beforeAll(() => {
58
+ result = addToRange(day, range);
59
+ });
60
+ test('should return an undefined range (reset)', () => {
61
+ expect(result).toBeUndefined();
62
+ });
63
+ });
64
+
65
+ describe('when "to" and "day" are the same', () => {
66
+ const from = new Date();
67
+ const to = addDays(from, 4);
68
+ const day = to;
69
+ const range = { from, to };
70
+ let result: DateRange | undefined;
71
+ beforeAll(() => {
72
+ result = addToRange(day, range);
73
+ });
74
+ test('should set "to" to undefined', () => {
75
+ expect(result).toEqual({ from: to, to: undefined });
76
+ });
77
+ });
78
+
79
+ describe('when "from" and "day" are the same', () => {
80
+ const from = new Date();
81
+ const to = addDays(from, 4);
82
+ const day = from;
83
+ const range = { from, to };
84
+ let result: DateRange | undefined;
85
+ beforeAll(() => {
86
+ result = addToRange(day, range);
87
+ });
88
+ test('should return an undefined range (reset)', () => {
89
+ expect(result).toBeUndefined();
90
+ });
91
+ });
92
+
93
+ describe('when "from" is after "day"', () => {
94
+ const day = new Date();
95
+ const from = addDays(day, 1);
96
+ const to = addDays(from, 4);
97
+ const range = { from, to };
98
+ let result: DateRange | undefined;
99
+ beforeAll(() => {
100
+ result = addToRange(day, range);
101
+ });
102
+ test('should set the day as "from"', () => {
103
+ expect(result).toEqual({ from: day, to: range.to });
104
+ });
105
+ });
106
+
107
+ describe('when "from" is before "day"', () => {
108
+ const day = new Date();
109
+ const from = subDays(day, 1);
110
+ const to = addDays(from, 4);
111
+ const range = { from, to };
112
+ let result: DateRange | undefined;
113
+ beforeAll(() => {
114
+ result = addToRange(day, range);
115
+ });
116
+ test('should set the day as "to"', () => {
117
+ expect(result).toEqual({ from: range.from, to: day });
118
+ });
119
+ });
@@ -0,0 +1,43 @@
1
+ import isAfter from 'date-fns/isAfter';
2
+ import isBefore from 'date-fns/isBefore';
3
+ import isSameDay from 'date-fns/isSameDay';
4
+
5
+ import { DateRange } from 'types/Matchers';
6
+
7
+ /**
8
+ * Add a day to an existing range.
9
+ *
10
+ * The returned range takes in account the `undefined` values and if the added
11
+ * day is already present in the range.
12
+ */
13
+ export function addToRange(
14
+ day: Date,
15
+ range?: DateRange
16
+ ): DateRange | undefined {
17
+ const { from, to } = range || {};
18
+ if (!from) {
19
+ return { from: day, to: undefined };
20
+ }
21
+ if (!to && isSameDay(from, day)) {
22
+ return { from: from, to: day };
23
+ }
24
+ if (!to && isBefore(day, from)) {
25
+ return { from: day, to: from };
26
+ }
27
+ if (!to) {
28
+ return { from, to: day };
29
+ }
30
+ if (isSameDay(to, day) && isSameDay(from, day)) {
31
+ return undefined;
32
+ }
33
+ if (isSameDay(to, day)) {
34
+ return { from: to, to: undefined };
35
+ }
36
+ if (isSameDay(from, day)) {
37
+ return undefined;
38
+ }
39
+ if (isAfter(from, day)) {
40
+ return { from: day, to };
41
+ }
42
+ return { from, to: day };
43
+ }
@@ -0,0 +1,80 @@
1
+ import React, { createContext } from 'react';
2
+
3
+ import { DayPickerBase } from 'types/DayPickerBase';
4
+ import { DayPickerSingleProps, isDayPickerSingle } from 'types/DayPickerSingle';
5
+ import { DayClickEventHandler } from 'types/EventHandlers';
6
+
7
+ /** Represents the value of a [[SelectSingleContext]]. */
8
+ export interface SelectSingleContextValue {
9
+ /** The day that has been selected. */
10
+ selected: Date | undefined;
11
+ /** Event handler to attach to the day button to enable the single select. */
12
+ onDayClick?: DayClickEventHandler;
13
+ }
14
+
15
+ /**
16
+ * The SelectSingle context shares details about the selected days when in
17
+ * single selection mode.
18
+ *
19
+ * Access this context from the [[useSelectSingle]] hook.
20
+ */
21
+ export const SelectSingleContext = createContext<
22
+ SelectSingleContextValue | undefined
23
+ >(undefined);
24
+
25
+ type SelectSingleProviderProps = {
26
+ initialProps: DayPickerBase;
27
+ children: React.ReactNode;
28
+ };
29
+
30
+ /** Provides the values for the [[SelectSingleProvider]]. */
31
+ export function SelectSingleProvider(
32
+ props: SelectSingleProviderProps
33
+ ): JSX.Element {
34
+ if (!isDayPickerSingle(props.initialProps)) {
35
+ const emptyContextValue: SelectSingleContextValue = {
36
+ selected: undefined
37
+ };
38
+ return (
39
+ <SelectSingleContext.Provider value={emptyContextValue}>
40
+ {props.children}
41
+ </SelectSingleContext.Provider>
42
+ );
43
+ }
44
+ return (
45
+ <SelectSingleProviderInternal
46
+ initialProps={props.initialProps}
47
+ children={props.children}
48
+ />
49
+ );
50
+ }
51
+
52
+ type SelectSingleProviderInternal = {
53
+ initialProps: DayPickerSingleProps;
54
+ children: React.ReactNode;
55
+ };
56
+
57
+ export function SelectSingleProviderInternal({
58
+ initialProps,
59
+ children
60
+ }: SelectSingleProviderInternal): JSX.Element {
61
+ const onDayClick: DayClickEventHandler = (day, activeModifiers, e) => {
62
+ initialProps.onDayClick?.(day, activeModifiers, e);
63
+
64
+ if (activeModifiers.selected && !initialProps.required) {
65
+ initialProps.onSelect?.(undefined, day, activeModifiers, e);
66
+ return;
67
+ }
68
+ initialProps.onSelect?.(day, day, activeModifiers, e);
69
+ };
70
+
71
+ const contextValue: SelectSingleContextValue = {
72
+ selected: initialProps.selected,
73
+ onDayClick
74
+ };
75
+ return (
76
+ <SelectSingleContext.Provider value={contextValue}>
77
+ {children}
78
+ </SelectSingleContext.Provider>
79
+ );
80
+ }
@@ -0,0 +1,2 @@
1
+ export * from './SelectSingleContext';
2
+ export * from './useSelectSingle';
@@ -0,0 +1,81 @@
1
+ import React from 'react';
2
+
3
+ import { customRenderHook } from 'test/render/customRenderHook';
4
+ import { freezeBeforeAll } from 'test/utils';
5
+
6
+ import { DayPickerBase } from 'types/DayPickerBase';
7
+ import { DayPickerSingleProps } from 'types/DayPickerSingle';
8
+ import { ActiveModifiers } from 'types/Modifiers';
9
+
10
+ import { useSelectSingle } from './useSelectSingle';
11
+
12
+ const today = new Date(2021, 11, 8);
13
+ freezeBeforeAll(today);
14
+
15
+ function setup(dayPickerProps?: DayPickerBase) {
16
+ const { result } = customRenderHook(() => useSelectSingle(), dayPickerProps);
17
+ return result;
18
+ }
19
+
20
+ describe('when is not a single select DayPicker', () => {
21
+ const result = setup();
22
+ test('the selected day should be undefined', () => {
23
+ expect(result.current.selected).toBeUndefined();
24
+ });
25
+ });
26
+
27
+ describe('when a day is selected from DayPicker props', () => {
28
+ test('the selected day should be today', () => {
29
+ const dayPickerProps: DayPickerSingleProps = {
30
+ mode: 'single',
31
+ selected: today
32
+ };
33
+ const result = setup(dayPickerProps);
34
+ expect(result.current.selected).toBe(today);
35
+ });
36
+ });
37
+ describe('when onDayClick is called', () => {
38
+ const dayPickerProps: DayPickerSingleProps = {
39
+ mode: 'single',
40
+ onSelect: jest.fn(),
41
+ onDayClick: jest.fn()
42
+ };
43
+ const result = setup(dayPickerProps);
44
+ const activeModifiers = {};
45
+ const event = {} as React.MouseEvent;
46
+ result.current.onDayClick?.(today, activeModifiers, event);
47
+ test('should call the `onSelect` event handler', () => {
48
+ expect(dayPickerProps.onSelect).toHaveBeenCalledWith(
49
+ today,
50
+ today,
51
+ activeModifiers,
52
+ event
53
+ );
54
+ });
55
+ test('should call the `onDayClick` event handler', () => {
56
+ expect(dayPickerProps.onDayClick).toHaveBeenCalledWith(
57
+ today,
58
+ activeModifiers,
59
+ event
60
+ );
61
+ });
62
+ });
63
+ describe('if a selected day is not required', () => {
64
+ const dayPickerProps: DayPickerSingleProps = {
65
+ mode: 'single',
66
+ onSelect: jest.fn(),
67
+ required: false
68
+ };
69
+ const result = setup(dayPickerProps);
70
+ const activeModifiers: ActiveModifiers = { selected: true };
71
+ const event = {} as React.MouseEvent;
72
+ result.current.onDayClick?.(today, activeModifiers, event);
73
+ test('should call the `onSelect` event handler with an undefined day', () => {
74
+ expect(dayPickerProps.onSelect).toHaveBeenCalledWith(
75
+ undefined,
76
+ today,
77
+ activeModifiers,
78
+ event
79
+ );
80
+ });
81
+ });
@@ -0,0 +1,17 @@
1
+ import { useContext } from 'react';
2
+
3
+ import {
4
+ SelectSingleContext,
5
+ SelectSingleContextValue
6
+ } from './SelectSingleContext';
7
+
8
+ /** Hook to access the [[SelectSingleContext]]. */
9
+ export function useSelectSingle(): SelectSingleContextValue {
10
+ const context = useContext(SelectSingleContext);
11
+ if (!context) {
12
+ throw new Error(
13
+ 'useSelectSingle must be used within a SelectSingleProvider'
14
+ );
15
+ }
16
+ return context;
17
+ }
@@ -0,0 +1 @@
1
+ export * from './useActiveModifiers';
@@ -0,0 +1,36 @@
1
+ import { RenderResult } from '@testing-library/react-hooks';
2
+ import { addMonths } from 'date-fns';
3
+
4
+ import { customRenderHook } from 'test/render';
5
+
6
+ import { ActiveModifiers } from 'types/Modifiers';
7
+
8
+ import { useActiveModifiers } from './useActiveModifiers';
9
+
10
+ const date = new Date(2010, 5, 23);
11
+
12
+ let renderResult: RenderResult<ActiveModifiers>;
13
+ function setup(day: Date, displayMonth?: Date) {
14
+ const view = customRenderHook(() => useActiveModifiers(day, displayMonth));
15
+ renderResult = view.result;
16
+ }
17
+
18
+ describe('when in the same month', () => {
19
+ const displayMonth = date;
20
+ beforeEach(() => {
21
+ setup(date, displayMonth);
22
+ });
23
+ test('should return the active modifiers', () => {
24
+ expect(renderResult.current).toBeDefined();
25
+ });
26
+ });
27
+
28
+ describe('when not in the same display month', () => {
29
+ const displayMonth = addMonths(date, 1);
30
+ beforeEach(() => {
31
+ setup(date, displayMonth);
32
+ });
33
+ test('should return the outside modifier', () => {
34
+ expect(renderResult.current.outside).toBe(true);
35
+ });
36
+ });
@@ -0,0 +1,18 @@
1
+ import { getActiveModifiers, useModifiers } from 'contexts/Modifiers';
2
+ import { ActiveModifiers } from 'types/Modifiers';
3
+
4
+ /**
5
+ * Return the active modifiers for the specified day.
6
+ *
7
+ * @param day
8
+ * @param displayMonth The month where the date is displayed. If not the same as
9
+ * `date`, the day is an "outside day".
10
+ */
11
+ export function useActiveModifiers(
12
+ day: Date,
13
+ displayMonth?: Date
14
+ ): ActiveModifiers {
15
+ const modifiers = useModifiers();
16
+ const activeModifiers = getActiveModifiers(day, modifiers, displayMonth);
17
+ return activeModifiers;
18
+ }