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.
Files changed (196) hide show
  1. package/package.json +13 -11
  2. package/src/DayPicker.tsx +113 -0
  3. package/src/components/Button/Button.test.tsx +47 -0
  4. package/src/components/Button/Button.tsx +36 -0
  5. package/src/components/Button/index.ts +1 -0
  6. package/src/components/Caption/Caption.test.tsx +86 -0
  7. package/src/components/Caption/Caption.tsx +54 -0
  8. package/src/components/Caption/index.ts +1 -0
  9. package/src/components/CaptionDropdowns/CaptionDropdowns.test.tsx +123 -0
  10. package/src/components/CaptionDropdowns/CaptionDropdowns.tsx +43 -0
  11. package/src/components/CaptionDropdowns/index.ts +1 -0
  12. package/src/components/CaptionLabel/CaptionLabel.test.tsx +29 -0
  13. package/src/components/CaptionLabel/CaptionLabel.tsx +32 -0
  14. package/src/components/CaptionLabel/index.ts +1 -0
  15. package/src/components/CaptionNavigation/CaptionNavigation.test.tsx +172 -0
  16. package/src/components/CaptionNavigation/CaptionNavigation.tsx +63 -0
  17. package/src/components/CaptionNavigation/index.ts +1 -0
  18. package/src/components/Day/Day.test.tsx +84 -0
  19. package/src/components/Day/Day.tsx +30 -0
  20. package/src/components/Day/index.ts +1 -0
  21. package/src/components/DayContent/DayContent.test.tsx +51 -0
  22. package/src/components/DayContent/DayContent.tsx +36 -0
  23. package/src/components/DayContent/index.ts +1 -0
  24. package/src/components/Dropdown/Dropdown.test.tsx +73 -0
  25. package/src/components/Dropdown/Dropdown.tsx +56 -0
  26. package/src/components/Dropdown/index.ts +1 -0
  27. package/src/components/Footer/Footer.test.tsx +29 -0
  28. package/src/components/Footer/Footer.tsx +20 -0
  29. package/src/components/Footer/index.ts +1 -0
  30. package/src/components/Head/Head.test.tsx +117 -0
  31. package/src/components/Head/Head.tsx +51 -0
  32. package/src/components/Head/index.ts +1 -0
  33. package/src/components/Head/utils/getWeekdays.test.ts +36 -0
  34. package/src/components/Head/utils/getWeekdays.ts +22 -0
  35. package/src/components/Head/utils/index.ts +1 -0
  36. package/src/components/IconDropdown/IconDropdown.test.tsx +20 -0
  37. package/src/components/IconDropdown/IconDropdown.tsx +24 -0
  38. package/src/components/IconDropdown/index.ts +1 -0
  39. package/src/components/IconLeft/IconLeft.test.tsx +20 -0
  40. package/src/components/IconLeft/IconLeft.tsx +18 -0
  41. package/src/components/IconLeft/index.ts +1 -0
  42. package/src/components/IconRight/IconRight.test.tsx +20 -0
  43. package/src/components/IconRight/IconRight.tsx +17 -0
  44. package/src/components/IconRight/index.ts +1 -0
  45. package/src/components/Month/Month.test.tsx +216 -0
  46. package/src/components/Month/Month.tsx +53 -0
  47. package/src/components/Month/index.ts +1 -0
  48. package/src/components/MonthsDropdown/MonthsDropdown.test.tsx +99 -0
  49. package/src/components/MonthsDropdown/MonthsDropdown.tsx +75 -0
  50. package/src/components/MonthsDropdown/index.ts +1 -0
  51. package/src/components/Navigation/Navigation.test.tsx +129 -0
  52. package/src/components/Navigation/Navigation.tsx +102 -0
  53. package/src/components/Navigation/index.ts +1 -0
  54. package/src/components/Root/Root.test.tsx +123 -0
  55. package/src/components/Root/Root.tsx +58 -0
  56. package/src/components/Root/index.ts +1 -0
  57. package/src/components/Row/Row.test.tsx +69 -0
  58. package/src/components/Row/Row.tsx +51 -0
  59. package/src/components/Row/index.ts +1 -0
  60. package/src/components/Table/Table.test.tsx +42 -0
  61. package/src/components/Table/Table.tsx +60 -0
  62. package/src/components/Table/__snapshots__/Table.test.tsx.snap +1453 -0
  63. package/src/components/Table/index.ts +1 -0
  64. package/src/components/Table/utils/daysToMonthWeeks.ts +47 -0
  65. package/src/components/Table/utils/getMonthWeeks.test.ts +68 -0
  66. package/src/components/Table/utils/getMonthWeeks.ts +55 -0
  67. package/src/components/WeekNumber/WeekNumber.test.tsx +46 -0
  68. package/src/components/WeekNumber/WeekNumber.tsx +58 -0
  69. package/src/components/WeekNumber/__snapshots__/WeekNumber.test.tsx.snap +11 -0
  70. package/src/components/WeekNumber/index.ts +1 -0
  71. package/src/components/YearsDropdown/YearsDropdown.test.tsx +98 -0
  72. package/src/components/YearsDropdown/YearsDropdown.tsx +76 -0
  73. package/src/components/YearsDropdown/index.ts +1 -0
  74. package/src/contexts/DayPicker/DayPickerContext.tsx +156 -0
  75. package/src/contexts/DayPicker/defaultClassNames.ts +58 -0
  76. package/src/contexts/DayPicker/defaultContextValue.ts +37 -0
  77. package/src/contexts/DayPicker/formatters/formatCaption.test.ts +15 -0
  78. package/src/contexts/DayPicker/formatters/formatCaption.ts +12 -0
  79. package/src/contexts/DayPicker/formatters/formatDay.test.ts +7 -0
  80. package/src/contexts/DayPicker/formatters/formatDay.ts +9 -0
  81. package/src/contexts/DayPicker/formatters/formatMonthCaption.test.ts +15 -0
  82. package/src/contexts/DayPicker/formatters/formatMonthCaption.ts +12 -0
  83. package/src/contexts/DayPicker/formatters/formatWeekNumber.test.ts +5 -0
  84. package/src/contexts/DayPicker/formatters/formatWeekNumber.ts +6 -0
  85. package/src/contexts/DayPicker/formatters/formatWeekdayName.test.ts +15 -0
  86. package/src/contexts/DayPicker/formatters/formatWeekdayName.ts +12 -0
  87. package/src/contexts/DayPicker/formatters/formatYearCaption.test.ts +7 -0
  88. package/src/contexts/DayPicker/formatters/formatYearCaption.ts +11 -0
  89. package/src/contexts/DayPicker/formatters/index.ts +6 -0
  90. package/src/contexts/DayPicker/index.ts +2 -0
  91. package/src/contexts/DayPicker/labels/index.ts +7 -0
  92. package/src/contexts/DayPicker/labels/labelDay.test.ts +7 -0
  93. package/src/contexts/DayPicker/labels/labelDay.ts +10 -0
  94. package/src/contexts/DayPicker/labels/labelMonthDropdown.test.ts +5 -0
  95. package/src/contexts/DayPicker/labels/labelMonthDropdown.ts +6 -0
  96. package/src/contexts/DayPicker/labels/labelNext.test.ts +5 -0
  97. package/src/contexts/DayPicker/labels/labelNext.ts +8 -0
  98. package/src/contexts/DayPicker/labels/labelPrevious.test.ts +5 -0
  99. package/src/contexts/DayPicker/labels/labelPrevious.ts +8 -0
  100. package/src/contexts/DayPicker/labels/labelWeekNumber.test.ts +5 -0
  101. package/src/contexts/DayPicker/labels/labelWeekNumber.ts +8 -0
  102. package/src/contexts/DayPicker/labels/labelWeekday.test.ts +15 -0
  103. package/src/contexts/DayPicker/labels/labelWeekday.ts +10 -0
  104. package/src/contexts/DayPicker/labels/labelYearDropdown.test.ts +5 -0
  105. package/src/contexts/DayPicker/labels/labelYearDropdown.ts +6 -0
  106. package/src/contexts/DayPicker/useDayPicker.test.ts +297 -0
  107. package/src/contexts/DayPicker/useDayPicker.ts +17 -0
  108. package/src/contexts/DayPicker/utils/index.ts +1 -0
  109. package/src/contexts/DayPicker/utils/parseFromToProps.test.ts +47 -0
  110. package/src/contexts/DayPicker/utils/parseFromToProps.ts +32 -0
  111. package/src/contexts/Focus/FocusContext.tsx +174 -0
  112. package/src/contexts/Focus/index.ts +2 -0
  113. package/src/contexts/Focus/useFocusContext.test.ts +183 -0
  114. package/src/contexts/Focus/useFocusContext.ts +12 -0
  115. package/src/contexts/Focus/utils/getInitialFocusTarget.test.tsx +12 -0
  116. package/src/contexts/Focus/utils/getInitialFocusTarget.tsx +44 -0
  117. package/src/contexts/Modifiers/ModifiersContext.tsx +44 -0
  118. package/src/contexts/Modifiers/index.ts +2 -0
  119. package/src/contexts/Modifiers/useModifiers.test.ts +46 -0
  120. package/src/contexts/Modifiers/useModifiers.ts +17 -0
  121. package/src/contexts/Modifiers/utils/getActiveModifiers.test.ts +53 -0
  122. package/src/contexts/Modifiers/utils/getActiveModifiers.ts +33 -0
  123. package/src/contexts/Modifiers/utils/getCustomModifiers.test.ts +14 -0
  124. package/src/contexts/Modifiers/utils/getCustomModifiers.ts +14 -0
  125. package/src/contexts/Modifiers/utils/getInternalModifiers.test.ts +146 -0
  126. package/src/contexts/Modifiers/utils/getInternalModifiers.ts +58 -0
  127. package/src/contexts/Modifiers/utils/isDateInRange.test.ts +28 -0
  128. package/src/contexts/Modifiers/utils/isDateInRange.ts +27 -0
  129. package/src/contexts/Modifiers/utils/isMatch.test.ts +92 -0
  130. package/src/contexts/Modifiers/utils/isMatch.ts +76 -0
  131. package/src/contexts/Modifiers/utils/matcherToArray.test.ts +22 -0
  132. package/src/contexts/Modifiers/utils/matcherToArray.ts +14 -0
  133. package/src/contexts/Navigation/NavigationContext.tsx +84 -0
  134. package/src/contexts/Navigation/index.ts +2 -0
  135. package/src/contexts/Navigation/useNavigation.test.ts +126 -0
  136. package/src/contexts/Navigation/useNavigation.ts +12 -0
  137. package/src/contexts/Navigation/useNavigationState.test.ts +36 -0
  138. package/src/contexts/Navigation/useNavigationState.ts +25 -0
  139. package/src/contexts/Navigation/utils/getDisplayMonths.ts +31 -0
  140. package/src/contexts/Navigation/utils/getInitialMonth.test.ts +56 -0
  141. package/src/contexts/Navigation/utils/getInitialMonth.ts +24 -0
  142. package/src/contexts/Navigation/utils/getNextMonth.test.ts +75 -0
  143. package/src/contexts/Navigation/utils/getNextMonth.ts +45 -0
  144. package/src/contexts/Navigation/utils/getPreviousMonth.test.ts +55 -0
  145. package/src/contexts/Navigation/utils/getPreviousMonth.ts +44 -0
  146. package/src/contexts/RootProvider.tsx +37 -0
  147. package/src/contexts/SelectMultiple/SelectMultipleContext.tsx +135 -0
  148. package/src/contexts/SelectMultiple/index.ts +2 -0
  149. package/src/contexts/SelectMultiple/useSelectMultiple.test.ts +191 -0
  150. package/src/contexts/SelectMultiple/useSelectMultiple.ts +17 -0
  151. package/src/contexts/SelectRange/SelectRangeContext.tsx +158 -0
  152. package/src/contexts/SelectRange/index.ts +2 -0
  153. package/src/contexts/SelectRange/useSelectRange.test.ts +282 -0
  154. package/src/contexts/SelectRange/useSelectRange.ts +15 -0
  155. package/src/contexts/SelectRange/utils/addToRange.test.ts +119 -0
  156. package/src/contexts/SelectRange/utils/addToRange.ts +43 -0
  157. package/src/contexts/SelectSingle/SelectSingleContext.tsx +80 -0
  158. package/src/contexts/SelectSingle/index.ts +2 -0
  159. package/src/contexts/SelectSingle/useSelectSingle.test.ts +81 -0
  160. package/src/contexts/SelectSingle/useSelectSingle.ts +17 -0
  161. package/src/hooks/useActiveModifiers/index.ts +1 -0
  162. package/src/hooks/useActiveModifiers/useActiveModifiers.test.tsx +36 -0
  163. package/src/hooks/useActiveModifiers/useActiveModifiers.tsx +18 -0
  164. package/src/hooks/useControlledValue/index.ts +1 -0
  165. package/src/hooks/useControlledValue/useControlledValue.test.ts +68 -0
  166. package/src/hooks/useControlledValue/useControlledValue.ts +24 -0
  167. package/src/hooks/useDayEventHandlers/index.ts +1 -0
  168. package/src/hooks/useDayEventHandlers/useDayEventHandlers.test.tsx +213 -0
  169. package/src/hooks/useDayEventHandlers/useDayEventHandlers.tsx +195 -0
  170. package/src/hooks/useDayRender/index.ts +1 -0
  171. package/src/hooks/useDayRender/useDayRender.test.tsx +304 -0
  172. package/src/hooks/useDayRender/useDayRender.tsx +123 -0
  173. package/src/hooks/useDayRender/utils/getDayClassNames.test.ts +63 -0
  174. package/src/hooks/useDayRender/utils/getDayClassNames.ts +32 -0
  175. package/src/hooks/useDayRender/utils/getDayStyle.ts +19 -0
  176. package/src/hooks/useInput/index.ts +1 -0
  177. package/src/hooks/useInput/useInput.ts +175 -0
  178. package/src/hooks/useInput/utils/isValidDate.tsx +4 -0
  179. package/src/hooks/useSelectedDays/index.ts +1 -0
  180. package/src/hooks/useSelectedDays/useSelectedDays.test.ts +72 -0
  181. package/src/hooks/useSelectedDays/useSelectedDays.ts +32 -0
  182. package/src/index.ts +43 -0
  183. package/src/style.css +310 -0
  184. package/src/style.css.d.ts +38 -0
  185. package/src/types/DayPickerBase.ts +267 -0
  186. package/src/types/DayPickerDefault.ts +15 -0
  187. package/src/types/DayPickerMultiple.ts +26 -0
  188. package/src/types/DayPickerRange.ts +27 -0
  189. package/src/types/DayPickerSingle.ts +24 -0
  190. package/src/types/EventHandlers.ts +87 -0
  191. package/src/types/Formatters.ts +29 -0
  192. package/src/types/Labels.ts +36 -0
  193. package/src/types/Matchers.ts +106 -0
  194. package/src/types/Modifiers.ts +62 -0
  195. package/src/types/Styles.ts +108 -0
  196. package/tsconfig.json +24 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-day-picker",
