react-day-picker 8.0.2 → 8.0.5

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 (213) hide show
  1. package/dist/components/Head/utils/getWeekdays.d.ts +1 -1
  2. package/dist/components/Table/utils/daysToMonthWeeks.d.ts +1 -1
  3. package/dist/components/Table/utils/getMonthWeeks.d.ts +1 -1
  4. package/dist/contexts/DayPicker/formatters/formatCaption.d.ts +1 -1
  5. package/dist/contexts/DayPicker/formatters/formatDay.d.ts +1 -1
  6. package/dist/contexts/DayPicker/formatters/formatMonthCaption.d.ts +1 -1
  7. package/dist/contexts/DayPicker/formatters/formatWeekdayName.d.ts +1 -1
  8. package/dist/hooks/useDayRender/useDayRender.d.ts +1 -1
  9. package/dist/index.esm.js +30 -7
  10. package/dist/index.esm.js.map +1 -1
  11. package/dist/index.js +154 -107
  12. package/dist/index.js.map +1 -1
  13. package/dist/react-day-picker.min.js +1 -1
  14. package/dist/style.css +11 -10
  15. package/dist/style.module.css +11 -10
  16. package/dist/types/DayPickerBase.d.ts +1 -1
  17. package/dist/types/Labels.d.ts +1 -1
  18. package/package.json +15 -14
  19. package/src/DayPicker.tsx +113 -0
  20. package/src/components/Button/Button.test.tsx +47 -0
  21. package/src/components/Button/Button.tsx +36 -0
  22. package/src/components/Button/index.ts +1 -0
  23. package/src/components/Caption/Caption.test.tsx +86 -0
  24. package/src/components/Caption/Caption.tsx +54 -0
  25. package/src/components/Caption/index.ts +1 -0
  26. package/src/components/CaptionDropdowns/CaptionDropdowns.test.tsx +123 -0
  27. package/src/components/CaptionDropdowns/CaptionDropdowns.tsx +43 -0
  28. package/src/components/CaptionDropdowns/index.ts +1 -0
  29. package/src/components/CaptionLabel/CaptionLabel.test.tsx +29 -0
  30. package/src/components/CaptionLabel/CaptionLabel.tsx +32 -0
  31. package/src/components/CaptionLabel/index.ts +1 -0
  32. package/src/components/CaptionNavigation/CaptionNavigation.test.tsx +172 -0
  33. package/src/components/CaptionNavigation/CaptionNavigation.tsx +63 -0
  34. package/src/components/CaptionNavigation/index.ts +1 -0
  35. package/src/components/Day/Day.test.tsx +84 -0
  36. package/src/components/Day/Day.tsx +30 -0
  37. package/src/components/Day/index.ts +1 -0
  38. package/src/components/DayContent/DayContent.test.tsx +51 -0
  39. package/src/components/DayContent/DayContent.tsx +36 -0
  40. package/src/components/DayContent/index.ts +1 -0
  41. package/src/components/Dropdown/Dropdown.test.tsx +73 -0
  42. package/src/components/Dropdown/Dropdown.tsx +56 -0
  43. package/src/components/Dropdown/index.ts +1 -0
  44. package/src/components/Footer/Footer.test.tsx +29 -0
  45. package/src/components/Footer/Footer.tsx +20 -0
  46. package/src/components/Footer/index.ts +1 -0
  47. package/src/components/Head/Head.test.tsx +117 -0
  48. package/src/components/Head/Head.tsx +51 -0
  49. package/src/components/Head/index.ts +1 -0
  50. package/src/components/Head/utils/getWeekdays.test.ts +36 -0
  51. package/src/components/Head/utils/getWeekdays.ts +22 -0
  52. package/src/components/Head/utils/index.ts +1 -0
  53. package/src/components/IconDropdown/IconDropdown.test.tsx +20 -0
  54. package/src/components/IconDropdown/IconDropdown.tsx +24 -0
  55. package/src/components/IconDropdown/index.ts +1 -0
  56. package/src/components/IconLeft/IconLeft.test.tsx +20 -0
  57. package/src/components/IconLeft/IconLeft.tsx +18 -0
  58. package/src/components/IconLeft/index.ts +1 -0
  59. package/src/components/IconRight/IconRight.test.tsx +20 -0
  60. package/src/components/IconRight/IconRight.tsx +17 -0
  61. package/src/components/IconRight/index.ts +1 -0
  62. package/src/components/Month/Month.test.tsx +216 -0
  63. package/src/components/Month/Month.tsx +53 -0
  64. package/src/components/Month/index.ts +1 -0
  65. package/src/components/MonthsDropdown/MonthsDropdown.test.tsx +99 -0
  66. package/src/components/MonthsDropdown/MonthsDropdown.tsx +75 -0
  67. package/src/components/MonthsDropdown/index.ts +1 -0
  68. package/src/components/Navigation/Navigation.test.tsx +129 -0
  69. package/src/components/Navigation/Navigation.tsx +102 -0
  70. package/src/components/Navigation/index.ts +1 -0
  71. package/src/components/Root/Root.test.tsx +123 -0
  72. package/src/components/Root/Root.tsx +58 -0
  73. package/src/components/Root/index.ts +1 -0
  74. package/src/components/Row/Row.test.tsx +69 -0
  75. package/src/components/Row/Row.tsx +51 -0
  76. package/src/components/Row/index.ts +1 -0
  77. package/src/components/Table/Table.test.tsx +42 -0
  78. package/src/components/Table/Table.tsx +60 -0
  79. package/src/components/Table/__snapshots__/Table.test.tsx.snap +1453 -0
  80. package/src/components/Table/index.ts +1 -0
  81. package/src/components/Table/utils/daysToMonthWeeks.ts +47 -0
  82. package/src/components/Table/utils/getMonthWeeks.test.ts +68 -0
  83. package/src/components/Table/utils/getMonthWeeks.ts +55 -0
  84. package/src/components/WeekNumber/WeekNumber.test.tsx +46 -0
  85. package/src/components/WeekNumber/WeekNumber.tsx +58 -0
  86. package/src/components/WeekNumber/__snapshots__/WeekNumber.test.tsx.snap +11 -0
  87. package/src/components/WeekNumber/index.ts +1 -0
  88. package/src/components/YearsDropdown/YearsDropdown.test.tsx +98 -0
  89. package/src/components/YearsDropdown/YearsDropdown.tsx +76 -0
  90. package/src/components/YearsDropdown/index.ts +1 -0
  91. package/src/contexts/DayPicker/DayPickerContext.tsx +156 -0
  92. package/src/contexts/DayPicker/defaultClassNames.ts +58 -0
  93. package/src/contexts/DayPicker/defaultContextValue.ts +37 -0
  94. package/src/contexts/DayPicker/formatters/formatCaption.test.ts +15 -0
  95. package/src/contexts/DayPicker/formatters/formatCaption.ts +12 -0
  96. package/src/contexts/DayPicker/formatters/formatDay.test.ts +7 -0
  97. package/src/contexts/DayPicker/formatters/formatDay.ts +9 -0
  98. package/src/contexts/DayPicker/formatters/formatMonthCaption.test.ts +15 -0
  99. package/src/contexts/DayPicker/formatters/formatMonthCaption.ts +12 -0
  100. package/src/contexts/DayPicker/formatters/formatWeekNumber.test.ts +5 -0
  101. package/src/contexts/DayPicker/formatters/formatWeekNumber.ts +6 -0
  102. package/src/contexts/DayPicker/formatters/formatWeekdayName.test.ts +15 -0
  103. package/src/contexts/DayPicker/formatters/formatWeekdayName.ts +12 -0
  104. package/src/contexts/DayPicker/formatters/formatYearCaption.test.ts +7 -0
  105. package/src/contexts/DayPicker/formatters/formatYearCaption.ts +11 -0
  106. package/src/contexts/DayPicker/formatters/index.ts +6 -0
  107. package/src/contexts/DayPicker/index.ts +2 -0
  108. package/src/contexts/DayPicker/labels/index.ts +7 -0
  109. package/src/contexts/DayPicker/labels/labelDay.test.ts +7 -0
  110. package/src/contexts/DayPicker/labels/labelDay.ts +10 -0
  111. package/src/contexts/DayPicker/labels/labelMonthDropdown.test.ts +5 -0
  112. package/src/contexts/DayPicker/labels/labelMonthDropdown.ts +6 -0
  113. package/src/contexts/DayPicker/labels/labelNext.test.ts +5 -0
  114. package/src/contexts/DayPicker/labels/labelNext.ts +8 -0
  115. package/src/contexts/DayPicker/labels/labelPrevious.test.ts +5 -0
  116. package/src/contexts/DayPicker/labels/labelPrevious.ts +8 -0
  117. package/src/contexts/DayPicker/labels/labelWeekNumber.test.ts +5 -0
  118. package/src/contexts/DayPicker/labels/labelWeekNumber.ts +8 -0
  119. package/src/contexts/DayPicker/labels/labelWeekday.test.ts +15 -0
  120. package/src/contexts/DayPicker/labels/labelWeekday.ts +10 -0
  121. package/src/contexts/DayPicker/labels/labelYearDropdown.test.ts +5 -0
  122. package/src/contexts/DayPicker/labels/labelYearDropdown.ts +6 -0
  123. package/src/contexts/DayPicker/useDayPicker.test.ts +297 -0
  124. package/src/contexts/DayPicker/useDayPicker.ts +17 -0
  125. package/src/contexts/DayPicker/utils/index.ts +1 -0
  126. package/src/contexts/DayPicker/utils/parseFromToProps.test.ts +47 -0
  127. package/src/contexts/DayPicker/utils/parseFromToProps.ts +32 -0
  128. package/src/contexts/Focus/FocusContext.tsx +174 -0
  129. package/src/contexts/Focus/index.ts +2 -0
  130. package/src/contexts/Focus/useFocusContext.test.ts +183 -0
  131. package/src/contexts/Focus/useFocusContext.ts +12 -0
  132. package/src/contexts/Focus/utils/getInitialFocusTarget.test.tsx +12 -0
  133. package/src/contexts/Focus/utils/getInitialFocusTarget.tsx +44 -0
  134. package/src/contexts/Modifiers/ModifiersContext.tsx +44 -0
  135. package/src/contexts/Modifiers/index.ts +2 -0
  136. package/src/contexts/Modifiers/useModifiers.test.ts +46 -0
  137. package/src/contexts/Modifiers/useModifiers.ts +17 -0
  138. package/src/contexts/Modifiers/utils/getActiveModifiers.test.ts +53 -0
  139. package/src/contexts/Modifiers/utils/getActiveModifiers.ts +33 -0
  140. package/src/contexts/Modifiers/utils/getCustomModifiers.test.ts +14 -0
  141. package/src/contexts/Modifiers/utils/getCustomModifiers.ts +14 -0
  142. package/src/contexts/Modifiers/utils/getInternalModifiers.test.ts +146 -0
  143. package/src/contexts/Modifiers/utils/getInternalModifiers.ts +58 -0
  144. package/src/contexts/Modifiers/utils/isDateInRange.test.ts +28 -0
  145. package/src/contexts/Modifiers/utils/isDateInRange.ts +27 -0
  146. package/src/contexts/Modifiers/utils/isMatch.test.ts +92 -0
  147. package/src/contexts/Modifiers/utils/isMatch.ts +76 -0
  148. package/src/contexts/Modifiers/utils/matcherToArray.test.ts +22 -0
  149. package/src/contexts/Modifiers/utils/matcherToArray.ts +14 -0
  150. package/src/contexts/Navigation/NavigationContext.tsx +84 -0
  151. package/src/contexts/Navigation/index.ts +2 -0
  152. package/src/contexts/Navigation/useNavigation.test.ts +126 -0
  153. package/src/contexts/Navigation/useNavigation.ts +12 -0
  154. package/src/contexts/Navigation/useNavigationState.test.ts +36 -0
  155. package/src/contexts/Navigation/useNavigationState.ts +25 -0
  156. package/src/contexts/Navigation/utils/getDisplayMonths.ts +31 -0
  157. package/src/contexts/Navigation/utils/getInitialMonth.test.ts +56 -0
  158. package/src/contexts/Navigation/utils/getInitialMonth.ts +24 -0
  159. package/src/contexts/Navigation/utils/getNextMonth.test.ts +75 -0
  160. package/src/contexts/Navigation/utils/getNextMonth.ts +45 -0
  161. package/src/contexts/Navigation/utils/getPreviousMonth.test.ts +55 -0
  162. package/src/contexts/Navigation/utils/getPreviousMonth.ts +44 -0
  163. package/src/contexts/RootProvider.tsx +37 -0
  164. package/src/contexts/SelectMultiple/SelectMultipleContext.tsx +135 -0
  165. package/src/contexts/SelectMultiple/index.ts +2 -0
  166. package/src/contexts/SelectMultiple/useSelectMultiple.test.ts +191 -0
  167. package/src/contexts/SelectMultiple/useSelectMultiple.ts +17 -0
  168. package/src/contexts/SelectRange/SelectRangeContext.tsx +158 -0
  169. package/src/contexts/SelectRange/index.ts +2 -0
  170. package/src/contexts/SelectRange/useSelectRange.test.ts +282 -0
  171. package/src/contexts/SelectRange/useSelectRange.ts +15 -0
  172. package/src/contexts/SelectRange/utils/addToRange.test.ts +119 -0
  173. package/src/contexts/SelectRange/utils/addToRange.ts +43 -0
  174. package/src/contexts/SelectSingle/SelectSingleContext.tsx +80 -0
  175. package/src/contexts/SelectSingle/index.ts +2 -0
  176. package/src/contexts/SelectSingle/useSelectSingle.test.ts +81 -0
  177. package/src/contexts/SelectSingle/useSelectSingle.ts +17 -0
  178. package/src/hooks/useActiveModifiers/index.ts +1 -0
  179. package/src/hooks/useActiveModifiers/useActiveModifiers.test.tsx +36 -0
  180. package/src/hooks/useActiveModifiers/useActiveModifiers.tsx +18 -0
  181. package/src/hooks/useControlledValue/index.ts +1 -0
  182. package/src/hooks/useControlledValue/useControlledValue.test.ts +68 -0
  183. package/src/hooks/useControlledValue/useControlledValue.ts +24 -0
  184. package/src/hooks/useDayEventHandlers/index.ts +1 -0
  185. package/src/hooks/useDayEventHandlers/useDayEventHandlers.test.tsx +213 -0
  186. package/src/hooks/useDayEventHandlers/useDayEventHandlers.tsx +195 -0
  187. package/src/hooks/useDayRender/index.ts +1 -0
  188. package/src/hooks/useDayRender/useDayRender.test.tsx +304 -0
  189. package/src/hooks/useDayRender/useDayRender.tsx +123 -0
  190. package/src/hooks/useDayRender/utils/getDayClassNames.test.ts +63 -0
  191. package/src/hooks/useDayRender/utils/getDayClassNames.ts +32 -0
  192. package/src/hooks/useDayRender/utils/getDayStyle.ts +19 -0
  193. package/src/hooks/useInput/index.ts +1 -0
  194. package/src/hooks/useInput/useInput.ts +175 -0
  195. package/src/hooks/useInput/utils/isValidDate.tsx +4 -0
  196. package/src/hooks/useSelectedDays/index.ts +1 -0
  197. package/src/hooks/useSelectedDays/useSelectedDays.test.ts +72 -0
  198. package/src/hooks/useSelectedDays/useSelectedDays.ts +32 -0
  199. package/src/index.ts +43 -0
  200. package/src/style.css +311 -0
  201. package/src/style.css.d.ts +38 -0
  202. package/src/types/DayPickerBase.ts +267 -0
  203. package/src/types/DayPickerDefault.ts +15 -0
  204. package/src/types/DayPickerMultiple.ts +26 -0
  205. package/src/types/DayPickerRange.ts +27 -0
  206. package/src/types/DayPickerSingle.ts +24 -0
  207. package/src/types/EventHandlers.ts +87 -0
  208. package/src/types/Formatters.ts +29 -0
  209. package/src/types/Labels.ts +36 -0
  210. package/src/types/Matchers.ts +106 -0
  211. package/src/types/Modifiers.ts +62 -0
  212. package/src/types/Styles.ts +108 -0
  213. package/tsconfig.json +24 -0
