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.
- package/package.json +13 -11
- package/src/DayPicker.tsx +113 -0
- package/src/components/Button/Button.test.tsx +47 -0
- package/src/components/Button/Button.tsx +36 -0
- package/src/components/Button/index.ts +1 -0
- package/src/components/Caption/Caption.test.tsx +86 -0
- package/src/components/Caption/Caption.tsx +54 -0
- package/src/components/Caption/index.ts +1 -0
- package/src/components/CaptionDropdowns/CaptionDropdowns.test.tsx +123 -0
- package/src/components/CaptionDropdowns/CaptionDropdowns.tsx +43 -0
- package/src/components/CaptionDropdowns/index.ts +1 -0
- package/src/components/CaptionLabel/CaptionLabel.test.tsx +29 -0
- package/src/components/CaptionLabel/CaptionLabel.tsx +32 -0
- package/src/components/CaptionLabel/index.ts +1 -0
- package/src/components/CaptionNavigation/CaptionNavigation.test.tsx +172 -0
- package/src/components/CaptionNavigation/CaptionNavigation.tsx +63 -0
- package/src/components/CaptionNavigation/index.ts +1 -0
- package/src/components/Day/Day.test.tsx +84 -0
- package/src/components/Day/Day.tsx +30 -0
- package/src/components/Day/index.ts +1 -0
- package/src/components/DayContent/DayContent.test.tsx +51 -0
- package/src/components/DayContent/DayContent.tsx +36 -0
- package/src/components/DayContent/index.ts +1 -0
- package/src/components/Dropdown/Dropdown.test.tsx +73 -0
- package/src/components/Dropdown/Dropdown.tsx +56 -0
- package/src/components/Dropdown/index.ts +1 -0
- package/src/components/Footer/Footer.test.tsx +29 -0
- package/src/components/Footer/Footer.tsx +20 -0
- package/src/components/Footer/index.ts +1 -0
- package/src/components/Head/Head.test.tsx +117 -0
- package/src/components/Head/Head.tsx +51 -0
- package/src/components/Head/index.ts +1 -0
- package/src/components/Head/utils/getWeekdays.test.ts +36 -0
- package/src/components/Head/utils/getWeekdays.ts +22 -0
- package/src/components/Head/utils/index.ts +1 -0
- package/src/components/IconDropdown/IconDropdown.test.tsx +20 -0
- package/src/components/IconDropdown/IconDropdown.tsx +24 -0
- package/src/components/IconDropdown/index.ts +1 -0
- package/src/components/IconLeft/IconLeft.test.tsx +20 -0
- package/src/components/IconLeft/IconLeft.tsx +18 -0
- package/src/components/IconLeft/index.ts +1 -0
- package/src/components/IconRight/IconRight.test.tsx +20 -0
- package/src/components/IconRight/IconRight.tsx +17 -0
- package/src/components/IconRight/index.ts +1 -0
- package/src/components/Month/Month.test.tsx +216 -0
- package/src/components/Month/Month.tsx +53 -0
- package/src/components/Month/index.ts +1 -0
- package/src/components/MonthsDropdown/MonthsDropdown.test.tsx +99 -0
- package/src/components/MonthsDropdown/MonthsDropdown.tsx +75 -0
- package/src/components/MonthsDropdown/index.ts +1 -0
- package/src/components/Navigation/Navigation.test.tsx +129 -0
- package/src/components/Navigation/Navigation.tsx +102 -0
- package/src/components/Navigation/index.ts +1 -0
- package/src/components/Root/Root.test.tsx +123 -0
- package/src/components/Root/Root.tsx +58 -0
- package/src/components/Root/index.ts +1 -0
- package/src/components/Row/Row.test.tsx +69 -0
- package/src/components/Row/Row.tsx +51 -0
- package/src/components/Row/index.ts +1 -0
- package/src/components/Table/Table.test.tsx +42 -0
- package/src/components/Table/Table.tsx +60 -0
- package/src/components/Table/__snapshots__/Table.test.tsx.snap +1453 -0
- package/src/components/Table/index.ts +1 -0
- package/src/components/Table/utils/daysToMonthWeeks.ts +47 -0
- package/src/components/Table/utils/getMonthWeeks.test.ts +68 -0
- package/src/components/Table/utils/getMonthWeeks.ts +55 -0
- package/src/components/WeekNumber/WeekNumber.test.tsx +46 -0
- package/src/components/WeekNumber/WeekNumber.tsx +58 -0
- package/src/components/WeekNumber/__snapshots__/WeekNumber.test.tsx.snap +11 -0
- package/src/components/WeekNumber/index.ts +1 -0
- package/src/components/YearsDropdown/YearsDropdown.test.tsx +98 -0
- package/src/components/YearsDropdown/YearsDropdown.tsx +76 -0
- package/src/components/YearsDropdown/index.ts +1 -0
- package/src/contexts/DayPicker/DayPickerContext.tsx +156 -0
- package/src/contexts/DayPicker/defaultClassNames.ts +58 -0
- package/src/contexts/DayPicker/defaultContextValue.ts +37 -0
- package/src/contexts/DayPicker/formatters/formatCaption.test.ts +15 -0
- package/src/contexts/DayPicker/formatters/formatCaption.ts +12 -0
- package/src/contexts/DayPicker/formatters/formatDay.test.ts +7 -0
- package/src/contexts/DayPicker/formatters/formatDay.ts +9 -0
- package/src/contexts/DayPicker/formatters/formatMonthCaption.test.ts +15 -0
- package/src/contexts/DayPicker/formatters/formatMonthCaption.ts +12 -0
- package/src/contexts/DayPicker/formatters/formatWeekNumber.test.ts +5 -0
- package/src/contexts/DayPicker/formatters/formatWeekNumber.ts +6 -0
- package/src/contexts/DayPicker/formatters/formatWeekdayName.test.ts +15 -0
- package/src/contexts/DayPicker/formatters/formatWeekdayName.ts +12 -0
- package/src/contexts/DayPicker/formatters/formatYearCaption.test.ts +7 -0
- package/src/contexts/DayPicker/formatters/formatYearCaption.ts +11 -0
- package/src/contexts/DayPicker/formatters/index.ts +6 -0
- package/src/contexts/DayPicker/index.ts +2 -0
- package/src/contexts/DayPicker/labels/index.ts +7 -0
- package/src/contexts/DayPicker/labels/labelDay.test.ts +7 -0
- package/src/contexts/DayPicker/labels/labelDay.ts +10 -0
- package/src/contexts/DayPicker/labels/labelMonthDropdown.test.ts +5 -0
- package/src/contexts/DayPicker/labels/labelMonthDropdown.ts +6 -0
- package/src/contexts/DayPicker/labels/labelNext.test.ts +5 -0
- package/src/contexts/DayPicker/labels/labelNext.ts +8 -0
- package/src/contexts/DayPicker/labels/labelPrevious.test.ts +5 -0
- package/src/contexts/DayPicker/labels/labelPrevious.ts +8 -0
- package/src/contexts/DayPicker/labels/labelWeekNumber.test.ts +5 -0
- package/src/contexts/DayPicker/labels/labelWeekNumber.ts +8 -0
- package/src/contexts/DayPicker/labels/labelWeekday.test.ts +15 -0
- package/src/contexts/DayPicker/labels/labelWeekday.ts +10 -0
- package/src/contexts/DayPicker/labels/labelYearDropdown.test.ts +5 -0
- package/src/contexts/DayPicker/labels/labelYearDropdown.ts +6 -0
- package/src/contexts/DayPicker/useDayPicker.test.ts +297 -0
- package/src/contexts/DayPicker/useDayPicker.ts +17 -0
- package/src/contexts/DayPicker/utils/index.ts +1 -0
- package/src/contexts/DayPicker/utils/parseFromToProps.test.ts +47 -0
- package/src/contexts/DayPicker/utils/parseFromToProps.ts +32 -0
- package/src/contexts/Focus/FocusContext.tsx +174 -0
- package/src/contexts/Focus/index.ts +2 -0
- package/src/contexts/Focus/useFocusContext.test.ts +183 -0
- package/src/contexts/Focus/useFocusContext.ts +12 -0
- package/src/contexts/Focus/utils/getInitialFocusTarget.test.tsx +12 -0
- package/src/contexts/Focus/utils/getInitialFocusTarget.tsx +44 -0
- package/src/contexts/Modifiers/ModifiersContext.tsx +44 -0
- package/src/contexts/Modifiers/index.ts +2 -0
- package/src/contexts/Modifiers/useModifiers.test.ts +46 -0
- package/src/contexts/Modifiers/useModifiers.ts +17 -0
- package/src/contexts/Modifiers/utils/getActiveModifiers.test.ts +53 -0
- package/src/contexts/Modifiers/utils/getActiveModifiers.ts +33 -0
- package/src/contexts/Modifiers/utils/getCustomModifiers.test.ts +14 -0
- package/src/contexts/Modifiers/utils/getCustomModifiers.ts +14 -0
- package/src/contexts/Modifiers/utils/getInternalModifiers.test.ts +146 -0
- package/src/contexts/Modifiers/utils/getInternalModifiers.ts +58 -0
- package/src/contexts/Modifiers/utils/isDateInRange.test.ts +28 -0
- package/src/contexts/Modifiers/utils/isDateInRange.ts +27 -0
- package/src/contexts/Modifiers/utils/isMatch.test.ts +92 -0
- package/src/contexts/Modifiers/utils/isMatch.ts +76 -0
- package/src/contexts/Modifiers/utils/matcherToArray.test.ts +22 -0
- package/src/contexts/Modifiers/utils/matcherToArray.ts +14 -0
- package/src/contexts/Navigation/NavigationContext.tsx +84 -0
- package/src/contexts/Navigation/index.ts +2 -0
- package/src/contexts/Navigation/useNavigation.test.ts +126 -0
- package/src/contexts/Navigation/useNavigation.ts +12 -0
- package/src/contexts/Navigation/useNavigationState.test.ts +36 -0
- package/src/contexts/Navigation/useNavigationState.ts +25 -0
- package/src/contexts/Navigation/utils/getDisplayMonths.ts +31 -0
- package/src/contexts/Navigation/utils/getInitialMonth.test.ts +56 -0
- package/src/contexts/Navigation/utils/getInitialMonth.ts +24 -0
- package/src/contexts/Navigation/utils/getNextMonth.test.ts +75 -0
- package/src/contexts/Navigation/utils/getNextMonth.ts +45 -0
- package/src/contexts/Navigation/utils/getPreviousMonth.test.ts +55 -0
- package/src/contexts/Navigation/utils/getPreviousMonth.ts +44 -0
- package/src/contexts/RootProvider.tsx +37 -0
- package/src/contexts/SelectMultiple/SelectMultipleContext.tsx +135 -0
- package/src/contexts/SelectMultiple/index.ts +2 -0
- package/src/contexts/SelectMultiple/useSelectMultiple.test.ts +191 -0
- package/src/contexts/SelectMultiple/useSelectMultiple.ts +17 -0
- package/src/contexts/SelectRange/SelectRangeContext.tsx +158 -0
- package/src/contexts/SelectRange/index.ts +2 -0
- package/src/contexts/SelectRange/useSelectRange.test.ts +282 -0
- package/src/contexts/SelectRange/useSelectRange.ts +15 -0
- package/src/contexts/SelectRange/utils/addToRange.test.ts +119 -0
- package/src/contexts/SelectRange/utils/addToRange.ts +43 -0
- package/src/contexts/SelectSingle/SelectSingleContext.tsx +80 -0
- package/src/contexts/SelectSingle/index.ts +2 -0
- package/src/contexts/SelectSingle/useSelectSingle.test.ts +81 -0
- package/src/contexts/SelectSingle/useSelectSingle.ts +17 -0
- package/src/hooks/useActiveModifiers/index.ts +1 -0
- package/src/hooks/useActiveModifiers/useActiveModifiers.test.tsx +36 -0
- package/src/hooks/useActiveModifiers/useActiveModifiers.tsx +18 -0
- package/src/hooks/useControlledValue/index.ts +1 -0
- package/src/hooks/useControlledValue/useControlledValue.test.ts +68 -0
- package/src/hooks/useControlledValue/useControlledValue.ts +24 -0
- package/src/hooks/useDayEventHandlers/index.ts +1 -0
- package/src/hooks/useDayEventHandlers/useDayEventHandlers.test.tsx +213 -0
- package/src/hooks/useDayEventHandlers/useDayEventHandlers.tsx +195 -0
- package/src/hooks/useDayRender/index.ts +1 -0
- package/src/hooks/useDayRender/useDayRender.test.tsx +304 -0
- package/src/hooks/useDayRender/useDayRender.tsx +123 -0
- package/src/hooks/useDayRender/utils/getDayClassNames.test.ts +63 -0
- package/src/hooks/useDayRender/utils/getDayClassNames.ts +32 -0
- package/src/hooks/useDayRender/utils/getDayStyle.ts +19 -0
- package/src/hooks/useInput/index.ts +1 -0
- package/src/hooks/useInput/useInput.ts +175 -0
- package/src/hooks/useInput/utils/isValidDate.tsx +4 -0
- package/src/hooks/useSelectedDays/index.ts +1 -0
- package/src/hooks/useSelectedDays/useSelectedDays.test.ts +72 -0
- package/src/hooks/useSelectedDays/useSelectedDays.ts +32 -0
- package/src/index.ts +43 -0
- package/src/style.css +310 -0
- package/src/style.css.d.ts +38 -0
- package/src/types/DayPickerBase.ts +267 -0
- package/src/types/DayPickerDefault.ts +15 -0
- package/src/types/DayPickerMultiple.ts +26 -0
- package/src/types/DayPickerRange.ts +27 -0
- package/src/types/DayPickerSingle.ts +24 -0
- package/src/types/EventHandlers.ts +87 -0
- package/src/types/Formatters.ts +29 -0
- package/src/types/Labels.ts +36 -0
- package/src/types/Matchers.ts +106 -0
- package/src/types/Modifiers.ts +62 -0
- package/src/types/Styles.ts +108 -0
- 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,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
|
+
}
|