react-day-picker 8.0.3 → 8.0.6

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 (207) hide show
  1. package/README.md +1 -1
  2. package/dist/hooks/useDayRender/useDayRender.d.ts +1 -1
  3. package/dist/hooks/useInput/useInput.d.ts +2 -2
  4. package/dist/index.esm.js +3 -3
  5. package/dist/index.esm.js.map +1 -1
  6. package/dist/index.js +3 -3
  7. package/dist/index.js.map +1 -1
  8. package/dist/react-day-picker.min.js +1 -0
  9. package/dist/style.css +11 -10
  10. package/dist/style.module.css +11 -10
  11. package/dist/types/Styles.d.ts +8 -2
  12. package/package.json +15 -13
  13. package/src/DayPicker.tsx +113 -0
  14. package/src/components/Button/Button.test.tsx +47 -0
  15. package/src/components/Button/Button.tsx +36 -0
  16. package/src/components/Button/index.ts +1 -0
  17. package/src/components/Caption/Caption.test.tsx +86 -0
  18. package/src/components/Caption/Caption.tsx +54 -0
  19. package/src/components/Caption/index.ts +1 -0
  20. package/src/components/CaptionDropdowns/CaptionDropdowns.test.tsx +123 -0
  21. package/src/components/CaptionDropdowns/CaptionDropdowns.tsx +43 -0
  22. package/src/components/CaptionDropdowns/index.ts +1 -0
  23. package/src/components/CaptionLabel/CaptionLabel.test.tsx +29 -0
  24. package/src/components/CaptionLabel/CaptionLabel.tsx +32 -0
  25. package/src/components/CaptionLabel/index.ts +1 -0
  26. package/src/components/CaptionNavigation/CaptionNavigation.test.tsx +172 -0
  27. package/src/components/CaptionNavigation/CaptionNavigation.tsx +63 -0
  28. package/src/components/CaptionNavigation/index.ts +1 -0
  29. package/src/components/Day/Day.test.tsx +84 -0
  30. package/src/components/Day/Day.tsx +30 -0
  31. package/src/components/Day/index.ts +1 -0
  32. package/src/components/DayContent/DayContent.test.tsx +51 -0
  33. package/src/components/DayContent/DayContent.tsx +36 -0
  34. package/src/components/DayContent/index.ts +1 -0
  35. package/src/components/Dropdown/Dropdown.test.tsx +73 -0
  36. package/src/components/Dropdown/Dropdown.tsx +56 -0
  37. package/src/components/Dropdown/index.ts +1 -0
  38. package/src/components/Footer/Footer.test.tsx +29 -0
  39. package/src/components/Footer/Footer.tsx +20 -0
  40. package/src/components/Footer/index.ts +1 -0
  41. package/src/components/Head/Head.test.tsx +117 -0
  42. package/src/components/Head/Head.tsx +51 -0
  43. package/src/components/Head/index.ts +1 -0
  44. package/src/components/Head/utils/getWeekdays.test.ts +36 -0
  45. package/src/components/Head/utils/getWeekdays.ts +22 -0
  46. package/src/components/Head/utils/index.ts +1 -0
  47. package/src/components/IconDropdown/IconDropdown.test.tsx +20 -0
  48. package/src/components/IconDropdown/IconDropdown.tsx +24 -0
  49. package/src/components/IconDropdown/index.ts +1 -0
  50. package/src/components/IconLeft/IconLeft.test.tsx +20 -0
  51. package/src/components/IconLeft/IconLeft.tsx +18 -0
  52. package/src/components/IconLeft/index.ts +1 -0
  53. package/src/components/IconRight/IconRight.test.tsx +20 -0
  54. package/src/components/IconRight/IconRight.tsx +17 -0
  55. package/src/components/IconRight/index.ts +1 -0
  56. package/src/components/Month/Month.test.tsx +216 -0
  57. package/src/components/Month/Month.tsx +53 -0
  58. package/src/components/Month/index.ts +1 -0
  59. package/src/components/MonthsDropdown/MonthsDropdown.test.tsx +99 -0
  60. package/src/components/MonthsDropdown/MonthsDropdown.tsx +75 -0
  61. package/src/components/MonthsDropdown/index.ts +1 -0
  62. package/src/components/Navigation/Navigation.test.tsx +129 -0
  63. package/src/components/Navigation/Navigation.tsx +102 -0
  64. package/src/components/Navigation/index.ts +1 -0
  65. package/src/components/Root/Root.test.tsx +123 -0
  66. package/src/components/Root/Root.tsx +58 -0
  67. package/src/components/Root/index.ts +1 -0
  68. package/src/components/Row/Row.test.tsx +69 -0
  69. package/src/components/Row/Row.tsx +51 -0
  70. package/src/components/Row/index.ts +1 -0
  71. package/src/components/Table/Table.test.tsx +42 -0
  72. package/src/components/Table/Table.tsx +60 -0
  73. package/src/components/Table/__snapshots__/Table.test.tsx.snap +1453 -0
  74. package/src/components/Table/index.ts +1 -0
  75. package/src/components/Table/utils/daysToMonthWeeks.ts +47 -0
  76. package/src/components/Table/utils/getMonthWeeks.test.ts +68 -0
  77. package/src/components/Table/utils/getMonthWeeks.ts +55 -0
  78. package/src/components/WeekNumber/WeekNumber.test.tsx +46 -0
  79. package/src/components/WeekNumber/WeekNumber.tsx +58 -0
  80. package/src/components/WeekNumber/__snapshots__/WeekNumber.test.tsx.snap +11 -0
  81. package/src/components/WeekNumber/index.ts +1 -0
  82. package/src/components/YearsDropdown/YearsDropdown.test.tsx +98 -0
  83. package/src/components/YearsDropdown/YearsDropdown.tsx +76 -0
  84. package/src/components/YearsDropdown/index.ts +1 -0
  85. package/src/contexts/DayPicker/DayPickerContext.tsx +156 -0
  86. package/src/contexts/DayPicker/defaultClassNames.ts +58 -0
  87. package/src/contexts/DayPicker/defaultContextValue.ts +37 -0
  88. package/src/contexts/DayPicker/formatters/formatCaption.test.ts +15 -0
  89. package/src/contexts/DayPicker/formatters/formatCaption.ts +12 -0
  90. package/src/contexts/DayPicker/formatters/formatDay.test.ts +7 -0
  91. package/src/contexts/DayPicker/formatters/formatDay.ts +9 -0
  92. package/src/contexts/DayPicker/formatters/formatMonthCaption.test.ts +15 -0
  93. package/src/contexts/DayPicker/formatters/formatMonthCaption.ts +12 -0
  94. package/src/contexts/DayPicker/formatters/formatWeekNumber.test.ts +5 -0
  95. package/src/contexts/DayPicker/formatters/formatWeekNumber.ts +6 -0
  96. package/src/contexts/DayPicker/formatters/formatWeekdayName.test.ts +15 -0
  97. package/src/contexts/DayPicker/formatters/formatWeekdayName.ts +12 -0
  98. package/src/contexts/DayPicker/formatters/formatYearCaption.test.ts +7 -0
  99. package/src/contexts/DayPicker/formatters/formatYearCaption.ts +11 -0
  100. package/src/contexts/DayPicker/formatters/index.ts +6 -0
  101. package/src/contexts/DayPicker/index.ts +2 -0
  102. package/src/contexts/DayPicker/labels/index.ts +7 -0
  103. package/src/contexts/DayPicker/labels/labelDay.test.ts +7 -0
  104. package/src/contexts/DayPicker/labels/labelDay.ts +10 -0
  105. package/src/contexts/DayPicker/labels/labelMonthDropdown.test.ts +5 -0
  106. package/src/contexts/DayPicker/labels/labelMonthDropdown.ts +6 -0
  107. package/src/contexts/DayPicker/labels/labelNext.test.ts +5 -0
  108. package/src/contexts/DayPicker/labels/labelNext.ts +8 -0
  109. package/src/contexts/DayPicker/labels/labelPrevious.test.ts +5 -0
  110. package/src/contexts/DayPicker/labels/labelPrevious.ts +8 -0
  111. package/src/contexts/DayPicker/labels/labelWeekNumber.test.ts +5 -0
  112. package/src/contexts/DayPicker/labels/labelWeekNumber.ts +8 -0
  113. package/src/contexts/DayPicker/labels/labelWeekday.test.ts +15 -0
  114. package/src/contexts/DayPicker/labels/labelWeekday.ts +10 -0
  115. package/src/contexts/DayPicker/labels/labelYearDropdown.test.ts +5 -0
  116. package/src/contexts/DayPicker/labels/labelYearDropdown.ts +6 -0
  117. package/src/contexts/DayPicker/useDayPicker.test.ts +297 -0
  118. package/src/contexts/DayPicker/useDayPicker.ts +17 -0
  119. package/src/contexts/DayPicker/utils/index.ts +1 -0
  120. package/src/contexts/DayPicker/utils/parseFromToProps.test.ts +47 -0
  121. package/src/contexts/DayPicker/utils/parseFromToProps.ts +32 -0
  122. package/src/contexts/Focus/FocusContext.tsx +174 -0
  123. package/src/contexts/Focus/index.ts +2 -0
  124. package/src/contexts/Focus/useFocusContext.test.ts +183 -0
  125. package/src/contexts/Focus/useFocusContext.ts +12 -0
  126. package/src/contexts/Focus/utils/getInitialFocusTarget.test.tsx +12 -0
  127. package/src/contexts/Focus/utils/getInitialFocusTarget.tsx +44 -0
  128. package/src/contexts/Modifiers/ModifiersContext.tsx +44 -0
  129. package/src/contexts/Modifiers/index.ts +2 -0
  130. package/src/contexts/Modifiers/useModifiers.test.ts +46 -0
  131. package/src/contexts/Modifiers/useModifiers.ts +17 -0
  132. package/src/contexts/Modifiers/utils/getActiveModifiers.test.ts +53 -0
  133. package/src/contexts/Modifiers/utils/getActiveModifiers.ts +33 -0
  134. package/src/contexts/Modifiers/utils/getCustomModifiers.test.ts +14 -0
  135. package/src/contexts/Modifiers/utils/getCustomModifiers.ts +14 -0
  136. package/src/contexts/Modifiers/utils/getInternalModifiers.test.ts +146 -0
  137. package/src/contexts/Modifiers/utils/getInternalModifiers.ts +58 -0
  138. package/src/contexts/Modifiers/utils/isDateInRange.test.ts +28 -0
  139. package/src/contexts/Modifiers/utils/isDateInRange.ts +27 -0
  140. package/src/contexts/Modifiers/utils/isMatch.test.ts +92 -0
  141. package/src/contexts/Modifiers/utils/isMatch.ts +76 -0
  142. package/src/contexts/Modifiers/utils/matcherToArray.test.ts +22 -0
  143. package/src/contexts/Modifiers/utils/matcherToArray.ts +14 -0
  144. package/src/contexts/Navigation/NavigationContext.tsx +84 -0
  145. package/src/contexts/Navigation/index.ts +2 -0
  146. package/src/contexts/Navigation/useNavigation.test.ts +126 -0
  147. package/src/contexts/Navigation/useNavigation.ts +12 -0
  148. package/src/contexts/Navigation/useNavigationState.test.ts +36 -0
  149. package/src/contexts/Navigation/useNavigationState.ts +25 -0
  150. package/src/contexts/Navigation/utils/getDisplayMonths.ts +31 -0
  151. package/src/contexts/Navigation/utils/getInitialMonth.test.ts +56 -0
  152. package/src/contexts/Navigation/utils/getInitialMonth.ts +24 -0
  153. package/src/contexts/Navigation/utils/getNextMonth.test.ts +75 -0
  154. package/src/contexts/Navigation/utils/getNextMonth.ts +45 -0
  155. package/src/contexts/Navigation/utils/getPreviousMonth.test.ts +55 -0
  156. package/src/contexts/Navigation/utils/getPreviousMonth.ts +44 -0
  157. package/src/contexts/RootProvider.tsx +37 -0
  158. package/src/contexts/SelectMultiple/SelectMultipleContext.tsx +135 -0
  159. package/src/contexts/SelectMultiple/index.ts +2 -0
  160. package/src/contexts/SelectMultiple/useSelectMultiple.test.ts +191 -0
  161. package/src/contexts/SelectMultiple/useSelectMultiple.ts +17 -0
  162. package/src/contexts/SelectRange/SelectRangeContext.tsx +158 -0
  163. package/src/contexts/SelectRange/index.ts +2 -0
  164. package/src/contexts/SelectRange/useSelectRange.test.ts +282 -0
  165. package/src/contexts/SelectRange/useSelectRange.ts +15 -0
  166. package/src/contexts/SelectRange/utils/addToRange.test.ts +119 -0
  167. package/src/contexts/SelectRange/utils/addToRange.ts +43 -0
  168. package/src/contexts/SelectSingle/SelectSingleContext.tsx +80 -0
  169. package/src/contexts/SelectSingle/index.ts +2 -0
  170. package/src/contexts/SelectSingle/useSelectSingle.test.ts +81 -0
  171. package/src/contexts/SelectSingle/useSelectSingle.ts +17 -0
  172. package/src/hooks/useActiveModifiers/index.ts +1 -0
  173. package/src/hooks/useActiveModifiers/useActiveModifiers.test.tsx +36 -0
  174. package/src/hooks/useActiveModifiers/useActiveModifiers.tsx +18 -0
  175. package/src/hooks/useControlledValue/index.ts +1 -0
  176. package/src/hooks/useControlledValue/useControlledValue.test.ts +68 -0
  177. package/src/hooks/useControlledValue/useControlledValue.ts +24 -0
  178. package/src/hooks/useDayEventHandlers/index.ts +1 -0
  179. package/src/hooks/useDayEventHandlers/useDayEventHandlers.test.tsx +213 -0
  180. package/src/hooks/useDayEventHandlers/useDayEventHandlers.tsx +195 -0
  181. package/src/hooks/useDayRender/index.ts +1 -0
  182. package/src/hooks/useDayRender/useDayRender.test.tsx +304 -0
  183. package/src/hooks/useDayRender/useDayRender.tsx +123 -0
  184. package/src/hooks/useDayRender/utils/getDayClassNames.test.ts +63 -0
  185. package/src/hooks/useDayRender/utils/getDayClassNames.ts +32 -0
  186. package/src/hooks/useDayRender/utils/getDayStyle.ts +19 -0
  187. package/src/hooks/useInput/index.ts +1 -0
  188. package/src/hooks/useInput/useInput.ts +175 -0
  189. package/src/hooks/useInput/utils/isValidDate.tsx +4 -0
  190. package/src/hooks/useSelectedDays/index.ts +1 -0
  191. package/src/hooks/useSelectedDays/useSelectedDays.test.ts +72 -0
  192. package/src/hooks/useSelectedDays/useSelectedDays.ts +32 -0
  193. package/src/index.ts +43 -0
  194. package/src/style.css +311 -0
  195. package/src/style.css.d.ts +38 -0
  196. package/src/types/DayPickerBase.ts +267 -0
  197. package/src/types/DayPickerDefault.ts +15 -0
  198. package/src/types/DayPickerMultiple.ts +26 -0
  199. package/src/types/DayPickerRange.ts +27 -0
  200. package/src/types/DayPickerSingle.ts +24 -0
  201. package/src/types/EventHandlers.ts +87 -0
  202. package/src/types/Formatters.ts +29 -0
  203. package/src/types/Labels.ts +36 -0
  204. package/src/types/Matchers.ts +106 -0
  205. package/src/types/Modifiers.ts +62 -0
  206. package/src/types/Styles.ts +125 -0
  207. package/tsconfig.json +24 -0