3
- "version": "8.0.3",
3
+ "version": "8.0.4",
4
4
  "description": "Customizable Date Picker for React",
5
5
  "author": "Giampaolo Bellavite <io@gpbl.dev>",
6
6
  "homepage": "http://react-day-picker.js.org",
@@ -28,7 +28,9 @@
28
28
  "typecheck-watch": "tsc --project ./tsconfig.json --noEmit --skipLibCheck --watch"
29
29
  },
30
30
  "files": [
31
- "dist"
31
+ "dist",
32
+ "src",
33
+ "tsconfig.json"
32
34
  ],
33
35
  "dependencies": {
34
36
  "@reach/auto-id": "^0.16.0"
@@ -37,22 +39,22 @@
37
39
  "@rollup/plugin-alias": "^3.1.9",
38
40
  "@rollup/plugin-commonjs": "^21.0.3",
39
41
  "@rollup/plugin-typescript": "^8.3.1",
40
- "@testing-library/jest-dom": "^5.16.3",
42
+ "@testing-library/jest-dom": "^5.16.4",
41
43
  "@testing-library/react": "^12.1.4",
42
44
  "@testing-library/react-hooks": "^7.0.2",
43
45
  "@testing-library/user-event": "^13.5.0",
44
46
  "@types/jest": "^27.4.1",
45
47
  "@types/node": "^17.0.23",
46
- "@types/react": "^17.0.43",
47
- "@typescript-eslint/eslint-plugin": "^5.17.0",
48
- "@typescript-eslint/parser": "^5.17.0",
48
+ "@types/react": "^17.0.44",
49
+ "@typescript-eslint/eslint-plugin": "^5.18.0",
50
+ "@typescript-eslint/parser": "^5.18.0",
49
51
  "date-fns": "^2.28.0",
50
- "eslint": "^8.12.0",
52
+ "eslint": "^8.13.0",
51
53
  "eslint-config-prettier": "^8.5.0",
52
54
  "eslint-config-rdp": "workspace:^",
53
- "eslint-import-resolver-typescript": "^2.7.0",
54
- "eslint-plugin-import": "^2.25.4",
55
- "eslint-plugin-jest": "^26.1.3",
55
+ "eslint-import-resolver-typescript": "^2.7.1",
56
+ "eslint-plugin-import": "^2.26.0",
57
+ "eslint-plugin-jest": "^26.1.4",
56
58
  "eslint-plugin-prettier": "^4.0.0",
57
59
  "eslint-plugin-react-hooks": "^4.4.0",
58
60
  "jest": "^27.5.1",
@@ -69,7 +71,7 @@
69
71
  "timekeeper": "^2.2.0",
70
72
  "ts-jest": "^27.1.4",
71
73
  "ts-node": "^10.7.0",
72
- "tsc-alias": "^1.6.5",
74
+ "tsc-alias": "^1.6.6",
73
75
  "tslib": "^2.3.1",
74
76
  "typed-css-modules": "^0.7.0",
75
77
  "typescript": "~4.5.5"
@@ -0,0 +1,113 @@
1
+ import React from 'react';
2
+
3
+ import { DaySelectionMode } from 'types/DayPickerBase';
4
+ import { DayPickerDefaultProps } from 'types/DayPickerDefault';
5
+ import { DayPickerMultipleProps } from 'types/DayPickerMultiple';
6
+ import { DayPickerRangeProps } from 'types/DayPickerRange';
7
+ import { DayPickerSingleProps } from 'types/DayPickerSingle';
8
+
9
+ import { Root } from './components/Root';
10
+ import { RootProvider } from './contexts/RootProvider';
11
+
12
+ /**
13
+ * DayPicker render a date picker component to let users pick dates from a
14
+ * calendar. See http://react-day-picker.js.org for updated documentation and
15
+ * examples.
16
+ *
17
+ * ### Customization
18
+ *
19
+ * DayPicker offers different customization props. For example,
20
+ *
21
+ * - show multiple months using `numberOfMonths`
22
+ * - display a dropdown to navigate the months via `captionLayout`
23
+ * - display the week numbers with `showWeekNumbers`
24
+ * - disable or hide days with `disabled` or `hidden`
25
+ *
26
+ * ### Controlling the months
27
+ *
28
+ * Change the initially displayed month using the `defaultMonth` prop. The
29
+ * displayed months are controlled by DayPicker and stored in its internal
30
+ * state. To control the months yourself, use `month` instead of `defaultMonth`
31
+ * and use the `onMonthChange` event to set it.
32
+ *
33
+ * To limit the months the user can navigate to, use
34
+ * `fromDate`/`fromMonth`/`fromYear` or `toDate`/`toMonth`/`toYear`.
35
+ *
36
+ * ### Selection modes
37
+ *
38
+ * DayPicker supports different selection mode that can be toggled using the
39
+ * `mode` prop:
40
+ *
41
+ * - `mode="single"`: only one day can be selected. Use `required` to make the
42
+ * selection required. Use the `onSelect` event handler to get the selected
43
+ * days.
44
+ * - `mode="multiple"`: users can select one or more days. Limit the amount of
45
+ * days that can be selected with the `min` or the `max` props.
46
+ * - `mode="range"`: users can select a range of days. Limit the amount of days
47
+ * in the range with the `min` or the `max` props.
48
+ * - `mode="default"` (default): the built-in selections are disabled. Implement
49
+ * your own selection mode with `onDayClick`.
50
+ *
51
+ * The selection modes should cover the most common use cases. In case you
52
+ * need a more refined way of selecting days, use `mode="default"`. Use the
53
+ * `selected` props and add the day event handlers to add/remove days from the
54
+ * selection.
55
+ *
56
+ * ### Modifiers
57
+ *
58
+ * A _modifier_ represents different styles or states for the days displayed in
59
+ * the calendar (like "selected" or "disabled"). Define custom modifiers using
60
+ * the `modifiers` prop.
61
+ *
62
+ * ### Formatters and custom component
63
+ *
64
+ * You can customize how the content is displayed in the date picker by using
65
+ * either the formatters or replacing the internal components.
66
+ *
67
+ * For the most common cases you want to use the `formatters` prop to change how
68
+ * the content is formatted in the calendar. Use the `components` prop to
69
+ * replace the internal components, like the navigation icons.
70
+ *
71
+ * ### Styling
72
+ *
73
+ * DayPicker comes with a default, basic style in `react-day-picker/style` – use
74
+ * it as template for your own style.
75
+ *
76
+ * If you are using CSS modules, pass the imported styles object the
77
+ * `classNames` props.
78
+ *
79
+ * You can also style the elements via inline-styles using the `styles` prop.
80
+ *
81
+ * ### Form fields
82
+ *
83
+ * If you need to bind the date picker to a form field, you can use the
84
+ * `useInput` hooks for a basic behavior. See the `useInput` source as an
85
+ * example to bind the date picker with form fields.
86
+ *
87
+ * ### Localization
88
+ *
89
+ * To localize DayPicker, import the locale from `date-fns` package and use the
90
+ * `locale` prop.
91
+ *
92
+ * For example, to use Spanish locale:
93
+ *
94
+ * ```
95
+ * import es from 'date-fns/locale/es';
96
+ * <DayPicker locale={es} />
97
+ * ```
98
+ */
99
+
100
+ export type DayPickerProps = { mode?: DaySelectionMode } & (
101
+ | DayPickerDefaultProps
102
+ | DayPickerSingleProps
103
+ | DayPickerMultipleProps
104
+ | DayPickerRangeProps
105
+ );
106
+
107
+ export function DayPicker(props: DayPickerProps): JSX.Element {
108
+ return (
109
+ <RootProvider {...props}>
110
+ <Root />
111
+ </RootProvider>
112
+ );
113
+ }
@@ -0,0 +1,47 @@
1
+ import React from 'react';
2
+
3
+ import { screen } from '@testing-library/react';
4
+
5
+ import { customRender } from 'test/render';
6
+
7
+ import { Button } from './Button';
8
+
9
+ let button: HTMLButtonElement;
10
+
11
+ describe('when rendered without props', () => {
12
+ beforeEach(() => {
13
+ customRender(<Button className="foo" style={{ color: 'blue' }} />);
14
+ button = screen.getByRole('button');
15
+ });
16
+ test('should render a button with type "button"', () => {
17
+ expect(button).toHaveAttribute('type', 'button');
18
+ });
19
+ test('should render a button with the button class name', () => {
20
+ expect(button).toHaveClass('rdp-button');
21
+ });
22
+ test('should render a button with the reset class name', () => {
23
+ expect(button).toHaveClass('rdp-button_reset');
24
+ });
25
+ test('should add the class name', () => {
26
+ expect(button).toHaveClass('foo');
27
+ });
28
+ test('should apply the style', () => {
29
+ expect(button).toHaveStyle({ color: 'blue' });
30
+ });
31
+ });
32
+
33
+ describe('when using class names and styles from context', () => {
34
+ beforeEach(() => {
35
+ customRender(<Button />, {
36
+ classNames: { button: 'foo' },
37
+ styles: { button: { color: 'red' } }
38
+ });
39
+ button = screen.getByRole('button');
40
+ });
41
+ test('should apply the style', () => {
42
+ expect(button).toHaveStyle({ color: 'red' });
43
+ });
44
+ test('should apply the class name', () => {
45
+ expect(button).toHaveClass('foo');
46
+ });
47
+ });
@@ -0,0 +1,36 @@
1
+ import React, { forwardRef } from 'react';
2
+
3
+ import { useDayPicker } from 'contexts/DayPicker';
4
+
5
+ /** The props for the [[Button]] component. */
6
+ export type ButtonProps = React.HTMLProps<HTMLButtonElement>;
7
+
8
+ /**
9
+ * Render a button HTML element applying the reset class name.
10
+ */
11
+ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
12
+ (props, ref) => {
13
+ const { classNames, styles } = useDayPicker();
14
+
15
+ const classNamesArr = [classNames.button_reset, classNames.button];
16
+ if (props.className) {
17
+ classNamesArr.push(props.className);
18
+ }
19
+ const className = classNamesArr.join(' ');
20
+
21
+ const style = { ...styles.button_reset, ...styles.button };
22
+ if (props.style) {
23
+ Object.assign(style, props.style);
24
+ }
25
+
26
+ return (
27
+ <button
28
+ {...props}
29
+ ref={ref}
30
+ type="button"
31
+ className={className}
32
+ style={style}
33
+ />
34
+ );
35
+ }
36
+ );
@@ -0,0 +1 @@
1
+ export * from './Button';
@@ -0,0 +1,86 @@
1
+ import React from 'react';
2
+
3
+ import { screen } from '@testing-library/react';
4
+ import { DayPickerProps } from 'DayPicker';
5
+
6
+ import {
7
+ getMonthCaption,
8
+ getMonthDropdown,
9
+ getNextButton,
10
+ getPrevButton,
11
+ getYearDropdown,
12
+ queryNextButton,
13
+ queryPrevButton
14
+ } from 'test/po';
15
+ import { customRender } from 'test/render';
16
+ import { freezeBeforeAll } from 'test/utils';
17
+
18
+ import { CustomComponents } from 'types/DayPickerBase';
19
+
20
+ import { Caption, CaptionProps } from './Caption';
21
+
22
+ const today = new Date(2021, 8);
23
+
24
+ freezeBeforeAll(today);
25
+
26
+ function setup(props: CaptionProps, dayPickerProps?: DayPickerProps) {
27
+ customRender(<Caption {...props} />, dayPickerProps);
28
+ }
29
+
30
+ describe('when navigation is disabled', () => {
31
+ const props = { displayMonth: today };
32
+ const dayPickerProps = { disableNavigation: true };
33
+ beforeEach(() => setup(props, dayPickerProps));
34
+ test('should display the caption label', () => {
35
+ expect(getMonthCaption()).toHaveTextContent('September 2021');
36
+ });
37
+ test('should not render the navigation', () => {
38
+ expect(queryPrevButton()).toBeNull();
39
+ expect(queryNextButton()).toBeNull();
40
+ });
41
+ });
42
+
43
+ describe('when using a custom CaptionLabel component', () => {
44
+ const components: CustomComponents = {
45
+ CaptionLabel: () => <>custom label foo</>
46
+ };
47
+ const props = { displayMonth: today };
48
+ beforeEach(() => {
49
+ setup(props, { components });
50
+ });
51
+ test('it should render the custom component instead', () => {
52
+ expect(screen.getByText('custom label foo')).toBeInTheDocument();
53
+ });
54
+ });
55
+
56
+ describe('when the caption layout is "dropdown"', () => {
57
+ const dayPickerProps: DayPickerProps = {
58
+ captionLayout: 'dropdown',
59
+ fromYear: 2020,
60
+ toYear: 2025
61
+ };
62
+ const props = { displayMonth: today };
63
+ beforeEach(() => {
64
+ setup(props, dayPickerProps);
65
+ });
66
+ test('should render the month drop-down', () => {
67
+ expect(getMonthDropdown()).toBeInTheDocument();
68
+ });
69
+ test('should render the year drop-down', () => {
70
+ expect(getYearDropdown()).toBeInTheDocument();
71
+ });
72
+ });
73
+
74
+ describe('when the caption layout is "buttons"', () => {
75
+ const dayPickerProps: DayPickerProps = {
76
+ captionLayout: 'buttons'
77
+ };
78
+ test('should render the next month button', () => {
79
+ customRender(<Caption displayMonth={today} />, dayPickerProps);
80
+ expect(getNextButton()).toBeInTheDocument();
81
+ });
82
+ test('should render the previous month button', () => {
83
+ customRender(<Caption displayMonth={today} />, dayPickerProps);
84
+ expect(getPrevButton()).toBeInTheDocument();
85
+ });
86
+ });
@@ -0,0 +1,54 @@
1
+ import React from 'react';
2
+
3
+ import { CaptionDropdowns } from 'components/CaptionDropdowns';
4
+ import { CaptionLabel } from 'components/CaptionLabel';
5
+ import { CaptionNavigation } from 'components/CaptionNavigation';
6
+ import { useDayPicker } from 'contexts/DayPicker';
7
+
8
+ /** Represent the props of the [[Caption]] component. */
9
+ export interface CaptionProps {
10
+ /** The ID for the heading element. Must be the same as the labelled-by in Table. */
11
+ id?: string;
12
+ /** The month where the caption is displayed. */
13
+ displayMonth: Date;
14
+ }
15
+
16
+ /**
17
+ * The layout of the caption:
18
+ *
19
+ * - `dropdown`: display dropdowns for choosing the month and the year.
20
+ * - `buttons`: display previous month / next month buttons.
21
+ */
22
+ export type CaptionLayout = 'dropdown' | 'buttons';
23
+
24
+ /**
25
+ * Render the caption of a month. The caption has a different layout when
26
+ * setting the [[DayPickerProps.captionLayout]] prop.
27
+ */
28
+ export function Caption(props: CaptionProps): JSX.Element {
29
+ const { classNames, disableNavigation, styles, captionLayout, components } =
30
+ useDayPicker();
31
+
32
+ const CaptionLabelComponent = components?.CaptionLabel ?? CaptionLabel;
33
+
34
+ let caption: JSX.Element;
35
+ if (disableNavigation) {
36
+ caption = (
37
+ <CaptionLabelComponent id={props.id} displayMonth={props.displayMonth} />
38
+ );
39
+ } else if (captionLayout === 'dropdown') {
40
+ caption = (
41
+ <CaptionDropdowns displayMonth={props.displayMonth} id={props.id} />
42
+ );
43
+ } else {
44
+ caption = (
45
+ <CaptionNavigation displayMonth={props.displayMonth} id={props.id} />
46
+ );
47
+ }
48
+
49
+ return (
50
+ <div className={classNames.caption} style={styles.caption}>
51
+ {caption}
52
+ </div>
53
+ );
54
+ }
@@ -0,0 +1 @@
1
+ export * from './Caption';
@@ -0,0 +1,123 @@
1
+ import React from 'react';
2
+
3
+ import { screen } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
5
+ import { setMonth, setYear } from 'date-fns';
6
+ import { DayPickerProps } from 'DayPicker';
7
+
8
+ import {
9
+ getMonthDropdown,
10
+ getYearDropdown,
11
+ queryMonthDropdown,
12
+ queryYearDropdown
13
+ } from 'test/po';
14
+ import { customRender } from 'test/render';
15
+ import { freezeBeforeAll } from 'test/utils';
16
+
17
+ import { CaptionProps } from 'components/Caption';
18
+ import { CustomComponents } from 'types/DayPickerBase';
19
+
20
+ import { CaptionDropdowns } from './CaptionDropdowns';
21
+
22
+ const today = new Date(2021, 8);
23
+ const fromYear = 2020;
24
+ const toYear = 2025;
25
+
26
+ freezeBeforeAll(today);
27
+
28
+ function setup(props: CaptionProps, dayPickerProps?: DayPickerProps) {
29
+ customRender(<CaptionDropdowns {...props} />, dayPickerProps);
30
+ }
31
+
32
+ describe('when using a custom CaptionLabel component', () => {
33
+ const components: CustomComponents = {
34
+ CaptionLabel: () => <>custom label foo</>
35
+ };
36
+ const props = { displayMonth: today };
37
+ beforeEach(() => {
38
+ setup(props, { components });
39
+ });
40
+ test('it should render the custom component instead', () => {
41
+ expect(screen.getByText('custom label foo')).toBeInTheDocument();
42
+ });
43
+ });
44
+
45
+ describe('when rendered with custom styles or classnames', () => {
46
+ let container: HTMLElement;
47
+
48
+ beforeEach(() => {
49
+ const dayPickerProps: DayPickerProps = {
50
+ captionLayout: 'dropdown',
51
+ fromYear,
52
+ toYear,
53
+ classNames: { caption_dropdowns: 'foo_dropdowns' },
54
+ styles: { caption_dropdowns: { color: 'red' } }
55
+ };
56
+ const view = customRender(
57
+ <CaptionDropdowns displayMonth={today} />,
58
+ dayPickerProps
59
+ );
60
+ container = view.container;
61
+ });
62
+ test('should use the `caption_dropdowns` class name', () => {
63
+ expect(container.firstChild).toHaveClass('foo_dropdowns');
64
+ });
65
+ test('should use the `caption_dropdowns` style', () => {
66
+ expect(container.firstChild).toHaveStyle({ color: 'red' });
67
+ });
68
+ test('should render the month drop-down', () => {
69
+ expect(getMonthDropdown()).toBeInTheDocument();
70
+ });
71
+ test('should render the year drop-down', () => {
72
+ expect(getYearDropdown()).toBeInTheDocument();
73
+ });
74
+ });
75
+
76
+ describe('when a month is selected', () => {
77
+ const dayPickerProps: DayPickerProps = {
78
+ captionLayout: 'dropdown',
79
+ fromYear,
80
+ toYear,
81
+ onMonthChange: jest.fn()
82
+ };
83
+ beforeEach(() => {
84
+ customRender(<CaptionDropdowns displayMonth={today} />, dayPickerProps);
85
+ });
86
+ describe('from the months drop-down', () => {
87
+ const newMonth = setMonth(today, 0);
88
+ beforeEach(() => {
89
+ userEvent.selectOptions(
90
+ getMonthDropdown(),
91
+ newMonth.getMonth().toString()
92
+ );
93
+ });
94
+ test('should call the `onMonthChange` callback', () => {
95
+ expect(dayPickerProps.onMonthChange).toHaveBeenCalledWith(newMonth);
96
+ });
97
+ });
98
+ describe('from the years drop-down', () => {
99
+ const newYear = setYear(today, 2022);
100
+ beforeEach(() => {
101
+ userEvent.selectOptions(
102
+ getYearDropdown(),
103
+ newYear.getFullYear().toString()
104
+ );
105
+ });
106
+ test('should call the `onMonthChange` callback', () => {
107
+ expect(dayPickerProps.onMonthChange).toHaveBeenCalledWith(newYear);
108
+ });
109
+ });
110
+ });
111
+
112
+ describe('when no date limits are set', () => {
113
+ const dayPickerProps: DayPickerProps = {
114
+ captionLayout: 'dropdown'
115
+ };
116
+ beforeEach(() => {
117
+ customRender(<CaptionDropdowns displayMonth={today} />, dayPickerProps);
118
+ });
119
+ test('should not render the drop-downs', () => {
120
+ expect(queryMonthDropdown()).toBeNull();
121
+ expect(queryYearDropdown()).toBeNull();
122
+ });
123
+ });
@@ -0,0 +1,43 @@
1
+ import React from 'react';
2
+
3
+ import { CaptionProps } from 'components/Caption/Caption';
4
+ import { CaptionLabel } from 'components/CaptionLabel';
5
+ import { MonthsDropdown } from 'components/MonthsDropdown';
6
+ import { YearsDropdown } from 'components/YearsDropdown';
7
+ import { useDayPicker } from 'contexts/DayPicker';
8
+ import { useNavigation } from 'contexts/Navigation';
9
+ import { MonthChangeEventHandler } from 'types/EventHandlers';
10
+
11
+ /**
12
+ * Render a caption with the dropdowns to navigate between months and years.
13
+ */
14
+ export function CaptionDropdowns(props: CaptionProps): JSX.Element {
15
+ const { classNames, styles, onMonthChange, components } = useDayPicker();
16
+ const { goToMonth } = useNavigation();
17
+
18
+ const handleMonthChange: MonthChangeEventHandler = (newMonth) => {
19
+ goToMonth(newMonth);
20
+ onMonthChange?.(newMonth);
21
+ };
22
+ const CaptionLabelComponent = components?.CaptionLabel ?? CaptionLabel;
23
+ const captionLabel = (
24
+ <CaptionLabelComponent id={props.id} displayMonth={props.displayMonth} />
25
+ );
26
+ return (
27
+ <div
28
+ className={classNames.caption_dropdowns}
29
+ style={styles.caption_dropdowns}
30
+ >
31
+ {/* Caption label is visually hidden but for a11y. */}
32
+ <div className={classNames.vhidden}>{captionLabel}</div>
33
+ <MonthsDropdown
34
+ onChange={handleMonthChange}
35
+ displayMonth={props.displayMonth}
36
+ />
37
+ <YearsDropdown
38
+ onChange={handleMonthChange}
39
+ displayMonth={props.displayMonth}
40
+ />
41
+ </div>
42
+ );
43
+ }
@@ -0,0 +1 @@
1
+ export * from './CaptionDropdowns';
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+
3
+ import { getMonthCaption } from 'test/po';
4
+ import { customRender } from 'test/render';
5
+ import { freezeBeforeAll } from 'test/utils';
6
+
7
+ import { CaptionLabel } from './CaptionLabel';
8
+
9
+ const today = new Date(1979, 8);
10
+ freezeBeforeAll(today);
11
+
12
+ test('should render the formatted display month', () => {
13
+ customRender(<CaptionLabel displayMonth={today} />);
14
+ expect(getMonthCaption()).toHaveTextContent('September 1979');
15
+ });
16
+
17
+ test('should apply the `caption_label` class name', () => {
18
+ customRender(<CaptionLabel displayMonth={today} />, {
19
+ classNames: { caption_label: 'foo' }
20
+ });
21
+ expect(getMonthCaption()).toHaveClass('foo');
22
+ });
23
+
24
+ test('should apply the `caption_label` style', () => {
25
+ customRender(<CaptionLabel displayMonth={today} />, {
26
+ styles: { caption_label: { color: 'red' } }
27
+ });
28
+ expect(getMonthCaption()).toHaveStyle({ color: 'red' });
29
+ });
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+
3
+ import { useDayPicker } from 'contexts/DayPicker';
4
+
5
+ /** The props for the [[CaptionLabel]] component. */
6
+ export interface CaptionLabelProps {
7
+ /** The ID for the heading element. Must be the same as the labelled-by in Table. */
8
+ id?: string;
9
+ /** The month where the caption is displayed. */
10
+ displayMonth: Date;
11
+ }
12
+
13
+ /** Render the caption for the displayed month. This component is used when `captionLayout="buttons"`. */
14
+ export function CaptionLabel(props: CaptionLabelProps): JSX.Element {
15
+ const {
16
+ locale,
17
+ classNames,
18
+ styles,
19
+ formatters: { formatCaption }
20
+ } = useDayPicker();
21
+ return (
22
+ <h2
23
+ className={classNames.caption_label}
24
+ style={styles.caption_label}
25
+ aria-live="polite"
26
+ aria-atomic="true"
27
+ id={props.id}
28
+ >
29
+ {formatCaption(props.displayMonth, { locale })}
30
+ </h2>
31
+ );
32
+ }
@@ -0,0 +1 @@
1
+ export * from './CaptionLabel';