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,304 @@
1
+ import { createRef } from 'react';
2
+
3
+ import { RenderResult } from '@testing-library/react-hooks';
4
+ import { addDays, addMonths } from 'date-fns';
5
+ import { DayPickerProps } from 'DayPicker';
6
+
7
+ import { customRenderHook, CustomRenderHookContexts } from 'test/render';
8
+ import { freezeBeforeAll } from 'test/utils';
9
+
10
+ import { defaultClassNames } from 'contexts/DayPicker/defaultClassNames';
11
+ import { FocusContextValue } from 'contexts/Focus';
12
+ import { EventName } from 'hooks/useDayEventHandlers';
13
+
14
+ import { DayRender } from './';
15
+ import { useDayRender } from './useDayRender';
16
+
17
+ const today = new Date(2022, 5, 13);
18
+
19
+ freezeBeforeAll(today);
20
+
21
+ let result: RenderResult<DayRender>;
22
+
23
+ const mockedFocusContext: FocusContextValue = {
24
+ focus: jest.fn(),
25
+ focusedDay: undefined,
26
+ focusTarget: undefined,
27
+ blur: jest.fn(),
28
+ focusDayAfter: jest.fn(),
29
+ focusDayBefore: jest.fn(),
30
+ focusWeekBefore: jest.fn(),
31
+ focusWeekAfter: jest.fn(),
32
+ focusMonthBefore: jest.fn(),
33
+ focusMonthAfter: jest.fn(),
34
+ focusYearBefore: jest.fn(),
35
+ focusYearAfter: jest.fn(),
36
+ focusStartOfWeek: jest.fn(),
37
+ focusEndOfWeek: jest.fn()
38
+ };
39
+
40
+ function setup(
41
+ date: Date,
42
+ displayMonth: Date,
43
+ dayPickerProps?: DayPickerProps,
44
+ contexts?: CustomRenderHookContexts
45
+ ) {
46
+ const buttonRef = createRef<HTMLButtonElement>();
47
+ const view = customRenderHook(
48
+ () => useDayRender(date, displayMonth, buttonRef),
49
+ dayPickerProps,
50
+ contexts
51
+ );
52
+ result = view.result;
53
+ }
54
+
55
+ describe('when rendering the today’s date', () => {
56
+ const date = today;
57
+ const displayMonth = date;
58
+ beforeEach(() => {
59
+ setup(date, displayMonth);
60
+ });
61
+ test('the div should include the default class name', () => {
62
+ expect(result.current.divProps.className?.split(' ')).toContain(
63
+ defaultClassNames.day
64
+ );
65
+ });
66
+ test('the button should include the default class name', () => {
67
+ expect(result.current.buttonProps.className?.split(' ')).toContain(
68
+ defaultClassNames.day
69
+ );
70
+ });
71
+ test('the button should not have "aria-pressed"', () => {
72
+ expect(result.current.buttonProps['aria-pressed']).toBeUndefined();
73
+ });
74
+ test('the button should have 0 as "tabIndex"', () => {
75
+ expect(result.current.buttonProps.tabIndex).toBe(0);
76
+ });
77
+
78
+ const testEvents: EventName[] = [
79
+ 'onClick',
80
+ 'onFocus',
81
+ 'onBlur',
82
+ 'onKeyDown',
83
+ 'onKeyUp',
84
+ 'onMouseEnter',
85
+ 'onMouseLeave',
86
+ 'onTouchCancel',
87
+ 'onTouchEnd',
88
+ 'onTouchMove',
89
+ 'onTouchStart'
90
+ ];
91
+ test.each(testEvents)(
92
+ 'the button should have the "%s" event handler',
93
+ (eventName) => {
94
+ expect(result.current.buttonProps[eventName]).toBeDefined();
95
+ }
96
+ );
97
+ test('should return the day active modifiers', () => {
98
+ expect(result.current.activeModifiers).toEqual({ today: true });
99
+ });
100
+ });
101
+
102
+ describe('when not in selection mode', () => {
103
+ const dayPickerProps = { mode: undefined };
104
+ beforeEach(() => {
105
+ setup(today, today, dayPickerProps);
106
+ });
107
+ test('should not be a button', () => {
108
+ expect(result.current.isButton).toBe(false);
109
+ });
110
+ });
111
+ describe('when "onDayClick" is not passed in', () => {
112
+ const dayPickerProps = { onDayClick: undefined };
113
+ beforeEach(() => {
114
+ setup(today, today, dayPickerProps);
115
+ });
116
+ test('should not be a button', () => {
117
+ expect(result.current.isButton).toBe(false);
118
+ });
119
+ });
120
+ describe('when in selection mode', () => {
121
+ const dayPickerProps: DayPickerProps = { mode: 'single' };
122
+ beforeEach(() => {
123
+ setup(today, today, dayPickerProps);
124
+ });
125
+ test('should be a button', () => {
126
+ expect(result.current.isButton).toBe(true);
127
+ });
128
+ });
129
+
130
+ describe('when "onDayClick" is passed in', () => {
131
+ const dayPickerProps: DayPickerProps = { onDayClick: jest.fn() };
132
+ beforeEach(() => {
133
+ setup(today, today, dayPickerProps);
134
+ });
135
+ test('should be a button', () => {
136
+ expect(result.current.isButton).toBe(true);
137
+ });
138
+ });
139
+
140
+ describe('when showing the outside days', () => {
141
+ const dayPickerProps: DayPickerProps = { showOutsideDays: false };
142
+ describe('when the day is outside', () => {
143
+ const day = today;
144
+ const displayMonth = addMonths(today, 1);
145
+ beforeEach(() => {
146
+ setup(day, displayMonth, dayPickerProps);
147
+ });
148
+ test('should be hidden', () => {
149
+ expect(result.current.isHidden).toBe(true);
150
+ });
151
+ });
152
+ });
153
+
154
+ describe('when the day has the "hidden" modifier active', () => {
155
+ const date = today;
156
+ const dayPickerProps: DayPickerProps = {
157
+ modifiers: { hidden: date }
158
+ };
159
+ beforeEach(() => {
160
+ setup(date, date, dayPickerProps);
161
+ });
162
+ test('should have the hidden modifier active', () => {
163
+ expect(result.current.activeModifiers.hidden).toBe(true);
164
+ });
165
+ test('should be hidden', () => {
166
+ expect(result.current.isHidden).toBe(true);
167
+ });
168
+ });
169
+
170
+ describe('when "modifiersStyles" is passed in', () => {
171
+ const date = today;
172
+ const dayPickerProps = {
173
+ modifiers: { foo: date },
174
+ modifiersStyles: { foo: { color: 'red' } }
175
+ };
176
+ beforeEach(() => {
177
+ setup(date, date, dayPickerProps);
178
+ });
179
+ test('the div props should include the modifiers style', () => {
180
+ expect(result.current.divProps.style).toStrictEqual(
181
+ dayPickerProps.modifiersStyles.foo
182
+ );
183
+ });
184
+ test('the button props should include the modifiers style', () => {
185
+ expect(result.current.buttonProps.style).toStrictEqual(
186
+ dayPickerProps.modifiersStyles.foo
187
+ );
188
+ });
189
+ });
190
+ describe('when "styles.day" is passed in', () => {
191
+ const date = today;
192
+ const dayPickerProps = {
193
+ styles: { day: { color: 'red' } }
194
+ };
195
+ beforeEach(() => {
196
+ setup(date, date, dayPickerProps);
197
+ });
198
+ test('the div props should include the style', () => {
199
+ expect(result.current.divProps.style).toStrictEqual(
200
+ dayPickerProps.styles.day
201
+ );
202
+ });
203
+ test('the button props should include the style', () => {
204
+ expect(result.current.buttonProps.style).toStrictEqual(
205
+ dayPickerProps.styles.day
206
+ );
207
+ });
208
+ });
209
+
210
+ describe('when "modifiersClassNames" is passed in', () => {
211
+ const date = today;
212
+ const dayPickerProps = {
213
+ modifiers: { foo: date },
214
+ modifiersClassNames: { foo: 'bar' }
215
+ };
216
+ beforeEach(() => {
217
+ setup(date, date, dayPickerProps);
218
+ });
219
+ test('the div props should include the modifiers classNames', () => {
220
+ expect(result.current.divProps.className).toContain(
221
+ dayPickerProps.modifiersClassNames.foo
222
+ );
223
+ });
224
+ test('the button props should include the modifiers classNames', () => {
225
+ expect(result.current.buttonProps.className).toContain(
226
+ dayPickerProps.modifiersClassNames.foo
227
+ );
228
+ });
229
+ });
230
+
231
+ describe('when "classNames.day" is passed in', () => {
232
+ const date = today;
233
+ const dayPickerProps = {
234
+ classNames: { day: 'foo' }
235
+ };
236
+ beforeEach(() => {
237
+ setup(date, date, dayPickerProps);
238
+ });
239
+ test('the div props should include the class name', () => {
240
+ expect(result.current.divProps.className).toContain(
241
+ dayPickerProps.classNames.day
242
+ );
243
+ });
244
+ test('the button props should include the class name', () => {
245
+ expect(result.current.buttonProps.className).toContain(
246
+ dayPickerProps.classNames.day
247
+ );
248
+ });
249
+ });
250
+
251
+ describe('when the day is not target of focus', () => {
252
+ const yesterday = addDays(today, -1);
253
+ const tomorrow = addDays(today, 1);
254
+ const focusContext: FocusContextValue = {
255
+ ...mockedFocusContext,
256
+ focusTarget: yesterday
257
+ };
258
+ beforeEach(() => {
259
+ setup(tomorrow, tomorrow, {}, { focus: focusContext });
260
+ });
261
+ test('the button should have tabIndex -1', () => {
262
+ expect(result.current.buttonProps.tabIndex).toBe(-1);
263
+ });
264
+ });
265
+
266
+ describe('when the day is target of focus', () => {
267
+ const date = today;
268
+ const focusContext: FocusContextValue = {
269
+ ...mockedFocusContext,
270
+ focusTarget: date
271
+ };
272
+ beforeEach(() => {
273
+ setup(date, date, {}, { focus: focusContext });
274
+ });
275
+ test('the button should have tabIndex 0', () => {
276
+ expect(result.current.buttonProps.tabIndex).toBe(0);
277
+ });
278
+ });
279
+
280
+ describe('when the day is disabled', () => {
281
+ const date = today;
282
+ const dayPickerProps = {
283
+ disabled: date
284
+ };
285
+ beforeEach(() => {
286
+ setup(date, date, dayPickerProps);
287
+ });
288
+ test('the button should be disabled', () => {
289
+ expect(result.current.buttonProps.disabled).toBe(true);
290
+ });
291
+ });
292
+
293
+ describe('when the day is selected', () => {
294
+ const date = today;
295
+ const dayPickerProps = {
296
+ selected: date
297
+ };
298
+ beforeEach(() => {
299
+ setup(date, date, dayPickerProps);
300
+ });
301
+ test('the button should have "aria-pressed"', () => {
302
+ expect(result.current.buttonProps['aria-pressed']).toBe(true);
303
+ });
304
+ });
@@ -0,0 +1,123 @@
1
+ import React, { useEffect } from 'react';
2
+
3
+ import isSameDay from 'date-fns/isSameDay';
4
+
5
+ import { ButtonProps } from 'components/Button';
6
+ import { DayContent } from 'components/DayContent';
7
+ import { useDayPicker } from 'contexts/DayPicker';
8
+ import { useFocusContext } from 'contexts/Focus';
9
+ import { useActiveModifiers } from 'hooks/useActiveModifiers';
10
+ import {
11
+ DayEventHandlers,
12
+ useDayEventHandlers
13
+ } from 'hooks/useDayEventHandlers';
14
+ import { SelectedDays, useSelectedDays } from 'hooks/useSelectedDays';
15
+ import { ActiveModifiers } from 'types/Modifiers';
16
+ import { StyledComponent } from 'types/Styles';
17
+
18
+ import { getDayClassNames } from './utils/getDayClassNames';
19
+ import { getDayStyle } from './utils/getDayStyle';
20
+
21
+ export type DayRender = {
22
+ /** Whether the day should be rendered a `button` instead of a `div` */
23
+ isButton: boolean;
24
+ /** Whether the day should be hidden. */
25
+ isHidden: boolean;
26
+ /** The modifiers active for the given day. */
27
+ activeModifiers: ActiveModifiers;
28
+ /** The props to apply to the button element (when `isButton` is true). */
29
+ buttonProps: StyledComponent &
30
+ Pick<ButtonProps, 'disabled' | 'aria-pressed' | 'tabIndex'> &
31
+ DayEventHandlers;
32
+ /** The props to apply to the div element (when `isButton` is false). */
33
+ divProps: StyledComponent;
34
+ selectedDays: SelectedDays;
35
+ };
36
+
37
+ /**
38
+ * Return props and data used to render the [[Day]] component.
39
+ *
40
+ * Use this hook when creating a component to replace the built-in `Day`
41
+ * component.
42
+ *
43
+ * Each Day in DayPicker should render one of the following, according to the return
44
+ * value:
45
+ *
46
+ * - an empty `React.Fragment`, to render if `isHidden` is true
47
+ * - a `button` element, when the day is interactive, e.g. is selectable
48
+ * - a `div` element, whe the day is not interactive
49
+ *
50
+ * @param day The date to render
51
+ * @param displayMonth The month where the date is displayed (if not the same as
52
+ * `date`, it means it is an "outside" day)
53
+ * @param buttonRef A ref to the button element that will be target of focus
54
+ * when rendered (if required).
55
+ */
56
+ export function useDayRender(
57
+ day: Date,
58
+ displayMonth: Date,
59
+ buttonRef: React.RefObject<HTMLButtonElement>
60
+ ): DayRender {
61
+ const dayPicker = useDayPicker();
62
+ const focusContext = useFocusContext();
63
+ const activeModifiers = useActiveModifiers(day, displayMonth);
64
+ const eventHandlers = useDayEventHandlers(day, activeModifiers);
65
+ const selectedDays = useSelectedDays();
66
+ const isButton = Boolean(
67
+ dayPicker.onDayClick || dayPicker.mode !== 'default'
68
+ );
69
+
70
+ // Focus the button if the day is focused according to the focus context
71
+ useEffect(() => {
72
+ if (!focusContext.focusedDay) return;
73
+ if (!isButton) return;
74
+ if (isSameDay(focusContext.focusedDay, day)) {
75
+ buttonRef.current?.focus();
76
+ }
77
+ }, [focusContext.focusedDay, day, buttonRef, isButton]);
78
+
79
+ const className = getDayClassNames(dayPicker, activeModifiers).join(' ');
80
+ const style = getDayStyle(dayPicker, activeModifiers);
81
+
82
+ const isHidden = Boolean(
83
+ (activeModifiers.outside && !dayPicker.showOutsideDays) ||
84
+ activeModifiers.hidden
85
+ );
86
+
87
+ const DayContentComponent = dayPicker.components?.DayContent ?? DayContent;
88
+ const children = (
89
+ <DayContentComponent
90
+ date={day}
91
+ displayMonth={displayMonth}
92
+ activeModifiers={activeModifiers}
93
+ />
94
+ );
95
+
96
+ const divProps = {
97
+ style,
98
+ className,
99
+ children
100
+ };
101
+
102
+ const isFocusTarget = Boolean(
103
+ focusContext.focusTarget && isSameDay(focusContext.focusTarget, day)
104
+ );
105
+ const buttonProps = {
106
+ ...divProps,
107
+ disabled: activeModifiers.disabled,
108
+ ['aria-pressed']: activeModifiers.selected,
109
+ tabIndex: isFocusTarget ? 0 : -1,
110
+ ...eventHandlers
111
+ };
112
+
113
+ const dayRender: DayRender = {
114
+ isButton,
115
+ isHidden,
116
+ activeModifiers: activeModifiers,
117
+ selectedDays,
118
+ buttonProps,
119
+ divProps
120
+ };
121
+
122
+ return dayRender;
123
+ }
@@ -0,0 +1,63 @@
1
+ import { DayPickerContextValue } from 'contexts/DayPicker';
2
+ import { defaultClassNames } from 'contexts/DayPicker/defaultClassNames';
3
+ import { ActiveModifiers, InternalModifier } from 'types/Modifiers';
4
+
5
+ import { getDayClassNames } from './getDayClassNames';
6
+
7
+ type DayPickerOptions = Pick<
8
+ DayPickerContextValue,
9
+ 'modifiersClassNames' | 'classNames'
10
+ >;
11
+
12
+ const internalModifiers = Object.values(InternalModifier);
13
+
14
+ test('should include the day class name', () => {
15
+ const dayPicker: DayPickerOptions = {
16
+ modifiersClassNames: {},
17
+ classNames: defaultClassNames
18
+ };
19
+ const activeModifiers: ActiveModifiers = {};
20
+ expect(getDayClassNames(dayPicker, activeModifiers)).toContain(
21
+ defaultClassNames.day
22
+ );
23
+ });
24
+
25
+ describe('when using "modifiersClassNames" for a custom modifier', () => {
26
+ const modifierClassName = `foo-class`;
27
+ const dayPicker: DayPickerOptions = {
28
+ modifiersClassNames: {
29
+ foo: modifierClassName
30
+ },
31
+ classNames: defaultClassNames
32
+ };
33
+ const activeModifiers: ActiveModifiers = { foo: true };
34
+ test('should return the custom class name for the modifier', () => {
35
+ expect(getDayClassNames(dayPicker, activeModifiers)).toContain(
36
+ modifierClassName
37
+ );
38
+ });
39
+ });
40
+
41
+ describe.each(internalModifiers)(
42
+ 'when using "modifiersClassNames" for the %s (internal) modifier',
43
+ (internalModifier) => {
44
+ const modifierClassName = `foo-${internalModifier}`;
45
+ const dayPicker: DayPickerOptions = {
46
+ modifiersClassNames: {
47
+ [internalModifier]: modifierClassName
48
+ },
49
+ classNames: defaultClassNames
50
+ };
51
+ const activeModifiers: ActiveModifiers = { [internalModifier]: true };
52
+ test('should return the custom class name for the modifier', () => {
53
+ expect(getDayClassNames(dayPicker, activeModifiers)).toContain(
54
+ modifierClassName
55
+ );
56
+ });
57
+ test('should not include the default class name for the modifier', () => {
58
+ expect(getDayClassNames(dayPicker, activeModifiers)).not.toContain(
59
+ defaultClassNames.day_selected
60
+ );
61
+ });
62
+ }
63
+ );
@@ -0,0 +1,32 @@
1
+ import { DayPickerContextValue } from 'contexts/DayPicker';
2
+ import { ActiveModifiers, InternalModifier } from 'types/Modifiers';
3
+
4
+ function isInternalModifier(modifier: string): modifier is InternalModifier {
5
+ return Object.values(InternalModifier).includes(modifier as InternalModifier);
6
+ }
7
+
8
+ /**
9
+ * Return the class names for the Day element, according to the given active
10
+ * modifiers.
11
+ *
12
+ * Custom class names are set via `modifiersClassNames` or `classNames`,
13
+ * where the first have the precedence.
14
+ */
15
+ export function getDayClassNames(
16
+ dayPicker: Pick<DayPickerContextValue, 'modifiersClassNames' | 'classNames'>,
17
+ activeModifiers: ActiveModifiers
18
+ ) {
19
+ const classNames: string[] = [dayPicker.classNames.day];
20
+ Object.keys(activeModifiers).forEach((modifier) => {
21
+ const customClassName = dayPicker.modifiersClassNames[modifier];
22
+ if (customClassName) {
23
+ classNames.push(customClassName);
24
+ } else if (isInternalModifier(modifier)) {
25
+ const internalClassName = dayPicker.classNames[`day_${modifier}`];
26
+ if (internalClassName) {
27
+ classNames.push(internalClassName);
28
+ }
29
+ }
30
+ });
31
+ return classNames;
32
+ }
@@ -0,0 +1,19 @@
1
+ import { DayPickerContextValue } from 'contexts/DayPicker';
2
+ import { ActiveModifiers } from 'types/Modifiers';
3
+
4
+ /** Return the style for the Day element, according to the given active modifiers. */
5
+ export function getDayStyle(
6
+ dayPicker: Pick<DayPickerContextValue, 'modifiersStyles' | 'styles'>,
7
+ activeModifiers: ActiveModifiers
8
+ ): React.CSSProperties {
9
+ let style: React.CSSProperties = {
10
+ ...dayPicker.styles.day
11
+ };
12
+ Object.keys(activeModifiers).forEach((modifier) => {
13
+ style = {
14
+ ...style,
15
+ ...dayPicker.modifiersStyles?.[modifier]
16
+ };
17
+ });
18
+ return style;
19
+ }
@@ -0,0 +1 @@
1
+ export * from './useInput';