@@ -0,0 +1 @@
1
+ export * from './useControlledValue';
@@ -0,0 +1,68 @@
1
+ import { act } from 'react-dom/test-utils';
2
+
3
+ import { renderHook, RenderHookResult } from '@testing-library/react-hooks';
4
+
5
+ import { DispatchStateAction, useControlledValue } from './useControlledValue';
6
+
7
+ type RenderHookProps = {
8
+ defaultValue: string;
9
+ controlledValue: string | undefined;
10
+ };
11
+
12
+ function setup(defaultValue: string, controlledValue: string | undefined) {
13
+ return renderHook<RenderHookProps, [string, DispatchStateAction<string>]>(
14
+ (props) =>
15
+ useControlledValue<string>(props.defaultValue, props.controlledValue),
16
+ {
17
+ initialProps: { defaultValue, controlledValue }
18
+ }
19
+ );
20
+ }
21
+
22
+ describe('when the value is controlled', () => {
23
+ const defaultValue = 'foo'; // not controlled
24
+ const controlledValue = 'bar'; // now controlled
25
+ let hook: RenderHookResult<
26
+ RenderHookProps,
27
+ [string, DispatchStateAction<string>]
28
+ >;
29
+ beforeEach(() => {
30
+ hook = setup(defaultValue, controlledValue);
31
+ });
32
+ test('should return the controlled value', () => {
33
+ expect(hook.result.current[0]).toBe(controlledValue);
34
+ });
35
+ describe('when setting a new value', () => {
36
+ const newValue = 'taz';
37
+ beforeEach(() => {
38
+ act(() => hook.result.current[1](newValue));
39
+ });
40
+ test('should return the controlled value instead', () => {
41
+ expect(hook.result.current[0]).toBe(controlledValue);
42
+ });
43
+ });
44
+ });
45
+
46
+ describe('when the value is not controlled', () => {
47
+ const defaultValue = 'foo';
48
+ const controlledValue = undefined;
49
+ let hook: RenderHookResult<
50
+ RenderHookProps,
51
+ [string, DispatchStateAction<string>]
52
+ >;
53
+ beforeEach(() => {
54
+ hook = setup(defaultValue, controlledValue);
55
+ });
56
+ test('should return the value', () => {
57
+ expect(hook.result.current[0]).toBe(defaultValue);
58
+ });
59
+ describe('when setting a new value', () => {
60
+ const newValue = 'bar';
61
+ beforeEach(() => {
62
+ act(() => hook.result.current[1](newValue));
63
+ });
64
+ test('should return the new value', () => {
65
+ expect(hook.result.current[0]).toBe(newValue);
66
+ });
67
+ });
68
+ });
@@ -0,0 +1,24 @@
1
+ import React, { useState } from 'react';
2
+
3
+ export type DispatchStateAction<T> = React.Dispatch<React.SetStateAction<T>>;
4
+
5
+ /**
6
+ * Helper hook for using controlled/uncontrolled values from a component props.
7
+ *
8
+ * When the value is not controlled, pass `undefined` as `controlledValue` and
9
+ * use the returned setter to update it.
10
+ *
11
+ * When the value is controlled, pass the controlled value as second
12
+ * argument, which will be always returned as `value`.
13
+ */
14
+ export function useControlledValue<T>(
15
+ defaultValue: T,
16
+ controlledValue: T | undefined
17
+ ): [T, DispatchStateAction<T>] {
18
+ const [uncontrolledValue, setValue] = useState(defaultValue);
19
+
20
+ const value =
21
+ controlledValue === undefined ? uncontrolledValue : controlledValue;
22
+
23
+ return [value, setValue] as [T, DispatchStateAction<T>];
24
+ }
@@ -0,0 +1 @@
1
+ export * from './useDayEventHandlers';
@@ -0,0 +1,213 @@
1
+ import { RenderResult } from '@testing-library/react-hooks';
2
+ import { DayPickerProps } from 'DayPicker';
3
+
4
+ import { customRenderHook } from 'test/render';
5
+
6
+ import { FocusContextValue } from 'contexts/Focus';
7
+ import { SelectMultipleContextValue } from 'contexts/SelectMultiple';
8
+ import { SelectRangeContextValue } from 'contexts/SelectRange';
9
+ import { SelectSingleContextValue } from 'contexts/SelectSingle';
10
+ import {
11
+ DayEventHandlers,
12
+ DayEventName,
13
+ EventName,
14
+ useDayEventHandlers
15
+ } from 'hooks/useDayEventHandlers';
16
+ import { ActiveModifiers } from 'types/Modifiers';
17
+
18
+ const today = new Date(2010, 5, 23);
19
+
20
+ const singleContext: SelectSingleContextValue = {
21
+ selected: today,
22
+ onDayClick: jest.fn()
23
+ };
24
+
25
+ const multipleContext: SelectMultipleContextValue = {
26
+ selected: [today],
27
+ modifiers: { disabled: [] },
28
+ onDayClick: jest.fn()
29
+ };
30
+
31
+ const rangeContext: SelectRangeContextValue = {
32
+ selected: undefined,
33
+ modifiers: {
34
+ disabled: [],
35
+ range_start: [],
36
+ range_end: [],
37
+ range_middle: []
38
+ },
39
+ onDayClick: jest.fn()
40
+ };
41
+
42
+ const focusContext: FocusContextValue = {
43
+ focus: jest.fn(),
44
+ focusedDay: undefined,
45
+ focusTarget: undefined,
46
+ blur: jest.fn(),
47
+ focusDayAfter: jest.fn(),
48
+ focusDayBefore: jest.fn(),
49
+ focusWeekBefore: jest.fn(),
50
+ focusWeekAfter: jest.fn(),
51
+ focusMonthBefore: jest.fn(),
52
+ focusMonthAfter: jest.fn(),
53
+ focusYearBefore: jest.fn(),
54
+ focusYearAfter: jest.fn(),
55
+ focusStartOfWeek: jest.fn(),
56
+ focusEndOfWeek: jest.fn()
57
+ };
58
+
59
+ const mockedContexts = {
60
+ single: singleContext,
61
+ multiple: multipleContext,
62
+ range: rangeContext,
63
+ focus: focusContext
64
+ };
65
+
66
+ let renderResult: RenderResult<DayEventHandlers>;
67
+ function setup(
68
+ date: Date,
69
+ activeModifiers: ActiveModifiers,
70
+ dayPickerProps?: DayPickerProps
71
+ ) {
72
+ const view = customRenderHook(
73
+ () => useDayEventHandlers(date, activeModifiers),
74
+ dayPickerProps,
75
+ mockedContexts
76
+ );
77
+ renderResult = view.result;
78
+ }
79
+
80
+ const tests: [EventName, DayEventName][] = [
81
+ ['onClick', 'onDayClick'],
82
+ ['onFocus', 'onDayFocus'],
83
+ ['onBlur', 'onDayBlur'],
84
+ ['onMouseEnter', 'onDayMouseEnter'],
85
+ ['onMouseLeave', 'onDayMouseLeave'],
86
+ ['onTouchEnd', 'onDayTouchEnd'],
87
+ ['onTouchCancel', 'onDayTouchCancel'],
88
+ ['onTouchMove', 'onDayTouchMove'],
89
+ ['onTouchStart', 'onDayTouchStart'],
90
+ ['onKeyUp', 'onDayKeyUp']
91
+ ];
92
+
93
+ describe.each(tests)('when calling "%s"', (eventName, dayEventName) => {
94
+ const activeModifiers: ActiveModifiers = {};
95
+ const dayPickerProps = {
96
+ [dayEventName]: jest.fn()
97
+ };
98
+ const mouseEvent = {} as React.MouseEvent<HTMLButtonElement, MouseEvent>;
99
+ const date = today;
100
+ beforeEach(() => {
101
+ setup(date, activeModifiers, dayPickerProps);
102
+ //@ts-expect-error TOFIX: How to mock mouse event here?
103
+ renderResult.current[eventName]?.(mouseEvent);
104
+ });
105
+ test(`${dayEventName} should have been called`, () => {
106
+ expect(dayPickerProps[dayEventName]).toHaveBeenCalledWith(
107
+ date,
108
+ activeModifiers,
109
+ mouseEvent
110
+ );
111
+ });
112
+ });
113
+
114
+ describe.each<'single' | 'multiple' | 'range'>(['single', 'multiple', 'range'])(
115
+ 'when calling "onClick" in "%s" selection mode',
116
+ (mode) => {
117
+ const activeModifiers: ActiveModifiers = {};
118
+ const dayPickerProps = { mode, onDayClick: jest.fn() };
119
+ const mouseEvent = {} as React.MouseEvent<HTMLButtonElement, MouseEvent>;
120
+ const date = today;
121
+ beforeEach(() => {
122
+ setup(date, activeModifiers, dayPickerProps);
123
+ renderResult.current.onClick?.(mouseEvent);
124
+ });
125
+ test(`should have called "onDayClick" from the day picker props`, () => {
126
+ expect(dayPickerProps.onDayClick).toHaveBeenCalled();
127
+ });
128
+ }
129
+ );
130
+
131
+ describe('when calling "onFocus"', () => {
132
+ const date = today;
133
+ const activeModifiers: ActiveModifiers = {};
134
+ const mouseEvent = {} as React.FocusEvent<HTMLButtonElement, Element>;
135
+ beforeEach(() => {
136
+ setup(date, activeModifiers);
137
+ renderResult.current.onFocus?.(mouseEvent);
138
+ });
139
+ test('should focus the date in the context', () => {
140
+ expect(focusContext.focus).toHaveBeenCalledWith(date);
141
+ });
142
+ });
143
+
144
+ describe('when calling "onBlur"', () => {
145
+ const date = today;
146
+ const activeModifiers: ActiveModifiers = {};
147
+ const mouseEvent = {} as React.FocusEvent<HTMLButtonElement, Element>;
148
+ beforeEach(() => {
149
+ setup(date, activeModifiers);
150
+ renderResult.current.onBlur?.(mouseEvent);
151
+ });
152
+ test('should blur the date in the context', () => {
153
+ expect(focusContext.blur).toHaveBeenCalled();
154
+ });
155
+ });
156
+
157
+ describe('when calling "onKeyDown"', () => {
158
+ const date = today;
159
+ const activeModifiers: ActiveModifiers = {};
160
+
161
+ const tests: [
162
+ key: string,
163
+ dir: string,
164
+ shiftKey: boolean,
165
+ expectedMethod: keyof FocusContextValue
166
+ ][] = [
167
+ ['ArrowLeft', 'ltr', false, 'focusDayBefore'],
168
+ ['ArrowLeft', 'rtl', false, 'focusDayAfter'],
169
+ ['ArrowRight', 'ltr', false, 'focusDayAfter'],
170
+ ['ArrowRight', 'ltr', false, 'focusDayBefore'],
171
+ ['ArrowRight', 'ltr', false, 'focusDayAfter'],
172
+ ['ArrowDown', 'ltr', false, 'focusWeekAfter'],
173
+ ['ArrowUp', 'ltr', false, 'focusWeekBefore'],
174
+ ['PageUp', 'ltr', true, 'focusYearBefore'],
175
+ ['PageUp', 'ltr', false, 'focusMonthBefore'],
176
+ ['PageDown', 'ltr', true, 'focusYearAfter'],
177
+ ['PageDown', 'ltr', false, 'focusMonthAfter'],
178
+ ['Home', 'ltr', false, 'focusStartOfWeek'],
179
+ ['End', 'ltr', false, 'focusEndOfWeek']
180
+ ];
181
+
182
+ describe.each(tests)(
183
+ 'when key is %s',
184
+ (key, dir, shiftKey, expectedMethod) => {
185
+ describe(`when text direction is "${dir.toUpperCase()}"`, () => {
186
+ describe(`when the shiftKey is ${
187
+ shiftKey ? '' : 'not'
188
+ } pressed`, () => {
189
+ const keyboardEvent = {
190
+ key,
191
+ shiftKey
192
+ } as React.KeyboardEvent<HTMLButtonElement>;
193
+ keyboardEvent.preventDefault = jest.fn();
194
+ keyboardEvent.stopPropagation = jest.fn();
195
+
196
+ beforeEach(() => {
197
+ setup(date, activeModifiers, { dir });
198
+ renderResult.current.onKeyDown?.(keyboardEvent);
199
+ });
200
+ test(`should call ${expectedMethod}`, () => {
201
+ expect(focusContext[expectedMethod]).toHaveBeenCalledWith();
202
+ });
203
+ test(`should prevent the default event`, () => {
204
+ expect(keyboardEvent.preventDefault).toHaveBeenCalledWith();
205
+ });
206
+ test(`should stop the event propagation`, () => {
207
+ expect(keyboardEvent.preventDefault).toHaveBeenCalledWith();
208
+ });
209
+ });
210
+ });
211
+ }
212
+ );
213
+ });
@@ -0,0 +1,195 @@
1
+ import {
2
+ FocusEventHandler,
3
+ HTMLProps,
4
+ KeyboardEventHandler,
5
+ MouseEventHandler,
6
+ TouchEventHandler
7
+ } from 'react';
8
+
9
+ import { useDayPicker } from 'contexts/DayPicker';
10
+ import { useFocusContext } from 'contexts/Focus';
11
+ import { useSelectMultiple } from 'contexts/SelectMultiple';
12
+ import { useSelectRange } from 'contexts/SelectRange';
13
+ import { useSelectSingle } from 'contexts/SelectSingle';
14
+ import { isDayPickerMultiple } from 'types/DayPickerMultiple';
15
+ import { isDayPickerRange } from 'types/DayPickerRange';
16
+ import { isDayPickerSingle } from 'types/DayPickerSingle';
17
+ import { ActiveModifiers } from 'types/Modifiers';
18
+
19
+ export type EventName =
20
+ | 'onClick'
21
+ | 'onFocus'
22
+ | 'onBlur'
23
+ | 'onKeyDown'
24
+ | 'onKeyUp'
25
+ | 'onMouseEnter'
26
+ | 'onMouseLeave'
27
+ | 'onTouchCancel'
28
+ | 'onTouchEnd'
29
+ | 'onTouchMove'
30
+ | 'onTouchStart';
31
+
32
+ export type DayEventName =
33
+ | 'onDayClick'
34
+ | 'onDayFocus'
35
+ | 'onDayBlur'
36
+ | 'onDayKeyDown'
37
+ | 'onDayKeyUp'
38
+ | 'onDayMouseEnter'
39
+ | 'onDayMouseLeave'
40
+ | 'onDayTouchCancel'
41
+ | 'onDayTouchEnd'
42
+ | 'onDayTouchMove'
43
+ | 'onDayTouchStart';
44
+
45
+ export type DayEventHandlers = Pick<HTMLProps<HTMLButtonElement>, EventName>;
46
+
47
+ /**
48
+ * This hook returns details about the content to render in the day cell.
49
+ *
50
+ *
51
+ * When a day cell is rendered in the table, DayPicker can either:
52
+ *
53
+ * - render nothing: when the day is outside the month or has matched the
54
+ * "hidden" modifier.
55
+ * - render a button when `onDayClick` or a selection mode is set.
56
+ * - render a non-interactive element: when no selection mode is set, the day
57
+ * cell shouldn’t respond to any interaction. DayPicker should render a `div`
58
+ * or a `span`.
59
+ *
60
+ * ### Usage
61
+ *
62
+ * Use this hook to customize the behavior of the [[Day]] component. Create a
63
+ * new `Day` component using this hook and pass it to the `components` prop.
64
+ * The source of [[Day]] can be a good starting point.
65
+ *
66
+ */
67
+ export function useDayEventHandlers(
68
+ date: Date,
69
+ activeModifiers: ActiveModifiers
70
+ ): DayEventHandlers {
71
+ const dayPicker = useDayPicker();
72
+ const single = useSelectSingle();
73
+ const multiple = useSelectMultiple();
74
+ const range = useSelectRange();
75
+ const {
76
+ focusDayAfter,
77
+ focusDayBefore,
78
+ focusWeekAfter,
79
+ focusWeekBefore,
80
+ blur,
81
+ focus,
82
+ focusMonthBefore,
83
+ focusMonthAfter,
84
+ focusYearBefore,
85
+ focusYearAfter,
86
+ focusStartOfWeek,
87
+ focusEndOfWeek
88
+ } = useFocusContext();
89
+
90
+ const onClick: MouseEventHandler = (e) => {
91
+ if (isDayPickerSingle(dayPicker)) {
92
+ single.onDayClick?.(date, activeModifiers, e);
93
+ } else if (isDayPickerMultiple(dayPicker)) {
94
+ multiple.onDayClick?.(date, activeModifiers, e);
95
+ } else if (isDayPickerRange(dayPicker)) {
96
+ range.onDayClick?.(date, activeModifiers, e);
97
+ }
98
+ dayPicker.onDayClick?.(date, activeModifiers, e);
99
+ };
100
+
101
+ const onFocus: FocusEventHandler = (e) => {
102
+ focus(date);
103
+ dayPicker.onDayFocus?.(date, activeModifiers, e);
104
+ };
105
+
106
+ const onBlur: FocusEventHandler = (e) => {
107
+ blur();
108
+ dayPicker.onDayBlur?.(date, activeModifiers, e);
109
+ };
110
+
111
+ const onMouseEnter: MouseEventHandler = (e) => {
112
+ dayPicker.onDayMouseEnter?.(date, activeModifiers, e);
113
+ };
114
+ const onMouseLeave: MouseEventHandler = (e) => {
115
+ dayPicker.onDayMouseLeave?.(date, activeModifiers, e);
116
+ };
117
+ const onTouchCancel: TouchEventHandler = (e) => {
118
+ dayPicker.onDayTouchCancel?.(date, activeModifiers, e);
119
+ };
120
+ const onTouchEnd: TouchEventHandler = (e) => {
121
+ dayPicker.onDayTouchEnd?.(date, activeModifiers, e);
122
+ };
123
+ const onTouchMove: TouchEventHandler = (e) => {
124
+ dayPicker.onDayTouchMove?.(date, activeModifiers, e);
125
+ };
126
+ const onTouchStart: TouchEventHandler = (e) => {
127
+ dayPicker.onDayTouchStart?.(date, activeModifiers, e);
128
+ };
129
+
130
+ const onKeyUp: KeyboardEventHandler = (e) => {
131
+ dayPicker.onDayKeyUp?.(date, activeModifiers, e);
132
+ };
133
+
134
+ const onKeyDown: KeyboardEventHandler = (e) => {
135
+ switch (e.key) {
136
+ case 'ArrowLeft':
137
+ e.preventDefault();
138
+ e.stopPropagation();
139
+ dayPicker.dir === 'rtl' ? focusDayAfter() : focusDayBefore();
140
+ break;
141
+ case 'ArrowRight':
142
+ e.preventDefault();
143
+ e.stopPropagation();
144
+ dayPicker.dir === 'rtl' ? focusDayBefore() : focusDayAfter();
145
+ break;
146
+ case 'ArrowDown':
147
+ e.preventDefault();
148
+ e.stopPropagation();
149
+ focusWeekAfter();
150
+ break;
151
+ case 'ArrowUp':
152
+ e.preventDefault();
153
+ e.stopPropagation();
154
+ focusWeekBefore();
155
+ break;
156
+ case 'PageUp':
157
+ e.preventDefault();
158
+ e.stopPropagation();
159
+ e.shiftKey ? focusYearBefore() : focusMonthBefore();
160
+ break;
161
+ case 'PageDown':
162
+ e.preventDefault();
163
+ e.stopPropagation();
164
+ e.shiftKey ? focusYearAfter() : focusMonthAfter();
165
+ break;
166
+ case 'Home':
167
+ e.preventDefault();
168
+ e.stopPropagation();
169
+ focusStartOfWeek();
170
+ break;
171
+ case 'End':
172
+ e.preventDefault();
173
+ e.stopPropagation();
174
+ focusEndOfWeek();
175
+ break;
176
+ }
177
+ dayPicker.onDayKeyDown?.(date, activeModifiers, e);
178
+ };
179
+
180
+ const eventHandlers: DayEventHandlers = {
181
+ onClick,
182
+ onFocus,
183
+ onBlur,
184
+ onKeyDown,
185
+ onKeyUp,
186
+ onMouseEnter,
187
+ onMouseLeave,
188
+ onTouchCancel,
189
+ onTouchEnd,
190
+ onTouchMove,
191
+ onTouchStart
192
+ };
193
+
194
+ return eventHandlers;
195
+ }
@@ -0,0 +1 @@
1
+ export * from './useDayRender';