@@ -0,0 +1,135 @@
1
+ import React, { createContext, ReactNode } from 'react';
2
+
3
+ import isSameDay from 'date-fns/isSameDay';
4
+
5
+ import { DayPickerBase } from 'types/DayPickerBase';
6
+ import {
7
+ DayPickerMultipleProps,
8
+ isDayPickerMultiple
9
+ } from 'types/DayPickerMultiple';
10
+ import { DayClickEventHandler } from 'types/EventHandlers';
11
+ import { InternalModifier, Modifiers } from 'types/Modifiers';
12
+
13
+ /** Represent the modifiers that are changed by the multiple selection. */
14
+ export type SelectMultipleModifiers = Pick<
15
+ Modifiers,
16
+ InternalModifier.Disabled
17
+ >;
18
+
19
+ /** Represents the value of a [[SelectMultipleContext]]. */
20
+ export interface SelectMultipleContextValue {
21
+ /** The days that have been selected. */
22
+ selected: Date[] | undefined;
23
+ /** The modifiers for the corresponding selection. */
24
+ modifiers: SelectMultipleModifiers;
25
+ /** Event handler to attach to the day button to enable the multiple select. */
26
+ onDayClick?: DayClickEventHandler;
27
+ }
28
+
29
+ /**
30
+ * The SelectMultiple context shares details about the selected days when in
31
+ * multiple selection mode.
32
+ *
33
+ * Access this context from the [[useSelectMultiple]] hook.
34
+ */
35
+ export const SelectMultipleContext = createContext<
36
+ SelectMultipleContextValue | undefined
37
+ >(undefined);
38
+
39
+ export type SelectMultipleProviderProps = {
40
+ initialProps: DayPickerBase;
41
+ children: ReactNode;
42
+ };
43
+
44
+ /** Provides the values for the [[SelectMultipleContext]]. */
45
+ export function SelectMultipleProvider(
46
+ props: SelectMultipleProviderProps
47
+ ): JSX.Element {
48
+ if (!isDayPickerMultiple(props.initialProps)) {
49
+ const emptyContextValue: SelectMultipleContextValue = {
50
+ selected: undefined,
51
+ modifiers: {
52
+ disabled: []
53
+ }
54
+ };
55
+ return (
56
+ <SelectMultipleContext.Provider value={emptyContextValue}>
57
+ {props.children}
58
+ </SelectMultipleContext.Provider>
59
+ );
60
+ }
61
+ return (
62
+ <SelectMultipleProviderInternal
63
+ initialProps={props.initialProps}
64
+ children={props.children}
65
+ />
66
+ );
67
+ }
68
+
69
+ type SelectMultipleProviderInternalProps = {
70
+ initialProps: DayPickerMultipleProps;
71
+ children: ReactNode;
72
+ };
73
+
74
+ export function SelectMultipleProviderInternal({
75
+ initialProps,
76
+ children
77
+ }: SelectMultipleProviderInternalProps): JSX.Element {
78
+ const { selected, min, max } = initialProps;
79
+
80
+ const onDayClick: DayClickEventHandler = (day, activeModifiers, e) => {
81
+ initialProps.onDayClick?.(day, activeModifiers, e);
82
+
83
+ const isMinSelected = Boolean(
84
+ activeModifiers.selected && min && selected?.length === min
85
+ );
86
+ if (isMinSelected) {
87
+ return;
88
+ }
89
+
90
+ const isMaxSelected = Boolean(
91
+ !activeModifiers.selected && max && selected?.length === max
92
+ );
93
+ if (isMaxSelected) {
94
+ return;
95
+ }
96
+
97
+ const selectedDays = selected ? [...selected] : [];
98
+
99
+ if (activeModifiers.selected) {
100
+ const index = selectedDays.findIndex((selectedDay) =>
101
+ isSameDay(day, selectedDay)
102
+ );
103
+ selectedDays.splice(index, 1);
104
+ } else {
105
+ selectedDays.push(day);
106
+ }
107
+ initialProps.onSelect?.(selectedDays, day, activeModifiers, e);
108
+ };
109
+
110
+ const modifiers: SelectMultipleModifiers = {
111
+ disabled: []
112
+ };
113
+
114
+ if (selected) {
115
+ modifiers.disabled.push((day: Date) => {
116
+ const isMaxSelected = max && selected.length > max - 1;
117
+ const isSelected = selected.some((selectedDay) =>
118
+ isSameDay(selectedDay, day)
119
+ );
120
+ return Boolean(isMaxSelected && !isSelected);
121
+ });
122
+ }
123
+
124
+ const contextValue = {
125
+ selected,
126
+ onDayClick,
127
+ modifiers
128
+ };
129
+
130
+ return (
131
+ <SelectMultipleContext.Provider value={contextValue}>
132
+ {children}
133
+ </SelectMultipleContext.Provider>
134
+ );
135
+ }
@@ -0,0 +1,2 @@
1
+ export * from './SelectMultipleContext';
2
+ export * from './useSelectMultiple';
@@ -0,0 +1,191 @@
1
+ import { RenderResult } from '@testing-library/react-hooks';
2
+ import { addDays, addMonths } from 'date-fns';
3
+
4
+ import { customRenderHook } from 'test/render/customRenderHook';
5
+ import { freezeBeforeAll } from 'test/utils';
6
+
7
+ import { isMatch } from 'contexts/Modifiers/utils/isMatch';
8
+ import { DayPickerMultipleProps } from 'types/DayPickerMultiple';
9
+ import { ActiveModifiers } from 'types/Modifiers';
10
+
11
+ import { SelectMultipleContextValue } from './SelectMultipleContext';
12
+ import { useSelectMultiple } from './useSelectMultiple';
13
+
14
+ const today = new Date(2021, 11, 8);
15
+ freezeBeforeAll(today);
16
+
17
+ let result: RenderResult<SelectMultipleContextValue>;
18
+
19
+ function setup(dayPickerProps?: DayPickerMultipleProps) {
20
+ const view = customRenderHook(useSelectMultiple, dayPickerProps);
21
+ result = view.result;
22
+ }
23
+
24
+ describe('when is not a multiple select DayPicker', () => {
25
+ beforeAll(() => {
26
+ setup();
27
+ });
28
+ test('the selected day should be undefined', () => {
29
+ expect(result.current.selected).toBeUndefined();
30
+ });
31
+ test('the disabled modifiers should be empty', () => {
32
+ expect(result.current.selected).toBeUndefined();
33
+ });
34
+ });
35
+
36
+ const initialProps: DayPickerMultipleProps = {
37
+ mode: 'multiple',
38
+ onDayClick: jest.fn(),
39
+ onSelect: jest.fn()
40
+ };
41
+
42
+ const selectedDay1 = today;
43
+ const selectedDay2 = addDays(today, 1);
44
+ const selectedDay3 = addDays(today, 4);
45
+
46
+ describe('when days are selected', () => {
47
+ const selected = [selectedDay1, selectedDay2, selectedDay3];
48
+ const dayPickerProps: DayPickerMultipleProps = {
49
+ ...initialProps,
50
+ selected
51
+ };
52
+
53
+ beforeAll(() => {
54
+ setup(dayPickerProps);
55
+ });
56
+ test('it should return the days as selected', () => {
57
+ expect(result.current.selected).toStrictEqual(selected);
58
+ });
59
+ describe('when `onDayClick` is called with a not selected day', () => {
60
+ const clickedDay = addDays(selectedDay1, -1);
61
+ const activeModifiers = {};
62
+ const event = {} as React.MouseEvent;
63
+ beforeAll(() => {
64
+ result.current.onDayClick?.(clickedDay, activeModifiers, event);
65
+ });
66
+ afterAll(() => {
67
+ jest.resetAllMocks();
68
+ });
69
+
70
+ test('should call the `onDayClick` from the DayPicker props', () => {
71
+ expect(dayPickerProps.onDayClick).toHaveBeenCalledWith(
72
+ clickedDay,
73
+ activeModifiers,
74
+ event
75
+ );
76
+ });
77
+ test('should call `onSelect` with the clicked day selected', () => {
78
+ expect(dayPickerProps.onSelect).toHaveBeenCalledWith(
79
+ [...selected, clickedDay],
80
+ clickedDay,
81
+ activeModifiers,
82
+ event
83
+ );
84
+ });
85
+ });
86
+ describe('when `onDayClick` is called with a selected day', () => {
87
+ const clickedDay = selectedDay1;
88
+ const activeModifiers: ActiveModifiers = { selected: true };
89
+ beforeAll(() => {
90
+ result.current.onDayClick?.(clickedDay, activeModifiers, event);
91
+ });
92
+ afterAll(() => {
93
+ jest.resetAllMocks();
94
+ });
95
+ const event = {} as React.MouseEvent;
96
+ test('should call the `onDayClick` from the DayPicker props', () => {
97
+ expect(dayPickerProps.onDayClick).toHaveBeenCalledWith(
98
+ clickedDay,
99
+ activeModifiers,
100
+ event
101
+ );
102
+ });
103
+ test('should call `onSelect` without the clicked day selected', () => {
104
+ const expectedSelected = selected.filter((day) => day !== clickedDay);
105
+ expect(dayPickerProps.onSelect).toHaveBeenCalledWith(
106
+ expectedSelected,
107
+ clickedDay,
108
+ activeModifiers,
109
+ event
110
+ );
111
+ });
112
+ });
113
+ });
114
+
115
+ describe('when the maximum number of days are selected', () => {
116
+ const selected = [selectedDay1, selectedDay2, selectedDay3];
117
+ const dayPickerProps: DayPickerMultipleProps = {
118
+ ...initialProps,
119
+ selected,
120
+ max: selected.length
121
+ };
122
+ beforeAll(() => {
123
+ setup(dayPickerProps);
124
+ });
125
+
126
+ test('the selected days should not be disabled', () => {
127
+ const { disabled } = result.current.modifiers;
128
+ expect(isMatch(selectedDay1, disabled)).toBe(false);
129
+ expect(isMatch(selectedDay2, disabled)).toBe(false);
130
+ expect(isMatch(selectedDay3, disabled)).toBe(false);
131
+ });
132
+ test('the other days should be disabled', () => {
133
+ const { disabled } = result.current.modifiers;
134
+ expect(isMatch(addMonths(selectedDay1, 1), disabled)).toBe(true);
135
+ expect(isMatch(addMonths(selectedDay2, 1), disabled)).toBe(true);
136
+ });
137
+ describe('when `onDayClick` is called', () => {
138
+ const clickedDay = addMonths(selectedDay1, 1);
139
+ const activeModifiers: ActiveModifiers = {};
140
+ beforeAll(() => {
141
+ result.current.onDayClick?.(clickedDay, activeModifiers, event);
142
+ });
143
+ afterAll(() => {
144
+ jest.resetAllMocks();
145
+ });
146
+ const event = {} as React.MouseEvent;
147
+ test('should call the `onDayClick` from the DayPicker props', () => {
148
+ expect(dayPickerProps.onDayClick).toHaveBeenCalledWith(
149
+ clickedDay,
150
+ activeModifiers,
151
+ event
152
+ );
153
+ });
154
+ test('should not call `onSelect`', () => {
155
+ expect(dayPickerProps.onSelect).not.toHaveBeenCalled();
156
+ });
157
+ });
158
+ });
159
+
160
+ describe('when the minimum number of days are selected', () => {
161
+ const selected = [selectedDay1, selectedDay2, selectedDay3];
162
+ const dayPickerProps: DayPickerMultipleProps = {
163
+ ...initialProps,
164
+ selected,
165
+ min: selected.length
166
+ };
167
+ beforeAll(() => {
168
+ setup(dayPickerProps);
169
+ });
170
+ describe('when `onDayClick` is called with one of the selected days', () => {
171
+ const clickedDay = selected[0];
172
+ const activeModifiers: ActiveModifiers = { selected: true };
173
+ beforeAll(() => {
174
+ result.current.onDayClick?.(clickedDay, activeModifiers, event);
175
+ });
176
+ afterAll(() => {
177
+ jest.resetAllMocks();
178
+ });
179
+ const event = {} as React.MouseEvent;
180
+ test('should call the `onDayClick` from the DayPicker props', () => {
181
+ expect(dayPickerProps.onDayClick).toHaveBeenCalledWith(
182
+ clickedDay,
183
+ activeModifiers,
184
+ event
185
+ );
186
+ });
187
+ test('should not call `onSelect`', () => {
188
+ expect(dayPickerProps.onSelect).not.toHaveBeenCalled();
189
+ });
190
+ });
191
+ });
@@ -0,0 +1,17 @@
1
+ import { useContext } from 'react';
2
+
3
+ import {
4
+ SelectMultipleContext,
5
+ SelectMultipleContextValue
6
+ } from './SelectMultipleContext';
7
+
8
+ /** Hook to access the [[SelectMultipleContext]]. */
9
+ export function useSelectMultiple(): SelectMultipleContextValue {
10
+ const context = useContext(SelectMultipleContext);
11
+ if (!context) {
12
+ throw new Error(
13
+ 'useSelectMultiple must be used within a SelectMultipleProvider'
14
+ );
15
+ }
16
+ return context;
17
+ }
@@ -0,0 +1,158 @@
1
+ import React, { createContext, ReactNode } from 'react';
2
+
3
+ import differenceInCalendarDays from 'date-fns/differenceInCalendarDays';
4
+ import isAfter from 'date-fns/isAfter';
5
+ import isBefore from 'date-fns/isBefore';
6
+
7
+ import { DayPickerBase } from 'types/DayPickerBase';
8
+ import { DayPickerRangeProps, isDayPickerRange } from 'types/DayPickerRange';
9
+ import { DayClickEventHandler } from 'types/EventHandlers';
10
+ import { DateRange } from 'types/Matchers';
11
+ import { InternalModifier, Modifiers } from 'types/Modifiers';
12
+
13
+ import { addToRange } from './utils/addToRange';
14
+
15
+ /** Represent the modifiers that are changed by the range selection. */
16
+ export type SelectRangeModifiers = Pick<
17
+ Modifiers,
18
+ | InternalModifier.Disabled
19
+ | InternalModifier.RangeEnd
20
+ | InternalModifier.RangeMiddle
21
+ | InternalModifier.RangeStart
22
+ >;
23
+
24
+ /** Represents the value of a [[SelectRangeContext]]. */
25
+ export interface SelectRangeContextValue {
26
+ /** The range of days that has been selected. */
27
+ selected: DateRange | undefined;
28
+ /** The modifiers for the corresponding selection. */
29
+ modifiers: SelectRangeModifiers;
30
+ /** Event handler to attach to the day button to enable the range select. */
31
+ onDayClick?: DayClickEventHandler;
32
+ }
33
+
34
+ /**
35
+ * The SelectRange context shares details about the selected days when in
36
+ * range selection mode.
37
+ *
38
+ * Access this context from the [[useSelectRange]] hook.
39
+ */
40
+ export const SelectRangeContext = createContext<
41
+ SelectRangeContextValue | undefined
42
+ >(undefined);
43
+
44
+ type SelectRangeProviderProps = {
45
+ initialProps: DayPickerBase;
46
+ children: ReactNode;
47
+ };
48
+
49
+ /** Provides the values for the [[SelectRangeProvider]]. */
50
+ export function SelectRangeProvider(
51
+ props: SelectRangeProviderProps
52
+ ): JSX.Element {
53
+ if (!isDayPickerRange(props.initialProps)) {
54
+ const emptyContextValue: SelectRangeContextValue = {
55
+ selected: undefined,
56
+ modifiers: {
57
+ range_start: [],
58
+ range_end: [],
59
+ range_middle: [],
60
+ disabled: []
61
+ }
62
+ };
63
+ return (
64
+ <SelectRangeContext.Provider value={emptyContextValue}>
65
+ {props.children}
66
+ </SelectRangeContext.Provider>
67
+ );
68
+ }
69
+ return (
70
+ <SelectRangeProviderInternal
71
+ initialProps={props.initialProps}
72
+ children={props.children}
73
+ />
74
+ );
75
+ }
76
+
77
+ type SelectRangeProviderInternalProps = {
78
+ initialProps: DayPickerRangeProps;
79
+ children: ReactNode;
80
+ };
81
+
82
+ export function SelectRangeProviderInternal({
83
+ initialProps,
84
+ children
85
+ }: SelectRangeProviderInternalProps): JSX.Element {
86
+ const { selected } = initialProps;
87
+ const { from: selectedFrom, to: selectedTo } = selected || {};
88
+ const min = initialProps.min;
89
+ const max = initialProps.max;
90
+
91
+ const onDayClick: DayClickEventHandler = (day, activeModifiers, e) => {
92
+ initialProps.onDayClick?.(day, activeModifiers, e);
93
+ const range = addToRange(day, selected);
94
+ if (
95
+ (min || max) &&
96
+ selected &&
97
+ range?.to &&
98
+ range.from &&
99
+ range.from !== range.to
100
+ ) {
101
+ const diff = Math.abs(differenceInCalendarDays(range?.to, range?.from));
102
+ if ((min && diff < min) || (max && diff >= max)) {
103
+ return;
104
+ }
105
+ }
106
+ initialProps.onSelect?.(range, day, activeModifiers, e);
107
+ };
108
+
109
+ const modifiers: SelectRangeModifiers = {
110
+ range_start: [],
111
+ range_end: [],
112
+ range_middle: [],
113
+ disabled: []
114
+ };
115
+
116
+ if (selectedFrom) {
117
+ modifiers.range_start = [selectedFrom];
118
+ if (!selectedTo) {
119
+ modifiers.range_end = [selectedFrom];
120
+ } else {
121
+ modifiers.range_end = [selectedTo];
122
+ modifiers.range_middle = [
123
+ {
124
+ after: selectedFrom,
125
+ before: selectedTo
126
+ }
127
+ ];
128
+ }
129
+ }
130
+
131
+ if (min && selectedFrom && selectedTo) {
132
+ modifiers.disabled.push((date: Date) => {
133
+ return (
134
+ (isBefore(date, selectedFrom) &&
135
+ differenceInCalendarDays(selectedFrom, date) < min) ||
136
+ (isAfter(date, selectedTo) &&
137
+ differenceInCalendarDays(date, selectedFrom) < min)
138
+ );
139
+ });
140
+ }
141
+
142
+ if (max && selectedFrom && selectedTo) {
143
+ modifiers.disabled.push((date: Date) => {
144
+ return (
145
+ (isBefore(date, selectedFrom) &&
146
+ differenceInCalendarDays(selectedTo, date) >= max) ||
147
+ (isAfter(date, selectedTo) &&
148
+ differenceInCalendarDays(date, selectedFrom) >= max)
149
+ );
150
+ });
151
+ }
152
+
153
+ return (
154
+ <SelectRangeContext.Provider value={{ selected, onDayClick, modifiers }}>
155
+ {children}
156
+ </SelectRangeContext.Provider>
157
+ );
158
+ }
@@ -0,0 +1,2 @@
1
+ export * from './SelectRangeContext';
2
+ export * from './useSelectRange';