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.
- package/dist/components/Head/utils/getWeekdays.d.ts +1 -1
- package/dist/components/Table/utils/daysToMonthWeeks.d.ts +1 -1
- package/dist/components/Table/utils/getMonthWeeks.d.ts +1 -1
- package/dist/contexts/DayPicker/formatters/formatCaption.d.ts +1 -1
- package/dist/contexts/DayPicker/formatters/formatDay.d.ts +1 -1
- package/dist/contexts/DayPicker/formatters/formatMonthCaption.d.ts +1 -1
- package/dist/contexts/DayPicker/formatters/formatWeekdayName.d.ts +1 -1
- package/dist/hooks/useDayRender/useDayRender.d.ts +1 -1
- package/dist/index.esm.js +30 -7
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +154 -107
- package/dist/index.js.map +1 -1
- package/dist/react-day-picker.min.js +1 -1
- package/dist/style.css +11 -10
- package/dist/style.module.css +11 -10
- package/dist/types/DayPickerBase.d.ts +1 -1
- package/dist/types/Labels.d.ts +1 -1
- package/package.json +15 -14
- 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 +311 -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,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,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
|
+
}
|