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 @@
1
+ export * from './Table';
@@ -0,0 +1,47 @@
1
+ import type { Locale } from 'date-fns';
2
+
3
+ import addDays from 'date-fns/addDays';
4
+ import differenceInCalendarDays from 'date-fns/differenceInCalendarDays';
5
+ import endOfWeek from 'date-fns/endOfWeek';
6
+ import getWeek from 'date-fns/getWeek';
7
+ import startOfWeek from 'date-fns/startOfWeek';
8
+
9
+ import { MonthWeek } from './getMonthWeeks';
10
+
11
+ /** Return the weeks between two dates. */
12
+ export function daysToMonthWeeks(
13
+ fromDate: Date,
14
+ toDate: Date,
15
+ options?: {
16
+ locale?: Locale;
17
+ weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
18
+ firstWeekContainsDate?: 1 | 2 | 3 | 4 | 5 | 6 | 7;
19
+ }
20
+ ): MonthWeek[] {
21
+ const toWeek = endOfWeek(toDate, options);
22
+ const fromWeek = startOfWeek(fromDate, options);
23
+ const nOfDays = differenceInCalendarDays(toWeek, fromWeek);
24
+ const days: Date[] = [];
25
+
26
+ for (let i = 0; i <= nOfDays; i++) {
27
+ days.push(addDays(fromWeek, i));
28
+ }
29
+
30
+ const weeksInMonth = days.reduce((result: MonthWeek[], date) => {
31
+ const weekNumber = getWeek(date, options);
32
+ const existingWeek = result.find(
33
+ (value) => value.weekNumber === weekNumber
34
+ );
35
+ if (existingWeek) {
36
+ existingWeek.dates.push(date);
37
+ return result;
38
+ }
39
+ result.push({
40
+ weekNumber,
41
+ dates: [date]
42
+ });
43
+ return result;
44
+ }, []);
45
+
46
+ return weeksInMonth;
47
+ }
@@ -0,0 +1,68 @@
1
+ import { enGB, enUS } from 'date-fns/locale';
2
+
3
+ import { getMonthWeeks } from './getMonthWeeks';
4
+
5
+ describe('when using the "enUS" locale', () => {
6
+ const locale = enUS;
7
+ describe('when using fixed weeks', () => {
8
+ const useFixedWeeks = true;
9
+ describe('when getting the weeks for December 2022', () => {
10
+ const date = new Date(2022, 11);
11
+ const weeks = getMonthWeeks(date, { useFixedWeeks, locale });
12
+ test('should return 49 - 1 week numbers', () => {
13
+ const weekNumbers = weeks.map((week) => week.weekNumber);
14
+ const expectedResult = [49, 50, 51, 52, 53, 1];
15
+ expect(weekNumbers).toEqual(expectedResult);
16
+ });
17
+ test('the last week should be the one in the next year', () => {
18
+ const lastWeek = weeks[weeks.length - 1];
19
+ const lastWeekDates = lastWeek.dates.map((date) => date.getDate());
20
+ const expectedResult = [1, 2, 3, 4, 5, 6, 7];
21
+ expect(lastWeekDates).toEqual(expectedResult);
22
+ });
23
+ });
24
+ describe('when getting the weeks for December 2021', () => {
25
+ const weeks = getMonthWeeks(new Date(2021, 11), {
26
+ useFixedWeeks: false,
27
+ locale: enUS
28
+ });
29
+ test('should return 49 - 1 week numbers', () => {
30
+ const weekNumbers = weeks.map((week) => week.weekNumber);
31
+ const expectedResult = [49, 50, 51, 52, 1];
32
+ expect(weekNumbers).toEqual(expectedResult);
33
+ });
34
+ test('the last week should be the last in the year', () => {
35
+ const lastWeek = weeks[weeks.length - 1];
36
+ const lastWeekDates = lastWeek.dates.map((date) => date.getDate());
37
+ const expectedResult = [26, 27, 28, 29, 30, 31, 1];
38
+ expect(lastWeekDates).toEqual(expectedResult);
39
+ });
40
+ test('week 1 contains the first day of the new year', () => {
41
+ expect(weeks[4].dates.map((date) => date.getDate())).toEqual([
42
+ 26, 27, 28, 29, 30, 31, 1
43
+ ]);
44
+ });
45
+ });
46
+ });
47
+ });
48
+
49
+ describe('when using the "enGB" locale', () => {
50
+ const locale = enGB;
51
+ describe('when getting the weeks for January 2022', () => {
52
+ const date = new Date(2022, 0);
53
+ const weeks = getMonthWeeks(date, { locale });
54
+ test('the first week should be the last of the previous year', () => {
55
+ const weekNumbers = weeks.map((week) => week.weekNumber);
56
+ expect(weekNumbers[0]).toEqual(52);
57
+ });
58
+ test('the first week should contain days from previous year', () => {
59
+ expect(weeks[0].dates.map((date) => date.getDate())).toEqual([
60
+ 27, 28, 29, 30, 31, 1, 2
61
+ ]);
62
+ });
63
+ test('the first week should be the last of January', () => {
64
+ const weekNumbers = weeks.map((week) => week.weekNumber);
65
+ expect(weekNumbers[weekNumbers.length - 1]).toEqual(5);
66
+ });
67
+ });
68
+ });
@@ -0,0 +1,55 @@
1
+ import type { Locale } from 'date-fns';
2
+
3
+ import addWeeks from 'date-fns/addWeeks';
4
+ import endOfMonth from 'date-fns/endOfMonth';
5
+ import getWeeksInMonth from 'date-fns/getWeeksInMonth';
6
+ import startOfMonth from 'date-fns/startOfMonth';
7
+
8
+ import { daysToMonthWeeks } from './daysToMonthWeeks';
9
+
10
+ /** Represents a week in the month.*/
11
+ export type MonthWeek = {
12
+ /** The week number from the start of the year. */
13
+ weekNumber: number;
14
+ /** The dates in the week. */
15
+ dates: Date[];
16
+ };
17
+
18
+ /**
19
+ * Return the weeks belonging to the given month, adding the "outside days" to
20
+ * the first and last week.
21
+ */
22
+ export function getMonthWeeks(
23
+ /** The month to get the weeks from */
24
+ month: Date,
25
+ options: {
26
+ locale: Locale;
27
+ /** Add extra weeks up to 6 weeks */
28
+ useFixedWeeks?: boolean;
29
+ weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
30
+ firstWeekContainsDate?: 1 | 2 | 3 | 4 | 5 | 6 | 7;
31
+ }
32
+ ): MonthWeek[] {
33
+ const weeksInMonth: MonthWeek[] = daysToMonthWeeks(
34
+ startOfMonth(month),
35
+ endOfMonth(month),
36
+ options
37
+ );
38
+
39
+ // Add extra weeks to the month, up to 6 weeks
40
+ if (options?.useFixedWeeks) {
41
+ const nrOfMonthWeeks = getWeeksInMonth(month, options);
42
+ if (nrOfMonthWeeks < 6) {
43
+ const lastWeek = weeksInMonth[weeksInMonth.length - 1];
44
+ const lastDate = lastWeek.dates[lastWeek.dates.length - 1];
45
+ const toDate = addWeeks(lastDate, 6 - nrOfMonthWeeks);
46
+ const extraWeeks = daysToMonthWeeks(
47
+ addWeeks(lastDate, 1),
48
+ toDate,
49
+ options
50
+ );
51
+ weeksInMonth.push(...extraWeeks);
52
+ }
53
+ }
54
+ return weeksInMonth;
55
+ }
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+
3
+ import { screen } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
5
+ import { DayPickerProps } from 'DayPicker';
6
+
7
+ import { customRender } from 'test/render/customRender';
8
+
9
+ import { DayPickerBase } from 'types/DayPickerBase';
10
+
11
+ import { WeekNumber, WeekNumberProps } from './WeekNumber';
12
+
13
+ function setup(props: WeekNumberProps, dayPickerProps?: DayPickerProps) {
14
+ return customRender(<WeekNumber {...props} />, dayPickerProps);
15
+ }
16
+
17
+ const props: WeekNumberProps = {
18
+ number: 10,
19
+ dates: [new Date(), new Date()]
20
+ };
21
+
22
+ describe('without "onWeekNumberClick" prop', () => {
23
+ const dayPickerProps: DayPickerBase = { onWeekNumberClick: undefined };
24
+ test('it should return a span element', () => {
25
+ const { container } = setup(props, dayPickerProps);
26
+ expect(container.firstChild).toMatchSnapshot();
27
+ });
28
+ });
29
+
30
+ describe('with "onWeekNumberClick" prop', () => {
31
+ const dayPickerProps: DayPickerBase = { onWeekNumberClick: jest.fn() };
32
+ const { container } = setup(props, dayPickerProps);
33
+ test('it should return a button element', () => {
34
+ expect(container.firstChild).toMatchSnapshot();
35
+ });
36
+ describe('when the button element is clicked', () => {
37
+ userEvent.click(screen.getByRole('button'));
38
+ test('should call onWeekNumberClick', () => {
39
+ expect(dayPickerProps.onWeekNumberClick).toHaveBeenCalledWith(
40
+ props.number,
41
+ props.dates,
42
+ expect.anything()
43
+ );
44
+ });
45
+ });
46
+ });
@@ -0,0 +1,58 @@
1
+ import React from 'react';
2
+
3
+ import { useDayPicker } from 'contexts/DayPicker';
4
+
5
+ import { Button } from '../Button';
6
+
7
+ /**
8
+ * The props for the [[WeekNumber]] component.
9
+ */
10
+ export interface WeekNumberProps {
11
+ /** The number of the week. */
12
+ number: number;
13
+ /** The dates in the week. */
14
+ dates: Date[];
15
+ }
16
+
17
+ /**
18
+ * Render the week number element. If `onWeekNumberClick` is passed to DayPicker, it
19
+ * renders a button, otherwise a span element.
20
+ */
21
+ export function WeekNumber(props: WeekNumberProps): JSX.Element {
22
+ const { number: weekNumber, dates } = props;
23
+ const {
24
+ onWeekNumberClick,
25
+ styles,
26
+ classNames,
27
+ locale,
28
+ labels: { labelWeekNumber },
29
+ formatters: { formatWeekNumber }
30
+ } = useDayPicker();
31
+
32
+ const content = formatWeekNumber(Number(weekNumber), { locale });
33
+
34
+ if (!onWeekNumberClick) {
35
+ return (
36
+ <span className={classNames.weeknumber} style={styles.weeknumber}>
37
+ {content}
38
+ </span>
39
+ );
40
+ }
41
+
42
+ const label = labelWeekNumber(Number(weekNumber), { locale });
43
+
44
+ const handleClick: React.MouseEventHandler = function (e) {
45
+ onWeekNumberClick(weekNumber, dates, e);
46
+ };
47
+
48
+ return (
49
+ <Button
50
+ aria-label={label}
51
+ className={classNames.weeknumber}
52
+ style={styles.weeknumber}
53
+ onClick={handleClick}
54
+ >
55
+ {content}
56
+ </Button>
57
+ );
58
+ }
@@ -0,0 +1,11 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`with "onWeekNumberClick" prop it should return a button element 1`] = `null`;
4
+
5
+ exports[`without "onWeekNumberClick" prop it should return a span element 1`] = `
6
+ <span
7
+ class="rdp-weeknumber"
8
+ >
9
+ 10
10
+ </span>
11
+ `;
@@ -0,0 +1 @@
1
+ export * from './WeekNumber';
@@ -0,0 +1,98 @@
1
+ import React from 'react';
2
+
3
+ import { screen } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
5
+ import { differenceInYears } from 'date-fns';
6
+ import { DayPickerProps } from 'DayPicker';
7
+
8
+ import { customRender } from 'test/render';
9
+ import { freezeBeforeAll } from 'test/utils';
10
+
11
+ import { YearsDropdown, YearsDropdownProps } from './YearsDropdown';
12
+
13
+ const today = new Date(2020, 12, 22);
14
+
15
+ freezeBeforeAll(today);
16
+
17
+ let root: HTMLDivElement;
18
+ let options: HTMLCollectionOf<HTMLOptionElement> | undefined;
19
+ let select: HTMLSelectElement | null;
20
+
21
+ function setup(props: YearsDropdownProps, dayPickerProps?: DayPickerProps) {
22
+ const view = customRender(<YearsDropdown {...props} />, dayPickerProps);
23
+ root = view.container.firstChild as HTMLDivElement;
24
+ select = screen.queryByRole('combobox', { name: 'Year:' });
25
+ options = select?.getElementsByTagName('option');
26
+ }
27
+
28
+ const props: YearsDropdownProps = {
29
+ displayMonth: today,
30
+ onChange: jest.fn()
31
+ };
32
+
33
+ describe('when "fromDate" is not set', () => {
34
+ beforeEach(() => {
35
+ setup(props, { fromDate: undefined });
36
+ });
37
+ test('should return nothing', () => {
38
+ expect(root).toBeNull();
39
+ });
40
+ });
41
+
42
+ describe('when "toDate" is not set', () => {
43
+ beforeEach(() => {
44
+ setup(props, { toDate: undefined });
45
+ });
46
+ test('should return nothing', () => {
47
+ expect(root).toBeNull();
48
+ });
49
+ });
50
+
51
+ describe('when "fromDate" and "toDate" are in the same year', () => {
52
+ const fromDate = new Date(2012, 0, 22);
53
+ const toDate = new Date(2012, 10, 22);
54
+ beforeEach(() => {
55
+ setup(props, { fromDate, toDate });
56
+ });
57
+ test('should display the months included between the two dates', () => {
58
+ expect(select).toBeInTheDocument();
59
+ expect(options).toHaveLength(differenceInYears(toDate, fromDate) + 1);
60
+ });
61
+ test('the month should be the same month', () => {
62
+ expect(options?.[0]).toHaveValue(`${fromDate.getFullYear()}`);
63
+ });
64
+ });
65
+
66
+ describe('when "fromDate" and "toDate" are not in the same year', () => {
67
+ const fromDate = new Date(2012, 0, 22);
68
+ const toDate = new Date(2015, 10, 22);
69
+ const displayMonth = new Date(2013, 7, 0);
70
+ beforeEach(() => {
71
+ setup({ ...props, displayMonth }, { fromDate, toDate });
72
+ });
73
+ test('should display the full years', () => {
74
+ expect(options).toHaveLength(differenceInYears(toDate, fromDate) + 1);
75
+ });
76
+ test('the first option should be fromDates year', () => {
77
+ expect(options?.[0]).toHaveValue(`${fromDate.getFullYear()}`);
78
+ });
79
+ test('the last option should be "toDate"s year', () => {
80
+ expect(options?.[options.length - 1]).toHaveValue(
81
+ `${toDate.getFullYear()}`
82
+ );
83
+ });
84
+ test('should select the displayed year', () => {
85
+ expect(select).toHaveValue(`${displayMonth.getFullYear()}`);
86
+ });
87
+
88
+ describe('when the dropdown changes', () => {
89
+ const newYear = fromDate.getFullYear();
90
+ beforeEach(() => {
91
+ if (select) userEvent.selectOptions(select, `${newYear}`);
92
+ });
93
+ test('should fire the "onChange" event handler', () => {
94
+ const expectedYear = new Date(newYear, displayMonth.getMonth(), 1);
95
+ expect(props.onChange).toHaveBeenCalledWith(expectedYear);
96
+ });
97
+ });
98
+ });
@@ -0,0 +1,76 @@
1
+ import React from 'react';
2
+
3
+ import setYear from 'date-fns/setYear';
4
+ import startOfMonth from 'date-fns/startOfMonth';
5
+ import startOfYear from 'date-fns/startOfYear';
6
+
7
+ import { Dropdown } from 'components/Dropdown';
8
+ import { useDayPicker } from 'contexts/DayPicker';
9
+ import { MonthChangeEventHandler } from 'types/EventHandlers';
10
+
11
+ /**
12
+ * The props for the [[YearsDropdown]] component.
13
+ */
14
+ export interface YearsDropdownProps {
15
+ /** The month where the drop-down is displayed. */
16
+ displayMonth: Date;
17
+ /** Callback to handle the `change` event. */
18
+ onChange: MonthChangeEventHandler;
19
+ }
20
+
21
+ /**
22
+ * Render a dropdown to change the year. Take in account the `nav.fromDate` and
23
+ * `toDate` from context.
24
+ */
25
+ export function YearsDropdown(props: YearsDropdownProps): JSX.Element {
26
+ const { displayMonth } = props;
27
+ const {
28
+ fromDate,
29
+ toDate,
30
+ locale,
31
+ styles,
32
+ classNames,
33
+ components,
34
+ formatters: { formatYearCaption },
35
+ labels: { labelYearDropdown }
36
+ } = useDayPicker();
37
+
38
+ const years: Date[] = [];
39
+
40
+ // Dropdown should appear only when both from/toDate is set
41
+ if (!fromDate) return <></>;
42
+ if (!toDate) return <></>;
43
+
44
+ const fromYear = fromDate.getFullYear();
45
+ const toYear = toDate.getFullYear();
46
+ for (let year = fromYear; year <= toYear; year++) {
47
+ years.push(setYear(startOfYear(new Date()), year));
48
+ }
49
+
50
+ const handleChange: React.ChangeEventHandler<HTMLSelectElement> = (e) => {
51
+ const newMonth = setYear(
52
+ startOfMonth(displayMonth),
53
+ Number(e.target.value)
54
+ );
55
+ props.onChange(newMonth);
56
+ };
57
+
58
+ const DropdownComponent = components?.Dropdown ?? Dropdown;
59
+
60
+ return (
61
+ <DropdownComponent
62
+ aria-label={labelYearDropdown()}
63
+ className={classNames.dropdown_month}
64
+ style={styles.dropdown_month}
65
+ onChange={handleChange}
66
+ value={displayMonth.getFullYear()}
67
+ caption={formatYearCaption(displayMonth, { locale })}
68
+ >
69
+ {years.map((year) => (
70
+ <option key={year.getFullYear()} value={year.getFullYear()}>
71
+ {formatYearCaption(year, { locale })}
72
+ </option>
73
+ ))}
74
+ </DropdownComponent>
75
+ );
76
+ }
@@ -0,0 +1 @@
1
+ export * from './YearsDropdown';
@@ -0,0 +1,156 @@
1
+ import React, { createContext, ReactNode } from 'react';
2
+
3
+ import { DayPickerProps } from 'DayPicker';
4
+
5
+ import { CaptionLayout } from 'components/Caption';
6
+ import { DayPickerBase, DaySelectionMode } from 'types/DayPickerBase';
7
+ import { DayPickerMultipleProps } from 'types/DayPickerMultiple';
8
+ import { DayPickerRangeProps } from 'types/DayPickerRange';
9
+ import { DayPickerSingleProps } from 'types/DayPickerSingle';
10
+ import { Formatters } from 'types/Formatters';
11
+ import { Labels } from 'types/Labels';
12
+ import { Matcher } from 'types/Matchers';
13
+ import { DayModifiers, ModifiersClassNames } from 'types/Modifiers';
14
+ import { ClassNames, Styles } from 'types/Styles';
15
+
16
+ import { getDefaultContextValue } from './defaultContextValue';
17
+ import { parseFromToProps } from './utils';
18
+
19
+ /** The value of the [[DayPickerContext]]. */
20
+ export interface DayPickerContextValue extends DayPickerBase {
21
+ mode: DaySelectionMode;
22
+ onSelect?:
23
+ | DayPickerSingleProps['onSelect']
24
+ | DayPickerMultipleProps['onSelect']
25
+ | DayPickerRangeProps['onSelect'];
26
+ required?: boolean;
27
+ min?: number;
28
+ max?: number;
29
+ selected?: Matcher | Matcher[];
30
+
31
+ captionLayout: CaptionLayout;
32
+ classNames: Required<ClassNames>;
33
+ formatters: Formatters;
34
+ labels: Labels;
35
+ locale: Locale;
36
+ modifiersClassNames: ModifiersClassNames;
37
+ modifiers: DayModifiers;
38
+ numberOfMonths: number;
39
+ styles: Styles;
40
+ today: Date;
41
+ }
42
+
43
+ /**
44
+ * The DayPicker Context shares the props passed to DayPicker within internal
45
+ * and custom components. It is used to set the default values and perform
46
+ * one-time calculations required to render the days.
47
+ *
48
+ * Developers may access this context from the [[useDayPicker]] hook when
49
+ * using custom components.
50
+ */
51
+ export const DayPickerContext = createContext<
52
+ DayPickerContextValue | undefined
53
+ >(undefined);
54
+
55
+ /** The props for the [[DayPickerProvider]]. */
56
+ export interface DayPickerProviderProps {
57
+ /** The initial props from the DayPicker component. */
58
+ initialProps: DayPickerProps;
59
+ children?: ReactNode;
60
+ }
61
+ /**
62
+ * The provider for the [[DayPickerContext]], assigning the defaults from the
63
+ * initial DayPicker props.
64
+ */
65
+ export function DayPickerProvider(props: DayPickerProviderProps): JSX.Element {
66
+ const { initialProps } = props;
67
+
68
+ const defaults = getDefaultContextValue();
69
+
70
+ const { fromDate, toDate } = parseFromToProps(initialProps);
71
+
72
+ let captionLayout = initialProps.captionLayout ?? defaults.captionLayout;
73
+
74
+ if (captionLayout !== 'buttons' && (!fromDate || !toDate)) {
75
+ captionLayout = 'buttons';
76
+ }
77
+
78
+ const value: DayPickerContextValue = {
79
+ captionLayout,
80
+ className: initialProps.className,
81
+ classNames: {
82
+ ...defaults.classNames,
83
+ ...initialProps.classNames
84
+ },
85
+ components: {
86
+ ...defaults.components,
87
+ ...initialProps.components
88
+ },
89
+ defaultMonth: initialProps.defaultMonth,
90
+ dir: initialProps.dir,
91
+ disabled: initialProps.disabled,
92
+ disableNavigation: initialProps.disableNavigation,
93
+ fixedWeeks: initialProps.fixedWeeks,
94
+ footer: initialProps.footer,
95
+ formatters: {
96
+ ...defaults.formatters,
97
+ ...initialProps.formatters
98
+ },
99
+ fromDate,
100
+ hidden: initialProps.hidden,
101
+ hideHead: initialProps.hideHead,
102
+ initialFocus: initialProps.initialFocus,
103
+ labels: {
104
+ ...defaults.labels,
105
+ ...initialProps.labels
106
+ },
107
+ locale: initialProps.locale ?? defaults.locale,
108
+ mode: initialProps.mode || 'default',
109
+ modifiers: {
110
+ ...defaults.modifiers,
111
+ ...initialProps.modifiers
112
+ },
113
+ modifiersClassNames: {
114
+ ...defaults.modifiersClassNames,
115
+ ...initialProps.modifiersClassNames
116
+ },
117
+ modifiersStyles: initialProps.modifiersStyles,
118
+ month: initialProps.month,
119
+ numberOfMonths: initialProps.numberOfMonths ?? defaults.numberOfMonths,
120
+ onDayBlur: initialProps.onDayBlur,
121
+ onDayClick: initialProps.onDayClick,
122
+ onDayFocus: initialProps.onDayFocus,
123
+ onDayKeyDown: initialProps.onDayKeyDown,
124
+ onDayKeyPress: initialProps.onDayKeyPress,
125
+ onDayKeyUp: initialProps.onDayKeyUp,
126
+ onDayMouseEnter: initialProps.onDayMouseEnter,
127
+ onDayMouseLeave: initialProps.onDayMouseLeave,
128
+ onDayTouchCancel: initialProps.onDayTouchCancel,
129
+ onDayTouchEnd: initialProps.onDayTouchEnd,
130
+ onDayTouchMove: initialProps.onDayTouchMove,
131
+ onDayTouchStart: initialProps.onDayTouchStart,
132
+ onMonthChange: initialProps.onMonthChange,
133
+ onNextClick: initialProps.onNextClick,
134
+ onPrevClick: initialProps.onPrevClick,
135
+ onWeekNumberClick: initialProps.onWeekNumberClick,
136
+ pagedNavigation: initialProps.pagedNavigation,
137
+ reverseMonths: initialProps.reverseMonths,
138
+ selected: initialProps.selected,
139
+ showOutsideDays: initialProps.showOutsideDays,
140
+ showWeekNumber: initialProps.showWeekNumber,
141
+ style: initialProps.style,
142
+ styles: {
143
+ ...defaults.styles,
144
+ ...initialProps.styles
145
+ },
146
+ toDate,
147
+ today: initialProps.today ?? defaults.today,
148
+ weekStartsOn: initialProps.weekStartsOn
149
+ };
150
+
151
+ return (
152
+ <DayPickerContext.Provider value={value}>
153
+ {props.children}
154
+ </DayPickerContext.Provider>
155
+ );
156
+ }
@@ -0,0 +1,58 @@
1
+ import { ClassNames } from 'types/Styles';
2
+
3
+ /**
4
+ * The name of the default CSS classes.
5
+ */
6
+ export const defaultClassNames: Required<ClassNames> = {
7
+ root: 'rdp',
8
+ multiple_months: 'rdp-multiple_months',
9
+ with_weeknumber: 'rdp-with_weeknumber',
10
+ vhidden: 'rdp-vhidden',
11
+ button_reset: 'rdp-button_reset',
12
+ button: 'rdp-button',
13
+
14
+ caption: 'rdp-caption',
15
+
16
+ caption_start: 'rdp-caption_start',
17
+ caption_end: 'rdp-caption_end',
18
+ caption_between: 'rdp-caption_between',
19
+ caption_label: 'rdp-caption_label',
20
+
21
+ caption_dropdowns: 'rdp-caption_dropdowns',
22
+
23
+ dropdown: 'rdp-dropdown',
24
+ dropdown_month: 'rdp-dropdown_month',
25
+ dropdown_year: 'rdp-dropdown_year',
26
+ dropdown_icon: 'rdp-dropdown_icon',
27
+
28
+ months: 'rdp-months',
29
+ month: 'rdp-month',
30
+ table: 'rdp-table',
31
+ tbody: 'rdp-tbody',
32
+ tfoot: 'rdp-tfoot',
33
+
34
+ head: 'rdp-head',
35
+ head_row: 'rdp-head_row',
36
+ head_cell: 'rdp-head_cell',
37
+
38
+ nav: 'rdp-nav',
39
+ nav_button: 'rdp-nav_button',
40
+ nav_button_previous: 'rdp-nav_button_previous',
41
+ nav_button_next: 'rdp-nav_button_next',
42
+
43
+ nav_icon: 'rdp-nav_icon',
44
+
45
+ row: 'rdp-row',
46
+ weeknumber: 'rdp-weeknumber',
47
+ cell: 'rdp-cell',
48
+
49
+ day: 'rdp-day',
50
+ day_today: 'rdp-day_today',
51
+ day_outside: 'rdp-day_outside',
52
+ day_selected: 'rdp-day_selected',
53
+ day_disabled: 'rdp-day_disabled',
54
+ day_hidden: 'rdp-day_hidden',
55
+ day_range_start: 'rdp-day_range_start',
56
+ day_range_end: 'rdp-day_range_end',
57
+ day_range_middle: 'rdp-day_range_middle'
58
+ };