├── .nvmrc
├── website
├── static
│ ├── .nojekill
│ ├── CNAME
│ └── images
│ │ ├── logo.png
│ │ ├── favicon.ico
│ │ ├── favicon.png
│ │ ├── og-image.png
│ │ └── sublist-icon.svg
├── examples
│ ├── typings.d.ts
│ ├── .prettierrc
│ ├── disabled.tsx
│ ├── outside-days.tsx
│ ├── fixedweeks.tsx
│ ├── default-month.tsx
│ ├── weeknumber-iso.tsx
│ ├── multiple-months.tsx
│ ├── week-iso.tsx
│ ├── multiple-months-paged.tsx
│ ├── keyboard.tsx
│ ├── spanish.tsx
│ ├── dropdown.tsx
│ ├── rtl.tsx
│ ├── container-attributes.tsx
│ ├── spanish-week-starts-on.tsx
│ ├── from-to-year.tsx
│ ├── dropdown-buttons.tsx
│ ├── styling-inline.tsx
│ ├── dropdown-multiple-months.tsx
│ ├── from-to-month.tsx
│ ├── modifiers-hidden.tsx
│ ├── custom-day.tsx
│ ├── styling-css.tsx
│ ├── weeknumber.tsx
│ ├── modifiers-disabled.tsx
│ ├── weeknumber-custom.tsx
│ ├── focus-recursive.tsx
│ ├── start.tsx
│ ├── useinput.tsx
│ ├── modifiers-style.tsx
│ ├── single.tsx
│ ├── custom-single.tsx
│ ├── multiple-min-max.tsx
│ ├── modifiers-today.tsx
│ ├── controlled.tsx
│ ├── multiple.tsx
│ ├── single-required.tsx
│ ├── styling-css-modules.tsx
│ ├── modifiers-classnames.tsx
│ ├── custom-disable-row.tsx
│ ├── testcase-1567.tsx
│ ├── modifiers-custom.tsx
│ ├── range-min-max.tsx
│ ├── custom-caption.tsx
│ ├── numbering-system.tsx
│ ├── range.tsx
│ ├── formatters.tsx
│ ├── styling-modifiers.tsx
│ ├── custom-multiple.tsx
│ └── range-shift-key.tsx
├── src
│ ├── components
│ │ ├── index.ts
│ │ ├── RenderExample.module.css
│ │ └── RenderExample.tsx
│ ├── theme
│ │ └── CodeBlock
│ │ │ └── sandpack-app
│ │ │ ├── App.tsx
│ │ │ ├── index.html
│ │ │ ├── index.tsx
│ │ │ ├── light.css
│ │ │ └── dark.css
│ └── pages
│ │ └── render.tsx
├── test
│ ├── utils
│ │ ├── index.ts
│ │ ├── freezeBeforeAll.ts
│ │ └── focusDaysGrid.ts
│ ├── setup.ts
│ ├── user.ts
│ └── axe.ts
├── docs
│ ├── changelog.md
│ ├── development
│ │ ├── index.md
│ │ └── code-of-conduct.md
│ ├── contributing.md
│ ├── index.md
│ ├── guides
│ │ ├── formatters.md
│ │ └── input-fields.md
│ ├── start.md
│ ├── reference.md
│ ├── basics
│ │ ├── keyboard.md
│ │ └── localization.md
│ └── license.md
├── .gitignore
├── README.md
├── tsconfig.json
├── docusaurus.typedoc.js
├── test-integration
│ └── examples
│ │ ├── formatters.test.tsx
│ │ ├── default-month.test.tsx
│ │ ├── testcase-1567.test.tsx
│ │ ├── styling-css.test.tsx
│ │ ├── styling-inline.test.tsx
│ │ ├── weeknumber-custom.test.tsx
│ │ ├── spanish-week-starts-on.test.tsx
│ │ ├── custom-day.test.tsx
│ │ ├── modifiers-disabled.test.tsx
│ │ ├── modifiers-hidden.test.tsx
│ │ ├── modifiers-classnames.test.tsx
│ │ ├── modifiers-style.test.tsx
│ │ ├── fixedweeks.test.tsx
│ │ ├── custom-disable-row.test.tsx
│ │ ├── spanish.test.tsx
│ │ ├── outside-days.test.tsx
│ │ ├── container-attributes.test.tsx
│ │ ├── numbering-system.test.tsx
│ │ ├── modifiers-custom.test.tsx
│ │ ├── weeknumber.test.tsx
│ │ ├── custom-single.test.tsx
│ │ ├── controlled.test.tsx
│ │ ├── start.test.tsx
│ │ ├── focus-recursive.test.tsx
│ │ ├── dropdown.test.tsx
│ │ ├── single-required.test.tsx
│ │ ├── rtl.test.tsx
│ │ ├── from-to-month.test.tsx
│ │ ├── single.test.tsx
│ │ ├── modifiers-today.test.tsx
│ │ ├── dropdown-buttons.test.tsx
│ │ ├── from-to-year.test.tsx
│ │ ├── multiple.test.tsx
│ │ └── multiple-months-paged.test.tsx
├── jest.config.ts
├── plugins
│ └── source-map.js
├── docusaurus.sidebars.js
└── docusaurus.navbar.js
├── src
├── .eslintignore
├── hooks
│ ├── useId
│ │ ├── index.ts
│ │ └── useIsomorphicLayoutEffect.ts
│ ├── useInput
│ │ ├── index.ts
│ │ └── utils
│ │ │ └── isValidDate.tsx
│ ├── useDayRender
│ │ ├── index.ts
│ │ └── utils
│ │ │ ├── getDayStyle.ts
│ │ │ └── getDayClassNames.ts
│ ├── useSelectedDays
│ │ ├── index.ts
│ │ ├── useSelectedDays.ts
│ │ └── useSelectedDays.test.ts
│ ├── useActiveModifiers
│ │ ├── index.ts
│ │ ├── useActiveModifiers.tsx
│ │ └── useActiveModifiers.test.tsx
│ ├── useControlledValue
│ │ ├── index.ts
│ │ ├── useControlledValue.ts
│ │ └── useControlledValue.test.ts
│ └── useDayEventHandlers
│ │ └── index.ts
├── components
│ ├── Day
│ │ ├── index.ts
│ │ └── Day.tsx
│ ├── Head
│ │ ├── index.ts
│ │ ├── Head.tsx
│ │ └── Head.test.tsx
│ ├── Month
│ │ └── index.ts
│ ├── Root
│ │ └── index.ts
│ ├── Row
│ │ ├── index.ts
│ │ └── Row.tsx
│ ├── Table
│ │ ├── index.ts
│ │ └── utils
│ │ │ ├── daysToMonthWeeks.ts
│ │ │ └── getMonthWeeks.ts
│ ├── Button
│ │ ├── index.ts
│ │ ├── Button.tsx
│ │ └── Button.test.tsx
│ ├── Caption
│ │ └── index.ts
│ ├── Footer
│ │ ├── index.ts
│ │ ├── Footer.tsx
│ │ └── Footer.test.tsx
│ ├── HeadRow
│ │ ├── index.ts
│ │ ├── utils
│ │ │ ├── index.ts
│ │ │ ├── getWeekdays.ts
│ │ │ └── getWeekdays.test.ts
│ │ └── HeadRow.tsx
│ ├── Months
│ │ ├── index.ts
│ │ ├── Months.tsx
│ │ └── Months.test.tsx
│ ├── DayContent
│ │ ├── index.ts
│ │ ├── DayContent.tsx
│ │ └── DayContent.test.tsx
│ ├── Dropdown
│ │ └── index.ts
│ ├── IconLeft
│ │ ├── index.ts
│ │ ├── IconLeft.test.tsx
│ │ └── IconLeft.tsx
│ ├── IconRight
│ │ ├── index.ts
│ │ ├── IconRight.test.tsx
│ │ └── IconRight.tsx
│ ├── Navigation
│ │ └── index.ts
│ ├── WeekNumber
│ │ ├── index.ts
│ │ ├── __snapshots__
│ │ │ └── WeekNumber.test.tsx.snap
│ │ └── WeekNumber.tsx
│ ├── CaptionLabel
│ │ ├── index.ts
│ │ ├── CaptionLabel.test.tsx
│ │ └── CaptionLabel.tsx
│ ├── IconDropdown
│ │ ├── index.ts
│ │ ├── IconDropdown.test.tsx
│ │ └── IconDropdown.tsx
│ ├── MonthsDropdown
│ │ ├── index.ts
│ │ └── __snapshots__
│ │ │ └── MonthsDropdown.test.tsx.snap
│ ├── YearsDropdown
│ │ ├── index.ts
│ │ └── __snapshots__
│ │ │ └── YearsDropdown.test.tsx.snap
│ ├── CaptionDropdowns
│ │ ├── index.ts
│ │ └── CaptionDropdowns.tsx
│ └── CaptionNavigation
│ │ ├── index.ts
│ │ └── CaptionNavigation.tsx
├── contexts
│ ├── Focus
│ │ ├── index.ts
│ │ └── utils
│ │ │ ├── getInitialFocusTarget.test.ts
│ │ │ └── getInitialFocusTarget.ts
│ ├── DayPicker
│ │ ├── index.ts
│ │ ├── utils
│ │ │ ├── index.ts
│ │ │ └── parseFromToProps.ts
│ │ ├── labels
│ │ │ ├── labelNext.test.ts
│ │ │ ├── labelYearDropdown.ts
│ │ │ ├── labelMonthDropdown.ts
│ │ │ ├── labelWeekNumber.test.ts
│ │ │ ├── labelPrevious.test.ts
│ │ │ ├── labelYearDropdown.test.ts
│ │ │ ├── labelMonthDropdown.test.ts
│ │ │ ├── labelDay.test.ts
│ │ │ ├── labelNext.ts
│ │ │ ├── labelWeekNumber.ts
│ │ │ ├── index.ts
│ │ │ ├── labelPrevious.ts
│ │ │ ├── labelWeekday.ts
│ │ │ ├── labelDay.ts
│ │ │ └── labelWeekday.test.ts
│ │ ├── formatters
│ │ │ ├── formatWeekNumber.ts
│ │ │ ├── formatWeekNumber.test.ts
│ │ │ ├── formatDay.test.ts
│ │ │ ├── index.ts
│ │ │ ├── formatDay.ts
│ │ │ ├── formatYearCaption.test.ts
│ │ │ ├── formatCaption.ts
│ │ │ ├── formatMonthCaption.ts
│ │ │ ├── formatWeekdayName.ts
│ │ │ ├── formatYearCaption.ts
│ │ │ ├── formatCaption.test.ts
│ │ │ ├── formatWeekdayName.test.ts
│ │ │ └── formatMonthCaption.test.ts
│ │ ├── defaultContextValues.ts
│ │ └── defaultClassNames.ts
│ ├── Navigation
│ │ ├── index.ts
│ │ ├── utils
│ │ │ ├── getDisplayMonths.ts
│ │ │ ├── getInitialMonth.ts
│ │ │ ├── getNextMonth.ts
│ │ │ └── getPreviousMonth.ts
│ │ ├── useNavigationState.ts
│ │ └── useNavigationState.test.ts
│ ├── SelectRange
│ │ ├── index.ts
│ │ └── utils
│ │ │ └── addToRange.ts
│ ├── SelectSingle
│ │ └── index.ts
│ ├── SelectMultiple
│ │ └── index.ts
│ ├── Modifiers
│ │ ├── index.ts
│ │ ├── utils
│ │ │ ├── matcherToArray.ts
│ │ │ ├── getCustomModifiers.test.ts
│ │ │ ├── getCustomModifiers.ts
│ │ │ ├── isDateInRange.ts
│ │ │ ├── matcherToArray.test.ts
│ │ │ ├── getActiveModifiers.ts
│ │ │ ├── isDateInRange.test.ts
│ │ │ └── getActiveModifiers.test.ts
│ │ └── ModifiersContext.test.ts
│ └── RootProvider.tsx
├── types
│ ├── DayPickerDefault.ts
│ ├── DayPickerSingle.ts
│ ├── DayPickerMultiple.ts
│ ├── DayPickerRange.ts
│ ├── Formatters.ts
│ └── Labels.ts
├── style.css.d.ts
└── index.ts
├── pnpm-workspace.yaml
├── test
├── setup.ts
├── utils
│ ├── index.ts
│ ├── freezeBeforeAll.ts
│ └── focusDaysGrid.ts
├── render
│ ├── index.ts
│ └── customRender.tsx
├── user.ts
└── mockedContexts.ts
├── .gitignore
├── CHANGELOG.md
├── .prettierrc
├── tsconfig.build.json
├── .editorconfig
├── SECURITY.md
├── jest.config.ts
├── tsconfig.json
├── CONTRIBUTING.md
├── LICENSE
├── .eslintrc.js
└── tea.yaml
/.nvmrc:
--------------------------------------------------------------------------------
1 | 18
2 |
--------------------------------------------------------------------------------
/website/static/.nojekill:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/.eslintignore:
--------------------------------------------------------------------------------
1 | style.css.d.ts
2 |
--------------------------------------------------------------------------------
/website/static/CNAME:
--------------------------------------------------------------------------------
1 | react-day-picker.js.org
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - "website"
3 |
--------------------------------------------------------------------------------
/src/hooks/useId/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useId';
2 |
--------------------------------------------------------------------------------
/test/setup.ts:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom';
2 |
--------------------------------------------------------------------------------
/src/components/Day/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Day';
2 |
--------------------------------------------------------------------------------
/src/components/Head/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Head';
2 |
--------------------------------------------------------------------------------
/src/components/Month/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Month';
2 |
--------------------------------------------------------------------------------
/src/components/Root/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Root';
2 |
--------------------------------------------------------------------------------
/src/components/Row/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Row';
2 |
--------------------------------------------------------------------------------
/src/components/Table/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Table';
2 |
--------------------------------------------------------------------------------
/src/components/Button/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Button';
2 |
--------------------------------------------------------------------------------
/src/components/Caption/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Caption';
2 |
--------------------------------------------------------------------------------
/src/components/Footer/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Footer';
2 |
--------------------------------------------------------------------------------
/src/components/HeadRow/index.ts:
--------------------------------------------------------------------------------
1 | export * from './HeadRow';
2 |
--------------------------------------------------------------------------------
/src/components/Months/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Months';
2 |
--------------------------------------------------------------------------------
/src/contexts/Focus/index.ts:
--------------------------------------------------------------------------------
1 | export * from './FocusContext';
2 |
--------------------------------------------------------------------------------
/src/hooks/useInput/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useInput';
2 |
--------------------------------------------------------------------------------
/src/components/DayContent/index.ts:
--------------------------------------------------------------------------------
1 | export * from './DayContent';
2 |
--------------------------------------------------------------------------------
/src/components/Dropdown/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Dropdown';
2 |
--------------------------------------------------------------------------------
/src/components/IconLeft/index.ts:
--------------------------------------------------------------------------------
1 | export * from './IconLeft';
2 |
--------------------------------------------------------------------------------
/src/components/IconRight/index.ts:
--------------------------------------------------------------------------------
1 | export * from './IconRight';
2 |
--------------------------------------------------------------------------------
/src/components/Navigation/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Navigation';
2 |
--------------------------------------------------------------------------------
/src/components/WeekNumber/index.ts:
--------------------------------------------------------------------------------
1 | export * from './WeekNumber';
2 |
--------------------------------------------------------------------------------
/src/hooks/useDayRender/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useDayRender';
2 |
--------------------------------------------------------------------------------
/website/examples/typings.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.module.css';
2 |
--------------------------------------------------------------------------------
/website/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from './RenderExample';
2 |
--------------------------------------------------------------------------------
/website/test/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './freezeBeforeAll';
2 |
--------------------------------------------------------------------------------
/src/components/CaptionLabel/index.ts:
--------------------------------------------------------------------------------
1 | export * from './CaptionLabel';
2 |
--------------------------------------------------------------------------------
/src/components/HeadRow/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './getWeekdays';
2 |
--------------------------------------------------------------------------------
/src/components/IconDropdown/index.ts:
--------------------------------------------------------------------------------
1 | export * from './IconDropdown';
2 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/index.ts:
--------------------------------------------------------------------------------
1 | export * from './DayPickerContext';
2 |
--------------------------------------------------------------------------------
/src/contexts/Navigation/index.ts:
--------------------------------------------------------------------------------
1 | export * from './NavigationContext';
2 |
--------------------------------------------------------------------------------
/src/hooks/useSelectedDays/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useSelectedDays';
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
3 | node_modules
4 | coverage
5 | build
6 | dist
7 |
--------------------------------------------------------------------------------
/src/components/MonthsDropdown/index.ts:
--------------------------------------------------------------------------------
1 | export * from './MonthsDropdown';
2 |
--------------------------------------------------------------------------------
/src/components/YearsDropdown/index.ts:
--------------------------------------------------------------------------------
1 | export * from './YearsDropdown';
2 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './parseFromToProps';
2 |
--------------------------------------------------------------------------------
/src/contexts/SelectRange/index.ts:
--------------------------------------------------------------------------------
1 | export * from './SelectRangeContext';
2 |
--------------------------------------------------------------------------------
/src/contexts/SelectSingle/index.ts:
--------------------------------------------------------------------------------
1 | export * from './SelectSingleContext';
2 |
--------------------------------------------------------------------------------
/src/components/CaptionDropdowns/index.ts:
--------------------------------------------------------------------------------
1 | export * from './CaptionDropdowns';
2 |
--------------------------------------------------------------------------------
/src/components/CaptionNavigation/index.ts:
--------------------------------------------------------------------------------
1 | export * from './CaptionNavigation';
2 |
--------------------------------------------------------------------------------
/src/contexts/SelectMultiple/index.ts:
--------------------------------------------------------------------------------
1 | export * from './SelectMultipleContext';
2 |
--------------------------------------------------------------------------------
/src/hooks/useActiveModifiers/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useActiveModifiers';
2 |
--------------------------------------------------------------------------------
/src/hooks/useControlledValue/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useControlledValue';
2 |
--------------------------------------------------------------------------------
/src/hooks/useDayEventHandlers/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useDayEventHandlers';
2 |
--------------------------------------------------------------------------------
/test/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './freezeBeforeAll';
2 | export * from './focusDaysGrid';
3 |
--------------------------------------------------------------------------------
/website/test/setup.ts:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom';
2 | import 'jest-axe/extend-expect';
3 |
--------------------------------------------------------------------------------
/test/render/index.ts:
--------------------------------------------------------------------------------
1 | export * from './customRender';
2 | export * from './renderDayPickerHook';
3 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | Full release notes at https://github.com/gpbl/react-day-picker/releases
4 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "none",
3 | "tabWidth": 2,
4 | "semi": true,
5 | "singleQuote": true
6 | }
7 |
--------------------------------------------------------------------------------
/src/contexts/Modifiers/index.ts:
--------------------------------------------------------------------------------
1 | export * from './ModifiersContext';
2 | export * from './utils/getActiveModifiers';
3 |
--------------------------------------------------------------------------------
/test/user.ts:
--------------------------------------------------------------------------------
1 | import { userEvent } from '@testing-library/user-event';
2 |
3 | export const user = userEvent.setup();
4 |
--------------------------------------------------------------------------------
/website/static/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0xGen/react-day-picker-trial/HEAD/website/static/images/logo.png
--------------------------------------------------------------------------------
/website/static/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0xGen/react-day-picker-trial/HEAD/website/static/images/favicon.ico
--------------------------------------------------------------------------------
/website/static/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0xGen/react-day-picker-trial/HEAD/website/static/images/favicon.png
--------------------------------------------------------------------------------
/website/test/user.ts:
--------------------------------------------------------------------------------
1 | import { userEvent } from '@testing-library/user-event';
2 |
3 | export const user = userEvent.setup();
4 |
--------------------------------------------------------------------------------
/website/examples/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "none",
3 | "tabWidth": 2,
4 | "semi": true,
5 | "singleQuote": true
6 | }
7 |
--------------------------------------------------------------------------------
/website/static/images/og-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0xGen/react-day-picker-trial/HEAD/website/static/images/og-image.png
--------------------------------------------------------------------------------
/src/hooks/useInput/utils/isValidDate.tsx:
--------------------------------------------------------------------------------
1 | /** @private */
2 | export function isValidDate(day: Date): boolean {
3 | return !isNaN(day.getTime());
4 | }
5 |
--------------------------------------------------------------------------------
/website/test/axe.ts:
--------------------------------------------------------------------------------
1 | import { configureAxe } from 'jest-axe';
2 |
3 | export const axe = configureAxe({
4 | rules: {
5 | 'aria-allowed-role': { enabled: false }
6 | }
7 | });
8 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "rootDir": "./src"
5 | },
6 | "exclude": ["**/*.test.*"],
7 | "include": ["./src/**/*"]
8 | }
9 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/labels/labelNext.test.ts:
--------------------------------------------------------------------------------
1 | import { labelNext } from './labelNext';
2 |
3 | test('should return the label', () => {
4 | expect(labelNext()).toEqual('Go to next month');
5 | });
6 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/labels/labelYearDropdown.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The default ARIA label for the WeekNumber element.
3 | */
4 | export const labelYearDropdown = (): string => {
5 | return 'Year: ';
6 | };
7 |
--------------------------------------------------------------------------------
/website/examples/disabled.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | return ;
7 | }
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 |
7 | [*.{js,jsx,ts,tsx,json,yml}]
8 | charset = utf-8
9 | indent_style = space
10 | indent_size = 2
11 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/labels/labelMonthDropdown.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The default ARIA label for the WeekNumber element.
3 | */
4 | export const labelMonthDropdown = (): string => {
5 | return 'Month: ';
6 | };
7 |
--------------------------------------------------------------------------------
/test/utils/freezeBeforeAll.ts:
--------------------------------------------------------------------------------
1 | import MockDate from 'mockdate';
2 |
3 | export function freezeBeforeAll(date: Date) {
4 | beforeAll(() => MockDate.set(date));
5 | afterAll(() => MockDate.reset());
6 | }
7 |
--------------------------------------------------------------------------------
/website/examples/outside-days.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | return ;
7 | }
8 |
--------------------------------------------------------------------------------
/website/src/theme/CodeBlock/sandpack-app/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | /** Placeholder app replaced by CustomSandpack component. */
4 | export default function App() {
5 | return <>>;
6 | }
7 |
--------------------------------------------------------------------------------
/website/examples/fixedweeks.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { DayPicker } from 'react-day-picker';
3 |
4 | export default function App() {
5 | return ;
6 | }
7 |
--------------------------------------------------------------------------------
/website/test/utils/freezeBeforeAll.ts:
--------------------------------------------------------------------------------
1 | import MockDate from 'mockdate';
2 |
3 | export function freezeBeforeAll(date: Date) {
4 | beforeAll(() => MockDate.set(date));
5 | afterAll(() => MockDate.reset());
6 | }
7 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/formatters/formatWeekNumber.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The default formatter for the week number.
3 | */
4 | export function formatWeekNumber(weekNumber: number): string {
5 | return `${weekNumber}`;
6 | }
7 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/labels/labelWeekNumber.test.ts:
--------------------------------------------------------------------------------
1 | import { labelWeekNumber } from './labelWeekNumber';
2 |
3 | test('should return the label', () => {
4 | expect(labelWeekNumber(2)).toEqual('Week n. 2');
5 | });
6 |
--------------------------------------------------------------------------------
/website/examples/default-month.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { DayPicker } from 'react-day-picker';
3 |
4 | export default function App() {
5 | return ;
6 | }
7 |
--------------------------------------------------------------------------------
/website/examples/weeknumber-iso.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | return ;
7 | }
8 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/labels/labelPrevious.test.ts:
--------------------------------------------------------------------------------
1 | import { labelPrevious } from './labelPrevious';
2 |
3 | test('should return the label', () => {
4 | expect(labelPrevious()).toEqual('Go to previous month');
5 | });
6 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/labels/labelYearDropdown.test.ts:
--------------------------------------------------------------------------------
1 | import { labelYearDropdown } from './labelYearDropdown';
2 |
3 | test('should return the label', () => {
4 | expect(labelYearDropdown()).toEqual('Year: ');
5 | });
6 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/labels/labelMonthDropdown.test.ts:
--------------------------------------------------------------------------------
1 | import { labelMonthDropdown } from './labelMonthDropdown';
2 |
3 | test('should return the label', () => {
4 | expect(labelMonthDropdown()).toEqual('Month: ');
5 | });
6 |
--------------------------------------------------------------------------------
/website/examples/multiple-months.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | return ;
7 | }
8 |
--------------------------------------------------------------------------------
/website/examples/week-iso.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | return ;
7 | }
8 |
--------------------------------------------------------------------------------
/website/examples/multiple-months-paged.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | return ;
7 | }
8 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/formatters/formatWeekNumber.test.ts:
--------------------------------------------------------------------------------
1 | import { formatWeekNumber } from './formatWeekNumber';
2 |
3 | test('should return the formatted week number', () => {
4 | expect(formatWeekNumber(10)).toEqual('10');
5 | });
6 |
--------------------------------------------------------------------------------
/website/examples/keyboard.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker, DayPickerProps } from 'react-day-picker';
4 |
5 | export default function Example(props: DayPickerProps) {
6 | return ;
7 | }
8 |
--------------------------------------------------------------------------------
/website/examples/spanish.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { es } from 'date-fns/locale';
4 | import { DayPicker } from 'react-day-picker';
5 |
6 | export default function App() {
7 | return ;
8 | }
9 |
--------------------------------------------------------------------------------
/website/examples/dropdown.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | return ;
7 | }
8 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/formatters/formatDay.test.ts:
--------------------------------------------------------------------------------
1 | import { formatDay } from './formatDay';
2 |
3 | const date = new Date(2022, 10, 21);
4 |
5 | test('should return the formatted day', () => {
6 | expect(formatDay(date)).toEqual('21');
7 | });
8 |
--------------------------------------------------------------------------------
/website/examples/rtl.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { arSA } from 'date-fns/locale';
4 | import { DayPicker } from 'react-day-picker';
5 |
6 | export default function App() {
7 | return ;
8 | }
9 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/labels/labelDay.test.ts:
--------------------------------------------------------------------------------
1 | import { labelDay } from './labelDay';
2 |
3 | const day = new Date(2022, 10, 21);
4 |
5 | test('should return the day label', () => {
6 | expect(labelDay(day, {})).toEqual('21st November (Monday)');
7 | });
8 |
--------------------------------------------------------------------------------
/website/examples/container-attributes.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function Example() {
6 | return ;
7 | }
8 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/formatters/index.ts:
--------------------------------------------------------------------------------
1 | export * from './formatCaption';
2 | export * from './formatDay';
3 | export * from './formatMonthCaption';
4 | export * from './formatWeekNumber';
5 | export * from './formatWeekdayName';
6 | export * from './formatYearCaption';
7 |
--------------------------------------------------------------------------------
/website/examples/spanish-week-starts-on.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { es } from 'date-fns/locale';
4 | import { DayPicker } from 'react-day-picker';
5 |
6 | export default function App() {
7 | return ;
8 | }
9 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/labels/labelNext.ts:
--------------------------------------------------------------------------------
1 | import { NavButtonLabel } from 'types/Labels';
2 |
3 | /**
4 | * The default ARIA label for next month button in navigation
5 | */
6 | export const labelNext: NavButtonLabel = (): string => {
7 | return 'Go to next month';
8 | };
9 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/labels/labelWeekNumber.ts:
--------------------------------------------------------------------------------
1 | import { WeekNumberLabel } from 'types/Labels';
2 |
3 | /**
4 | * The default ARIA label for the WeekNumber element.
5 | */
6 | export const labelWeekNumber: WeekNumberLabel = (n): string => {
7 | return `Week n. ${n}`;
8 | };
9 |
--------------------------------------------------------------------------------
/website/examples/from-to-year.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | return (
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/formatters/formatDay.ts:
--------------------------------------------------------------------------------
1 | import { format, Locale } from 'date-fns';
2 |
3 | /**
4 | * The default formatter for the Day button.
5 | */
6 | export function formatDay(day: Date, options?: { locale?: Locale }): string {
7 | return format(day, 'd', options);
8 | }
9 |
--------------------------------------------------------------------------------
/website/examples/dropdown-buttons.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | return (
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/formatters/formatYearCaption.test.ts:
--------------------------------------------------------------------------------
1 | import { formatYearCaption } from './formatYearCaption';
2 |
3 | const date = new Date(2022, 10, 21);
4 |
5 | test('should return the formatted weekday name', () => {
6 | expect(formatYearCaption(date)).toEqual('2022');
7 | });
8 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/labels/index.ts:
--------------------------------------------------------------------------------
1 | export * from './labelDay';
2 | export * from './labelMonthDropdown';
3 | export * from './labelNext';
4 | export * from './labelPrevious';
5 | export * from './labelWeekday';
6 | export * from './labelWeekNumber';
7 | export * from './labelYearDropdown';
8 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/labels/labelPrevious.ts:
--------------------------------------------------------------------------------
1 | import { NavButtonLabel } from 'types/Labels';
2 |
3 | /**
4 | * The default ARIA label for previous month button in navigation
5 | */
6 | export const labelPrevious: NavButtonLabel = (): string => {
7 | return 'Go to previous month';
8 | };
9 |
--------------------------------------------------------------------------------
/website/docs/changelog.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Changelog
3 | hide_title: true
4 | pagination_next: null
5 | pagination_prev: null
6 | ---
7 |
8 | import ChangeLog, {
9 | toc as ChangeLogTOC
10 | } from '@site/../CHANGELOG.md';
11 |
12 |
13 |
14 | export const toc = ChangeLogTOC;
15 |
--------------------------------------------------------------------------------
/website/docs/development/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: /development
3 | hide_title: true
4 | sidebar_label: Contributing
5 | ---
6 |
7 | import Contributing, {
8 | toc as ContributingTOC
9 | } from '@site/../CONTRIBUTING.md';
10 |
11 |
12 |
13 | export const toc = ContributingTOC;
14 |
--------------------------------------------------------------------------------
/website/examples/styling-inline.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | return (
7 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/website/docs/contributing.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Contributing
3 | hide_title: true
4 | pagination_next: null
5 | pagination_prev: null
6 | ---
7 |
8 | import Contributing, { toc as ContributingTOC } from '@site/../CONTRIBUTING.md';
9 |
10 |
11 |
12 | export const toc = ContributingTOC;
13 |
--------------------------------------------------------------------------------
/website/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Date Picker Component
3 | hide_title: true
4 | sidebar_label: Welcome
5 | slug: /
6 | hide_table_of_contents: true
7 | ---
8 |
9 | import Readme, {
10 | toc as ReadmeTOC
11 | } from '@site/../README.md';
12 |
13 |
14 |
15 | export const toc = ReadmeTOC;
16 |
--------------------------------------------------------------------------------
/website/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependencies
2 | /node_modules
3 |
4 | # Built
5 | /build
6 |
7 | # Generated files
8 | .docusaurus
9 | .cache-loader
10 | /docs/api
11 |
12 | # Misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 |
--------------------------------------------------------------------------------
/website/src/components/RenderExample.module.css:
--------------------------------------------------------------------------------
1 | .example {
2 | padding: 1em;
3 | }
4 | .example input {
5 | font-size: 1em;
6 | padding: 0.4em 0.6em;
7 | }
8 |
9 | .example button {
10 | font-size: 1em;
11 | padding: 0.4em 0.6em;
12 | }
13 | .example input + button {
14 | margin-left: 0.5em;
15 | }
16 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/formatters/formatCaption.ts:
--------------------------------------------------------------------------------
1 | import { format, Locale } from 'date-fns';
2 |
3 | /**
4 | * The default formatter for the caption.
5 | */
6 | export function formatCaption(
7 | month: Date,
8 | options?: { locale?: Locale }
9 | ): string {
10 | return format(month, 'LLLL y', options);
11 | }
12 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/formatters/formatMonthCaption.ts:
--------------------------------------------------------------------------------
1 | import { format, Locale } from 'date-fns';
2 |
3 | /**
4 | * The default formatter for the Month caption.
5 | */
6 | export function formatMonthCaption(
7 | month: Date,
8 | options?: { locale?: Locale }
9 | ): string {
10 | return format(month, 'LLLL', options);
11 | }
12 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/labels/labelWeekday.ts:
--------------------------------------------------------------------------------
1 | import { format } from 'date-fns';
2 |
3 | import { WeekdayLabel } from 'types/Labels';
4 |
5 | /**
6 | * The default ARIA label for the Weekday element.
7 | */
8 | export const labelWeekday: WeekdayLabel = (day, options): string => {
9 | return format(day, 'cccc', options);
10 | };
11 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/labels/labelDay.ts:
--------------------------------------------------------------------------------
1 | import { format } from 'date-fns';
2 |
3 | import { DayLabel } from 'types/Labels';
4 |
5 | /**
6 | * The default ARIA label for the day button.
7 | */
8 | export const labelDay: DayLabel = (day, activeModifiers, options): string => {
9 | return format(day, 'do MMMM (EEEE)', options);
10 | };
11 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/formatters/formatWeekdayName.ts:
--------------------------------------------------------------------------------
1 | import { format, Locale } from 'date-fns';
2 |
3 | /**
4 | * The default formatter for the name of the weekday.
5 | */
6 | export function formatWeekdayName(
7 | weekday: Date,
8 | options?: { locale?: Locale }
9 | ): string {
10 | return format(weekday, 'cccccc', options);
11 | }
12 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/formatters/formatYearCaption.ts:
--------------------------------------------------------------------------------
1 | import { format, Locale } from 'date-fns';
2 |
3 | /**
4 | * The default formatter for the Year caption.
5 | */
6 | export function formatYearCaption(
7 | year: Date,
8 | options?: {
9 | locale?: Locale;
10 | }
11 | ): string {
12 | return format(year, 'yyyy', options);
13 | }
14 |
--------------------------------------------------------------------------------
/website/README.md:
--------------------------------------------------------------------------------
1 | # DayPicker website
2 |
3 | The website is deployed at this temporary URL: http://react-day-picker-next.netlify.app
4 |
5 | Built using [Docusaurus 2](https://v2.docusaurus.io/) and the [typedoc plugin](https://github.com/tgreyuk/typedoc-plugin-markdown).
6 |
7 | ```
8 | $ pnpm start # start the development environment
9 | ```
10 |
--------------------------------------------------------------------------------
/website/src/theme/CodeBlock/sandpack-app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | react-day-picker example
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/website/examples/dropdown-multiple-months.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | return (
7 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/website/docs/development/code-of-conduct.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: /development/conduct
3 | hide_title: true
4 | pagination_next: null
5 | pagination_prev: null
6 | sidebar_label: Code of Conduct
7 | ---
8 |
9 | import Conduct, {
10 | toc as ConductTOC
11 | } from '@site/../.github/CODE_OF_CONDUCT.md';
12 |
13 |
14 |
15 | export const toc = ConductTOC;
16 |
--------------------------------------------------------------------------------
/website/examples/from-to-month.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | const defaultMonth = new Date(2015, 5);
7 | return (
8 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/website/examples/modifiers-hidden.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | const hiddenDays = [
7 | new Date(2022, 5, 10),
8 | new Date(2022, 5, 20),
9 | new Date(2022, 5, 11)
10 | ];
11 |
12 | return ;
13 | }
14 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | We support security updates for the following versions:
6 |
7 | | Version | Supported |
8 | | ------- | ------------------ |
9 | | 8.9 | :white_check_mark: |
10 | | < 8.9 | :x: |
11 |
12 | ## Reporting a Vulnerability
13 |
14 | To report a vulnerability please write a message to `io@gpbl.dev`.
15 |
--------------------------------------------------------------------------------
/website/src/theme/CodeBlock/sandpack-app/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { StrictMode } from 'react';
2 |
3 | /** Root file used by the Sandpack */
4 | import 'react-day-picker/dist/style.css';
5 | import { render } from 'react-dom';
6 |
7 | import App from './App';
8 | import './styles.css';
9 |
10 | render(
11 |
12 |
13 | ,
14 | document.getElementById('root')
15 | );
16 |
--------------------------------------------------------------------------------
/src/contexts/Modifiers/utils/matcherToArray.ts:
--------------------------------------------------------------------------------
1 | import { Matcher } from 'types/Matchers';
2 |
3 | /** Normalize to array a matcher input. */
4 | export function matcherToArray(
5 | matcher: Matcher | Matcher[] | undefined
6 | ): Matcher[] {
7 | if (Array.isArray(matcher)) {
8 | return [...matcher];
9 | } else if (matcher !== undefined) {
10 | return [matcher];
11 | } else {
12 | return [];
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/website/docs/guides/formatters.md:
--------------------------------------------------------------------------------
1 | # Formatters
2 |
3 | Use the [formatters](/api/interfaces/daypickerdefaultprops#formatters) to change the default format for the day, the weekday name, etc.
4 |
5 | Formatters can be useful for a custom [localization](/basics/localization).
6 |
7 | ## Example: add emoji to the calendar
8 |
9 | The following example add some emoji to the caption and to the day cells.
10 |
11 | ```include-example
12 | formatters
13 | ```
14 |
--------------------------------------------------------------------------------
/website/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@tsconfig/docusaurus/tsconfig.json",
3 | "compilerOptions": {
4 | "strict": true,
5 | "jsx": "react",
6 | "baseUrl": ".",
7 | "lib": ["es2019", "DOM"],
8 | "resolveJsonModule": true,
9 | "skipLibCheck": true,
10 | "module": "Node16",
11 | "paths": {
12 | "@examples/*": ["./examples/*"],
13 | "@site/*": ["./*"]
14 | },
15 | "types": ["jest", "node"]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/Head/Head.tsx:
--------------------------------------------------------------------------------
1 | import { HeadRow } from 'components/HeadRow';
2 | import { useDayPicker } from 'contexts/DayPicker';
3 |
4 | /** Render the table head. */
5 | export function Head(): JSX.Element {
6 | const { classNames, styles, components } = useDayPicker();
7 | const HeadRowComponent = components?.HeadRow ?? HeadRow;
8 | return (
9 |
10 |
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/src/contexts/Modifiers/utils/getCustomModifiers.test.ts:
--------------------------------------------------------------------------------
1 | import { DayModifiers } from 'index';
2 |
3 | import { getCustomModifiers } from './getCustomModifiers';
4 |
5 | describe('when some modifiers are not an array', () => {
6 | const date = new Date();
7 | const dayModifiers: DayModifiers = {
8 | foo: date
9 | };
10 | const result = getCustomModifiers(dayModifiers);
11 | test('should return as array', () => {
12 | expect(result.foo).toEqual([date]);
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/website/src/components/RenderExample.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import styles from './RenderExample.module.css';
4 |
5 | export function RenderExample(props: {
6 | name: string;
7 | rootStyle?: React.CSSProperties;
8 | }) {
9 | // eslint-disable-next-line @typescript-eslint/no-var-requires
10 | const Component = require(`@site/examples/${props.name}`).default;
11 | return (
12 |
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/website/docusaurus.typedoc.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 | const typedoc = {
3 | // Typedoc settings
4 | entryPoints: [`../src/index.ts`],
5 | tsconfig: `../tsconfig.build.json`,
6 | allReflectionsHaveOwnDocument: true,
7 | entryDocument: 'reference',
8 | watch: process.env.TYPEDOC_WATCH,
9 | out: './api',
10 |
11 | // Markdown plugin settings
12 | hideBreadcrumbs: true,
13 | hideInPageTOC: true,
14 | indexTitle: 'Exports',
15 | publicPath: '/api/'
16 | };
17 |
18 | module.exports = typedoc;
19 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/labels/labelWeekday.test.ts:
--------------------------------------------------------------------------------
1 | import { es } from 'date-fns/locale';
2 |
3 | import { labelWeekday } from './labelWeekday';
4 |
5 | const weekDay = new Date(2022, 10, 21);
6 |
7 | test('should return the formatted weekday name', () => {
8 | expect(labelWeekday(weekDay)).toEqual('Monday');
9 | });
10 |
11 | describe('when a locale is passed in', () => {
12 | test('should format using the locale', () => {
13 | expect(labelWeekday(weekDay, { locale: es })).toEqual('lunes');
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/test/utils/focusDaysGrid.ts:
--------------------------------------------------------------------------------
1 | import { fireEvent } from '@testing-library/dom';
2 | import { act } from '@testing-library/react';
3 |
4 | import { user } from 'test/user';
5 |
6 | import { getFocusedElement } from '../selectors';
7 |
8 | export async function focusDaysGrid() {
9 | // Make sure nothing is focused
10 | await act(() => fireEvent.blur(getFocusedElement()));
11 | // By pressing tab 3 times
12 | await act(() => user.tab());
13 | await act(() => user.tab());
14 | await act(() => user.tab());
15 | }
16 |
--------------------------------------------------------------------------------
/website/examples/custom-day.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { format } from 'date-fns';
4 | import { DayContent, DayContentProps, DayPicker } from 'react-day-picker';
5 |
6 | function DateTime(props: DayContentProps) {
7 | const dateTime = format(props.date, 'yyyy-MM-dd');
8 | return (
9 |
12 | );
13 | }
14 |
15 | export default function App() {
16 | return ;
17 | }
18 |
--------------------------------------------------------------------------------
/website/examples/styling-css.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | const style = `
7 | .caption_aqua {
8 | color: aquamarine;
9 | font-weight: bold;
10 | font-size: 140%;
11 | }
12 | `;
13 | return (
14 | <>
15 |
16 |
21 | >
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/website/examples/weeknumber.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | const [weekNumber, setWeekNumber] = useState();
7 |
8 | const footer = weekNumber
9 | ? `You clicked the week n. ${weekNumber}.`
10 | : 'Try clicking a week number.';
11 |
12 | return (
13 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/formatters/formatCaption.test.ts:
--------------------------------------------------------------------------------
1 | import { es } from 'date-fns/locale';
2 |
3 | import { formatCaption } from './formatCaption';
4 |
5 | const date = new Date(2022, 10, 21);
6 |
7 | test('should return the formatted caption', () => {
8 | expect(formatCaption(date)).toEqual('November 2022');
9 | });
10 |
11 | describe('when a locale is passed in', () => {
12 | test('should format using the locale', () => {
13 | expect(formatCaption(date, { locale: es })).toEqual('noviembre 2022');
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/formatters/formatWeekdayName.test.ts:
--------------------------------------------------------------------------------
1 | import { es } from 'date-fns/locale';
2 |
3 | import { formatWeekdayName } from './formatWeekdayName';
4 |
5 | const date = new Date(2022, 10, 21);
6 |
7 | test('should return the formatted weekday name', () => {
8 | expect(formatWeekdayName(date)).toEqual('Mo');
9 | });
10 |
11 | describe('when a locale is passed in', () => {
12 | test('should format using the locale', () => {
13 | expect(formatWeekdayName(date, { locale: es })).toEqual('lu');
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/website/examples/modifiers-disabled.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | const disabledDays = [
7 | new Date(2022, 5, 10),
8 | new Date(2022, 5, 12),
9 | new Date(2022, 5, 20),
10 | { from: new Date(2022, 4, 18), to: new Date(2022, 4, 29) }
11 | ];
12 |
13 | return (
14 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/website/examples/weeknumber-custom.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | return (
7 | `W${weekNumber}`
14 | }}
15 | />
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/website/test-integration/examples/formatters.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { freezeBeforeAll } from '@site/test/utils';
4 | import { render, screen } from '@testing-library/react';
5 |
6 | import Example from '@examples/formatters';
7 |
8 | const today = new Date(2021, 10, 25);
9 | freezeBeforeAll(today);
10 |
11 | beforeEach(() => {
12 | render();
13 | });
14 |
15 | test('should display the autumn emoji', () => {
16 | expect(screen.getByRole('img', { name: 'autumn' })).toBeInTheDocument();
17 | });
18 |
--------------------------------------------------------------------------------
/src/components/WeekNumber/__snapshots__/WeekNumber.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`with "onWeekNumberClick" prop it should return a button element 1`] = `
4 |
12 | `;
13 |
14 | exports[`without "onWeekNumberClick" prop it should return a span element 1`] = `
15 |
18 | 10
19 |
20 | `;
21 |
--------------------------------------------------------------------------------
/website/test/utils/focusDaysGrid.ts:
--------------------------------------------------------------------------------
1 | import { fireEvent } from '@testing-library/dom';
2 | import { act } from '@testing-library/react';
3 |
4 | import { getFocusedElement } from 'react-day-picker/test/selectors';
5 |
6 | import { user } from '../user';
7 |
8 | export async function focusDaysGrid() {
9 | // Make sure nothing is focused
10 | await act(() => fireEvent.blur(getFocusedElement()));
11 | // By pressing tab 3 times
12 | await act(() => user.tab());
13 | await act(() => user.tab());
14 | await act(() => user.tab());
15 | }
16 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/formatters/formatMonthCaption.test.ts:
--------------------------------------------------------------------------------
1 | import { es } from 'date-fns/locale';
2 |
3 | import { formatMonthCaption } from './formatMonthCaption';
4 |
5 | const date = new Date(2022, 10, 21);
6 |
7 | test('should return the formatted month caption', () => {
8 | expect(formatMonthCaption(date)).toEqual('November');
9 | });
10 |
11 | describe('when a locale is passed in', () => {
12 | test('should format using the locale', () => {
13 | expect(formatMonthCaption(date, { locale: es })).toEqual('noviembre');
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/src/components/IconLeft/IconLeft.test.tsx:
--------------------------------------------------------------------------------
1 | import { customRender } from 'test/render';
2 |
3 | import { IconLeft } from './IconLeft';
4 |
5 | let root: HTMLElement;
6 |
7 | beforeEach(() => {
8 | const view = customRender(
9 |
10 | );
11 | root = view.container.firstChild as HTMLElement;
12 | });
13 | test('should add the class name', () => {
14 | expect(root).toHaveClass('foo');
15 | });
16 | test('should apply the style', () => {
17 | expect(root).toHaveStyle({ color: 'red' });
18 | });
19 |
--------------------------------------------------------------------------------
/src/contexts/Modifiers/utils/getCustomModifiers.ts:
--------------------------------------------------------------------------------
1 | import { CustomModifiers, DayModifiers } from 'types/Modifiers';
2 |
3 | import { matcherToArray } from './matcherToArray';
4 |
5 | /** Create CustomModifiers from dayModifiers */
6 | export function getCustomModifiers(
7 | dayModifiers: DayModifiers
8 | ): CustomModifiers {
9 | const customModifiers: CustomModifiers = {};
10 | Object.entries(dayModifiers).forEach(([modifier, matcher]) => {
11 | customModifiers[modifier] = matcherToArray(matcher);
12 | });
13 | return customModifiers;
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/IconRight/IconRight.test.tsx:
--------------------------------------------------------------------------------
1 | import { customRender } from 'test/render';
2 |
3 | import { IconRight } from './IconRight';
4 |
5 | let root: HTMLElement;
6 |
7 | beforeEach(() => {
8 | const view = customRender(
9 |
10 | );
11 | root = view.container.firstChild as HTMLElement;
12 | });
13 | test('should add the class name', () => {
14 | expect(root).toHaveClass('foo');
15 | });
16 | test('should apply the style', () => {
17 | expect(root).toHaveStyle({ color: 'red' });
18 | });
19 |
--------------------------------------------------------------------------------
/src/components/Months/Months.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react';
2 |
3 | import { useDayPicker } from 'contexts/DayPicker';
4 |
5 | /** The props for the {@link Months} component. */
6 | export type MonthsProps = { children: ReactNode };
7 |
8 | /**
9 | * Render the wrapper for the month grids.
10 | */
11 | export function Months(props: MonthsProps): JSX.Element {
12 | const { classNames, styles } = useDayPicker();
13 |
14 | return (
15 |
16 | {props.children}
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/website/examples/focus-recursive.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | /** Test for the next focus day to not cause an infinite recursion. */
6 | export default function App() {
7 | const disabledDays = [
8 | new Date(2022, 5, 4),
9 | {
10 | after: new Date(2022, 5, 26)
11 | }
12 | ];
13 |
14 | return (
15 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/IconDropdown/IconDropdown.test.tsx:
--------------------------------------------------------------------------------
1 | import { customRender } from 'test/render';
2 |
3 | import { IconDropdown } from './IconDropdown';
4 |
5 | let root: HTMLElement;
6 |
7 | beforeEach(() => {
8 | const view = customRender(
9 |
10 | );
11 | root = view.container.firstChild as HTMLElement;
12 | });
13 | test('should add the class name', () => {
14 | expect(root).toHaveClass('foo');
15 | });
16 | test('should apply the style', () => {
17 | expect(root).toHaveStyle({ color: 'red' });
18 | });
19 |
--------------------------------------------------------------------------------
/website/examples/start.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { format } from 'date-fns';
4 | import { DayPicker } from 'react-day-picker';
5 |
6 | export default function Example() {
7 | const [selected, setSelected] = React.useState();
8 |
9 | let footer = Please pick a day.
;
10 | if (selected) {
11 | footer = You picked {format(selected, 'PP')}.
;
12 | }
13 | return (
14 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/website/examples/useinput.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker, useInput } from 'react-day-picker';
4 |
5 | export default function App() {
6 | const { inputProps, dayPickerProps } = useInput({
7 | defaultSelected: new Date(),
8 | fromYear: 2021,
9 | toYear: 2023,
10 | format: 'PP',
11 | required: true
12 | });
13 |
14 | const footer = (
15 |
20 | );
21 | return ;
22 | }
23 |
--------------------------------------------------------------------------------
/website/examples/modifiers-style.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | const availableDays = [new Date(2021, 5, 23), new Date(2021, 5, 24)];
6 |
7 | const availableStyle = {
8 | fontWeight: 900,
9 | color: 'lightgreen'
10 | };
11 |
12 | export default function App() {
13 | return (
14 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/website/test-integration/examples/default-month.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { freezeBeforeAll } from '@site/test/utils';
4 | import { render } from '@testing-library/react';
5 |
6 | import { getMonthCaption } from 'react-day-picker/test/selectors';
7 |
8 | import Example from '@examples/default-month';
9 |
10 | const today = new Date(2022, 5, 10);
11 | freezeBeforeAll(today);
12 |
13 | beforeEach(() => {
14 | render();
15 | });
16 |
17 | test('should display September 1979', () => {
18 | expect(getMonthCaption()).toHaveTextContent('September 1979');
19 | });
20 |
--------------------------------------------------------------------------------
/website/examples/single.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import { format } from 'date-fns';
4 | import { DayPicker } from 'react-day-picker';
5 |
6 | export default function App() {
7 | const [selectedDay, setSelectedDay] = useState();
8 |
9 | const footer = selectedDay ? (
10 | You selected {format(selectedDay, 'PPP')}.
11 | ) : (
12 | Please pick a day.
13 | );
14 |
15 | return (
16 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/src/types/DayPickerDefault.ts:
--------------------------------------------------------------------------------
1 | import { DayPickerProps } from 'DayPicker';
2 |
3 | import { DayPickerBase } from './DayPickerBase';
4 |
5 | /** The props for the {@link DayPicker} component when using `mode="default"` or `undefined`. */
6 | export interface DayPickerDefaultProps extends DayPickerBase {
7 | mode?: undefined | 'default';
8 | }
9 |
10 | /** Returns true when the props are of type {@link DayPickerDefaultProps}. */
11 | export function isDayPickerDefault(
12 | props: DayPickerProps
13 | ): props is DayPickerDefaultProps {
14 | return props.mode === undefined || props.mode === 'default';
15 | }
16 |
--------------------------------------------------------------------------------
/test/render/customRender.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 |
3 | import { render } from '@testing-library/react';
4 | import { DayPickerProps } from 'DayPicker';
5 |
6 | import { RootProvider } from 'contexts/RootProvider';
7 |
8 | /** Render a React Element wrapped with the Root Provider. */
9 | export function customRender(
10 | /** The element to render. */
11 | element: ReactElement,
12 | /** The initial DayPicker props to pass to the Root Provider. */
13 | dayPickerProps: DayPickerProps = {}
14 | ) {
15 | return render({element});
16 | }
17 |
--------------------------------------------------------------------------------
/jest.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from '@jest/types';
2 |
3 | const config: Config.InitialOptions = {
4 | preset: 'ts-jest',
5 | roots: ['./src'],
6 | moduleNameMapper: {
7 | '^test/(.*)': ['/test/$1'],
8 | '^components(.*)': ['/src/components$1'],
9 | '^contexts(.*)': ['/src/contexts$1'],
10 | '^hooks(.*)': ['/src/hooks$1'],
11 | '^types(.*)': ['/src/types$1']
12 | },
13 | testEnvironment: 'jsdom',
14 | coverageReporters: ['lcov', 'text', 'clover'],
15 | setupFilesAfterEnv: ['./test/setup.ts']
16 | };
17 |
18 | export default config;
19 |
--------------------------------------------------------------------------------
/website/examples/custom-single.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | const [selectedDay, setSelectedDay] = useState();
7 | const handleDayClick = (day: Date) => setSelectedDay(day);
8 |
9 | const footer = selectedDay ? (
10 | You selected {selectedDay.toDateString()}.
11 | ) : (
12 | Please pick a day.
13 | );
14 |
15 | return (
16 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/website/examples/multiple-min-max.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | const [days, setDays] = React.useState();
7 |
8 | const footer =
9 | days && days.length > 0 ? (
10 | You selected {days.length} day(s).
11 | ) : (
12 | Please pick one or more days.
13 | );
14 |
15 | return (
16 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/website/test-integration/examples/testcase-1567.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { user } from '@site/test/user';
4 | import { act, render, screen } from '@testing-library/react';
5 |
6 | import Example from '@examples/testcase-1567';
7 |
8 | beforeEach(async () => {
9 | render();
10 | await act(() => user.tab());
11 | await act(() => user.tab());
12 | await act(() => user.tab());
13 | await act(() => user.tab());
14 | });
15 |
16 | test('the button should have focus', () => {
17 | expect(
18 | screen.getByRole('button', { name: 'I should be focusable' })
19 | ).toHaveFocus();
20 | });
21 |
--------------------------------------------------------------------------------
/website/examples/modifiers-today.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import { DayClickEventHandler, DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | const initialFooter = Try clicking today’s date.
;
7 | const [footer, setFooter] = useState(initialFooter);
8 |
9 | const handleDayClick: DayClickEventHandler = (day, modifiers) => {
10 | if (modifiers.today) {
11 | setFooter(You clicked today’s date.
);
12 | } else {
13 | setFooter(initialFooter);
14 | }
15 | };
16 |
17 | return ;
18 | }
19 |
--------------------------------------------------------------------------------
/website/test-integration/examples/styling-css.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { freezeBeforeAll } from '@site/test/utils';
4 | import { render } from '@testing-library/react';
5 |
6 | import Example from '@examples/styling-css';
7 |
8 | const today = new Date(2021, 10, 25);
9 | freezeBeforeAll(today);
10 |
11 | let container: HTMLElement;
12 | beforeEach(() => {
13 | container = render().container;
14 | });
15 |
16 | test('the caption should use the custom class name', () => {
17 | expect(container.getElementsByClassName('caption_aqua')[0]).toHaveTextContent(
18 | 'November 2021'
19 | );
20 | });
21 |
--------------------------------------------------------------------------------
/website/test-integration/examples/styling-inline.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { freezeBeforeAll } from '@site/test/utils';
4 | import { render } from '@testing-library/react';
5 |
6 | import { getMonthCaption } from 'react-day-picker/test/selectors';
7 |
8 | import Example from '@examples/styling-inline';
9 |
10 | const today = new Date(2021, 10, 25);
11 | freezeBeforeAll(today);
12 |
13 | beforeEach(() => {
14 | render().container;
15 | });
16 |
17 | test('the caption should apply the custom style', () => {
18 | expect(getMonthCaption(0).parentElement).toHaveStyle({
19 | color: 'red'
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/website/test-integration/examples/weeknumber-custom.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { freezeBeforeAll } from '@site/test/utils';
4 | import { render, screen } from '@testing-library/react';
5 |
6 | import Example from '@examples/weeknumber-custom';
7 |
8 | const today = new Date(2022, 0, 1);
9 | freezeBeforeAll(today);
10 |
11 | beforeEach(() => render());
12 |
13 | describe('when displaying January 2022', () => {
14 | test('should display the 53th week', () => {
15 | const week53 = screen.getByRole('row', {
16 | name: /^W53/
17 | });
18 | expect(week53).toBeInTheDocument();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/website/examples/controlled.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import { addMonths, isSameMonth } from 'date-fns';
4 | import { DayPicker } from 'react-day-picker';
5 |
6 | export default function App() {
7 | const today = new Date();
8 | const nextMonth = addMonths(new Date(), 1);
9 | const [month, setMonth] = useState(nextMonth);
10 |
11 | const footer = (
12 |
18 | );
19 |
20 | return ;
21 | }
22 |
--------------------------------------------------------------------------------
/website/examples/multiple.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | export default function App() {
6 | const initialDays: Date[] = [];
7 | const [days, setDays] = React.useState(initialDays);
8 |
9 | const footer =
10 | days && days.length > 0 ? (
11 | You selected {days.length} day(s).
12 | ) : (
13 | Please pick one or more days.
14 | );
15 |
16 | return (
17 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/website/test-integration/examples/spanish-week-starts-on.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { freezeBeforeAll } from '@site/test/utils';
4 | import { render } from '@testing-library/react';
5 |
6 | import { getMonthGrid } from 'react-day-picker/test/selectors';
7 |
8 | import Example from '@examples/spanish-week-starts-on';
9 |
10 | const today = new Date(2021, 10, 25);
11 | freezeBeforeAll(today);
12 |
13 | beforeEach(() => render());
14 |
15 | test('should have "Domingo" as first day of week', () => {
16 | expect(
17 | getMonthGrid().firstChild?.firstChild?.firstChild
18 | ).toHaveAccessibleName('domingo');
19 | });
20 |
--------------------------------------------------------------------------------
/website/test-integration/examples/custom-day.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { freezeBeforeAll } from '@site/test/utils';
4 | import { render } from '@testing-library/react';
5 | import { getDaysInMonth } from 'date-fns';
6 |
7 | import Example from '@examples/custom-day';
8 |
9 | const today = new Date(2021, 10, 25);
10 | freezeBeforeAll(today);
11 |
12 | let container: HTMLElement;
13 | beforeEach(() => {
14 | container = render().container;
15 | });
16 |
17 | test('should render time elements', () => {
18 | const timeElements = container.getElementsByTagName('time');
19 | expect(timeElements).toHaveLength(getDaysInMonth(today));
20 | });
21 |
--------------------------------------------------------------------------------
/website/examples/single-required.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import { format } from 'date-fns';
4 | import { DayPicker } from 'react-day-picker';
5 |
6 | export default function App() {
7 | const today = new Date();
8 | const [selectedDay, setSelectedDay] = useState(today);
9 |
10 | const footer = selectedDay ? (
11 | You selected {format(selectedDay, 'PPP')}.
12 | ) : (
13 | Please pick a day.
14 | );
15 |
16 | return (
17 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/website/examples/styling-css-modules.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { ClassNames, DayPicker } from 'react-day-picker';
4 | import styles from 'react-day-picker/dist/style.module.css';
5 |
6 | export default function App() {
7 | const [selectedDay, setSelectedDay] = React.useState();
8 |
9 | const classNames: ClassNames = {
10 | ...styles,
11 | head: 'custom-head'
12 | };
13 | return (
14 | <>
15 |
16 |
22 | >
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/Footer/Footer.tsx:
--------------------------------------------------------------------------------
1 | import { useDayPicker } from 'contexts/DayPicker';
2 |
3 | export interface FooterProps {
4 | /** The month where the footer is displayed. */
5 | displayMonth?: Date;
6 | }
7 | /** Render the Footer component (empty as default).*/
8 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
9 | export function Footer(props: FooterProps): JSX.Element {
10 | const {
11 | footer,
12 | styles,
13 | classNames: { tfoot }
14 | } = useDayPicker();
15 | if (!footer) return <>>;
16 | return (
17 |
18 |
19 | | {footer} |
20 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/website/examples/modifiers-classnames.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | const bookedDays = [
6 | new Date(2021, 5, 8),
7 | new Date(2021, 5, 9),
8 | new Date(2021, 5, 11)
9 | ];
10 |
11 | const style = `
12 | .my-booked-class {
13 | color: tomato;
14 | }
15 | `;
16 |
17 | export default function App() {
18 | return (
19 | <>
20 |
21 |
30 | >
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/website/docs/start.md:
--------------------------------------------------------------------------------
1 | ---
2 | hide_table_of_contents: true
3 | ---
4 |
5 | # Getting Started
6 |
7 | 1. Add `react-day-picker` and [date-fns](https://date-fns.org) to your dependencies:
8 |
9 | ```bash
10 | npm install react-day-picker date-fns # with npm
11 | pnpm install react-day-picker date-fns # with pnpm
12 | yarn add react-day-picker date-fns # with yarn
13 | ```
14 |
15 | 2. When importing, include the DayPicker CSS in your component:
16 |
17 | ```tsx
18 | import { DayPicker } from 'react-day-picker';
19 | import 'react-day-picker/dist/style.css';
20 |
21 | function Component() {
22 | return ;
23 | }
24 | ```
25 |
26 | ## Example
27 |
28 | ```include-example
29 | start
30 | ```
31 |
--------------------------------------------------------------------------------
/website/test-integration/examples/modifiers-disabled.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { freezeBeforeAll } from '@site/test/utils';
4 | import { render } from '@testing-library/react';
5 |
6 | import { getDayButton } from 'react-day-picker/test/selectors';
7 |
8 | import Example from '@examples/modifiers-disabled';
9 |
10 | const days = [
11 | new Date(2022, 5, 10),
12 | new Date(2022, 5, 12),
13 | new Date(2022, 5, 20)
14 | ];
15 | const today = new Date(2021, 10, 25);
16 | freezeBeforeAll(today);
17 |
18 | beforeEach(() => {
19 | render();
20 | });
21 |
22 | test.each(days)('the day %s should be disabled', (day) => {
23 | expect(getDayButton(day)).toBeDisabled();
24 | });
25 |
--------------------------------------------------------------------------------
/website/examples/custom-disable-row.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { differenceInCalendarDays } from 'date-fns';
4 | import { DayPicker, Row, RowProps } from 'react-day-picker';
5 |
6 | function isPastDate(date: Date) {
7 | return differenceInCalendarDays(date, new Date()) < 0;
8 | }
9 |
10 | function OnlyFutureRow(props: RowProps) {
11 | const isPastRow = props.dates.every(isPastDate);
12 | if (isPastRow) return <>>;
13 | return
;
14 | }
15 |
16 | export default function App() {
17 | return (
18 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/website/test-integration/examples/modifiers-hidden.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { freezeBeforeAll } from '@site/test/utils';
4 | import { render } from '@testing-library/react';
5 |
6 | import { queryDayButton } from 'react-day-picker/test/selectors';
7 |
8 | import Example from '@examples/modifiers-hidden';
9 |
10 | const days = [
11 | new Date(2022, 5, 10),
12 | new Date(2022, 5, 20),
13 | new Date(2022, 5, 11)
14 | ];
15 |
16 | const today = new Date(2021, 10, 25);
17 | freezeBeforeAll(today);
18 |
19 | beforeEach(() => {
20 | render();
21 | });
22 |
23 | test.each(days)('the day %s should be hidden', (day) => {
24 | expect(queryDayButton(day)).not.toBeInTheDocument();
25 | });
26 |
--------------------------------------------------------------------------------
/src/hooks/useDayRender/utils/getDayStyle.ts:
--------------------------------------------------------------------------------
1 | import { CSSProperties } from 'react';
2 |
3 | import { DayPickerContextValue } from 'contexts/DayPicker';
4 | import { ActiveModifiers } from 'types/Modifiers';
5 |
6 | /** Return the style for the Day element, according to the given active modifiers. */
7 | export function getDayStyle(
8 | dayPicker: Pick,
9 | activeModifiers: ActiveModifiers
10 | ): CSSProperties {
11 | let style: CSSProperties = {
12 | ...dayPicker.styles.day
13 | };
14 | Object.keys(activeModifiers).forEach((modifier) => {
15 | style = {
16 | ...style,
17 | ...dayPicker.modifiersStyles?.[modifier]
18 | };
19 | });
20 | return style;
21 | }
22 |
--------------------------------------------------------------------------------
/website/jest.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from '@jest/types';
2 | import { pathsToModuleNameMapper } from 'ts-jest';
3 |
4 | import { compilerOptions } from './tsconfig.json';
5 |
6 | const config: Config.InitialOptions = {
7 | preset: 'ts-jest',
8 | roots: ['./test-integration'],
9 | transform: {
10 | '^.+\\.tsx?$': 'ts-jest'
11 | },
12 | moduleNameMapper: {
13 | ...pathsToModuleNameMapper(compilerOptions.paths, {
14 | prefix: ''
15 | }),
16 | '\\.css$': 'identity-obj-proxy',
17 | '@generated/(.*)': 'identity-obj-proxy'
18 | },
19 | testEnvironment: 'jsdom',
20 | coverageReporters: ['lcov', 'text', 'clover'],
21 | setupFilesAfterEnv: ['./test/setup.ts']
22 | };
23 |
24 | export default config;
25 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "jsx": "react-jsx",
5 | "module": "es6",
6 | "moduleResolution": "node",
7 | "rootDir": "./",
8 | "baseUrl": "./src",
9 | "noEmit": true,
10 | "paths": {
11 | "test/*": ["../test/*"]
12 | },
13 | "declaration": true,
14 | "sourceMap": true,
15 | "outDir": "./dist",
16 | "importHelpers": true,
17 | "esModuleInterop": false,
18 | "allowSyntheticDefaultImports": false,
19 | "forceConsistentCasingInFileNames": true,
20 | "strict": true,
21 | "skipLibCheck": true,
22 | "resolveJsonModule": true,
23 | "isolatedModules": false,
24 | "types": ["jest", "node"]
25 | },
26 | "include": ["test", "src"]
27 | }
28 |
--------------------------------------------------------------------------------
/website/test-integration/examples/modifiers-classnames.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { freezeBeforeAll } from '@site/test/utils';
4 | import { render } from '@testing-library/react';
5 |
6 | import { getDayButton } from 'react-day-picker/test/selectors';
7 |
8 | import Example from '@examples/modifiers-classnames';
9 |
10 | const days = [
11 | new Date(2021, 5, 8),
12 | new Date(2021, 5, 9),
13 | new Date(2021, 5, 11)
14 | ];
15 |
16 | const today = new Date(2021, 10, 25);
17 | freezeBeforeAll(today);
18 |
19 | beforeEach(() => {
20 | render();
21 | });
22 |
23 | test.each(days)('the day %s should have the `my-booked-class` class', (day) => {
24 | expect(getDayButton(day)).toHaveClass('my-booked-class');
25 | });
26 |
--------------------------------------------------------------------------------
/website/test-integration/examples/modifiers-style.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { freezeBeforeAll } from '@site/test/utils';
4 | import { render } from '@testing-library/react';
5 |
6 | import { getDayButton } from 'react-day-picker/test/selectors';
7 |
8 | import Example from '@examples/modifiers-style';
9 |
10 | const today = new Date(2021, 10, 25);
11 | freezeBeforeAll(today);
12 |
13 | beforeEach(() => {
14 | render();
15 | });
16 |
17 | const days = [new Date(2021, 5, 23), new Date(2021, 5, 24)];
18 | const style = {
19 | fontWeight: 900,
20 | color: 'lightgreen'
21 | };
22 | test.each(days)('The day %s should have the proper inline style', (day) => {
23 | expect(getDayButton(day)).toHaveStyle(style);
24 | });
25 |
--------------------------------------------------------------------------------
/website/test-integration/examples/fixedweeks.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { axe } from '@site/test/axe';
4 | import { freezeBeforeAll } from '@site/test/utils';
5 | import { render } from '@testing-library/react';
6 |
7 | import Example from '@examples/fixedweeks';
8 |
9 | const today = new Date(2021, 10, 25);
10 | freezeBeforeAll(today);
11 |
12 | let container: HTMLElement;
13 | beforeEach(() => {
14 | container = render().container;
15 | });
16 |
17 | test('should not have AXE violations', async () => {
18 | expect(await axe(container)).toHaveNoViolations();
19 | });
20 |
21 | test('should render 7 rows', () => {
22 | const rowElements = container.getElementsByTagName('tr');
23 | expect(rowElements).toHaveLength(7);
24 | });
25 |
--------------------------------------------------------------------------------
/src/components/DayContent/DayContent.tsx:
--------------------------------------------------------------------------------
1 | import { useDayPicker } from 'contexts/DayPicker';
2 | import { ActiveModifiers } from 'types/Modifiers';
3 |
4 | /** Represent the props for the {@link DayContent} component. */
5 | export interface DayContentProps {
6 | /** The date representing the day. */
7 | date: Date;
8 | /** The month where the day is displayed. */
9 | displayMonth: Date;
10 | /** The active modifiers for the given date. */
11 | activeModifiers: ActiveModifiers;
12 | }
13 |
14 | /** Render the content of the day cell. */
15 | export function DayContent(props: DayContentProps): JSX.Element {
16 | const {
17 | locale,
18 | formatters: { formatDay }
19 | } = useDayPicker();
20 |
21 | return <>{formatDay(props.date, { locale })}>;
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/Footer/Footer.test.tsx:
--------------------------------------------------------------------------------
1 | import { customRender } from 'test/render';
2 | import { getTableFooter, queryTableFooter } from 'test/selectors';
3 |
4 | import { Footer } from './Footer';
5 |
6 | customRender(
7 |
10 | );
11 | test('should not render anything as default', () => {
12 | expect(queryTableFooter()).toBeNull();
13 | });
14 |
15 | describe('when using the `footer` prop', () => {
16 | beforeEach(() => {
17 | customRender(
18 | ,
21 | { footer: 'footer_foo' }
22 | );
23 | });
24 | test('should render the table footer', () => {
25 | expect(getTableFooter()).toHaveTextContent('footer_foo');
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/src/hooks/useActiveModifiers/useActiveModifiers.tsx:
--------------------------------------------------------------------------------
1 | import { getActiveModifiers, useModifiers } from 'contexts/Modifiers';
2 | import { ActiveModifiers } from 'types/Modifiers';
3 |
4 | /**
5 | * Return the active modifiers for the specified day.
6 | *
7 | * This hook is meant to be used inside internal or custom components.
8 | *
9 | * @param day
10 | * @param displayMonth
11 | */
12 | export function useActiveModifiers(
13 | day: Date,
14 | /**
15 | * The month where the date is displayed. If not the same as `date`, the day
16 | * is an "outside day".
17 | */
18 | displayMonth?: Date
19 | ): ActiveModifiers {
20 | const modifiers = useModifiers();
21 | const activeModifiers = getActiveModifiers(day, modifiers, displayMonth);
22 | return activeModifiers;
23 | }
24 |
--------------------------------------------------------------------------------
/website/test-integration/examples/custom-disable-row.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { axe } from '@site/test/axe';
4 | import { freezeBeforeAll } from '@site/test/utils';
5 | import { render } from '@testing-library/react';
6 |
7 | import Example from '@examples/custom-disable-row';
8 |
9 | const today = new Date(2021, 10, 25);
10 | freezeBeforeAll(today);
11 |
12 | let container: HTMLElement;
13 | beforeEach(() => {
14 | container = render().container;
15 | });
16 |
17 | test('should not have AXE violations', async () => {
18 | expect(await axe(container)).toHaveNoViolations();
19 | });
20 |
21 | test('should render only 3 rows', () => {
22 | const rowElements = container.getElementsByTagName('tr');
23 | expect(rowElements).toHaveLength(3);
24 | });
25 |
--------------------------------------------------------------------------------
/src/components/HeadRow/utils/getWeekdays.ts:
--------------------------------------------------------------------------------
1 | import { addDays, Locale, startOfISOWeek, startOfWeek } from 'date-fns';
2 |
3 | /**
4 | * Generate a series of 7 days, starting from the week, to use for formatting
5 | * the weekday names (Monday, Tuesday, etc.).
6 | */
7 | export function getWeekdays(
8 | locale?: Locale,
9 | /** The index of the first day of the week (0 - Sunday). */
10 | weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6,
11 | /** Use ISOWeek instead of locale/ */
12 | ISOWeek?: boolean
13 | ): Date[] {
14 | const start = ISOWeek
15 | ? startOfISOWeek(new Date())
16 | : startOfWeek(new Date(), { locale, weekStartsOn });
17 |
18 | const days = [];
19 | for (let i = 0; i < 7; i++) {
20 | const day = addDays(start, i);
21 | days.push(day);
22 | }
23 | return days;
24 | }
25 |
--------------------------------------------------------------------------------
/website/test-integration/examples/spanish.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { axe } from '@site/test/axe';
4 | import { freezeBeforeAll } from '@site/test/utils';
5 | import { render } from '@testing-library/react';
6 |
7 | import { getMonthCaption } from 'react-day-picker/test/selectors';
8 |
9 | import Example from '@examples/spanish';
10 |
11 | const today = new Date(2021, 10, 25);
12 | freezeBeforeAll(today);
13 |
14 | let container: HTMLElement;
15 | beforeEach(() => (container = render().container));
16 |
17 | test('should not have AXE violations', async () => {
18 | expect(await axe(container)).toHaveNoViolations();
19 | });
20 |
21 | test('should localize the caption in Spanish', () => {
22 | expect(getMonthCaption()).toHaveTextContent('noviembre 2021');
23 | });
24 |
--------------------------------------------------------------------------------
/src/contexts/Modifiers/utils/isDateInRange.ts:
--------------------------------------------------------------------------------
1 | import { differenceInCalendarDays, isSameDay } from 'date-fns';
2 |
3 | import { DateRange } from 'types/Matchers';
4 |
5 | /** Return `true` whether `date` is inside `range`. */
6 | export function isDateInRange(date: Date, range: DateRange): boolean {
7 | let { from, to } = range;
8 | if (from && to) {
9 | const isRangeInverted = differenceInCalendarDays(to, from) < 0;
10 | if (isRangeInverted) {
11 | [from, to] = [to, from];
12 | }
13 | const isInRange =
14 | differenceInCalendarDays(date, from) >= 0 &&
15 | differenceInCalendarDays(to, date) >= 0;
16 | return isInRange;
17 | }
18 | if (to) {
19 | return isSameDay(to, date);
20 | }
21 | if (from) {
22 | return isSameDay(from, date);
23 | }
24 | return false;
25 | }
26 |
--------------------------------------------------------------------------------
/website/test-integration/examples/outside-days.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { getDayButton } from '@site/../test/selectors';
4 | import { axe } from '@site/test/axe';
5 | import { freezeBeforeAll } from '@site/test/utils';
6 | import { render } from '@testing-library/react';
7 |
8 | import Example from '@examples/outside-days';
9 |
10 | const today = new Date(2021, 10, 25);
11 | freezeBeforeAll(today);
12 |
13 | let container: HTMLElement;
14 | beforeEach(() => (container = render().container));
15 | test('should not have AXE violations', async () => {
16 | expect(await axe(container)).toHaveNoViolations();
17 | });
18 |
19 | describe('when displaying November 2021', () => {
20 | test('should display the 31st October', () => {
21 | expect(getDayButton(new Date(2021, 9, 31))).toBeInTheDocument();
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/website/examples/testcase-1567.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import { DateRange, DayPicker } from 'react-day-picker';
4 |
5 | /**
6 | * Test case for issue #1567
7 | *
8 | * @see https://github.com/gpbl/react-day-picker/issues/1567
9 | */
10 | export default function App() {
11 | const [selected, setSelected] = useState({
12 | from: new Date(2022, 8, 25),
13 | to: new Date(2022, 9, 1)
14 | });
15 |
16 | return (
17 |
18 |
27 |
28 |
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/website/plugins/source-map.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | /* eslint-env node */
3 | /**
4 | * Docusaurus plugin to add https://webpack.js.org/loaders/source-map-loader so
5 | * that we can have corgi-x source maps in corgi-website for better debugging.
6 | * @type {import('@docusaurus/types').PluginModule}
7 | */
8 | const sourceMapPlugin = () => {
9 | return {
10 | name: 'docusaurus-source-map-plugin',
11 | configureWebpack() {
12 | return {
13 | devtool: 'source-map',
14 | ignoreWarnings: [/Failed to parse source map/],
15 | module: {
16 | rules: [
17 | {
18 | test: /\.(jsx|js|ts|tsx)$/,
19 | enforce: 'pre',
20 | use: ['source-map-loader']
21 | }
22 | ]
23 | }
24 | };
25 | }
26 | };
27 | };
28 | module.exports = sourceMapPlugin;
29 |
--------------------------------------------------------------------------------
/website/examples/modifiers-custom.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DayClickEventHandler, DayPicker } from 'react-day-picker';
4 |
5 | const bookedDays = [new Date(2021, 5, 8), new Date(2021, 5, 9)];
6 | const bookedStyle = { border: '2px solid currentColor' };
7 |
8 | export default function App() {
9 | const [booked, setBooked] = React.useState(false);
10 |
11 | const handleDayClick: DayClickEventHandler = (day, modifiers) => {
12 | setBooked(day && modifiers.booked);
13 | };
14 |
15 | const footer = booked
16 | ? 'This day is already booked!'
17 | : 'Try to pick a booked day.';
18 |
19 | return (
20 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/src/contexts/Navigation/utils/getDisplayMonths.ts:
--------------------------------------------------------------------------------
1 | import { addMonths, differenceInCalendarMonths, startOfMonth } from 'date-fns';
2 |
3 | /**
4 | * Return the months to display in the component according to the number of
5 | * months and the from/to date.
6 | */
7 | export function getDisplayMonths(
8 | month: Date,
9 | {
10 | reverseMonths,
11 | numberOfMonths
12 | }: {
13 | reverseMonths?: boolean;
14 | numberOfMonths: number;
15 | }
16 | ): Date[] {
17 | const start = startOfMonth(month);
18 | const end = startOfMonth(addMonths(start, numberOfMonths));
19 | const monthsDiff = differenceInCalendarMonths(end, start);
20 | let months = [];
21 |
22 | for (let i = 0; i < monthsDiff; i++) {
23 | const nextMonth = addMonths(start, i);
24 | months.push(nextMonth);
25 | }
26 |
27 | if (reverseMonths) months = months.reverse();
28 | return months;
29 | }
30 |
--------------------------------------------------------------------------------
/website/examples/range-min-max.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import { format } from 'date-fns';
4 | import { DateRange, DayPicker } from 'react-day-picker';
5 |
6 | export default function App() {
7 | const [range, setRange] = useState();
8 |
9 | let footer = Please pick the first day.
;
10 | if (range?.from) {
11 | if (!range.to) {
12 | footer = {format(range.from, 'PPP')}
;
13 | } else if (range.to) {
14 | footer = (
15 |
16 | {format(range.from, 'PPP')}–{format(range.to, 'PPP')}
17 |
18 | );
19 | }
20 | }
21 |
22 | return (
23 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/website/test-integration/examples/container-attributes.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { axe } from '@site/test/axe';
4 | import { freezeBeforeAll } from '@site/test/utils';
5 | import { render } from '@testing-library/react';
6 |
7 | import Example from '@examples/container-attributes';
8 |
9 | const today = new Date(2021, 10, 25);
10 | freezeBeforeAll(today);
11 |
12 | let firstChild: HTMLDivElement;
13 | beforeEach(
14 | () =>
15 | (firstChild = render().container.firstChild as HTMLDivElement)
16 | );
17 |
18 | test('should not have AXE violations', async () => {
19 | expect(await axe(firstChild)).toHaveNoViolations();
20 | });
21 |
22 | test('should have the specified id', () => {
23 | expect(firstChild.id).toBe('testId');
24 | });
25 |
26 | test('should have the DataSet attribute', () => {
27 | expect(firstChild).toHaveAttribute('data-test', 'testData');
28 | });
29 |
--------------------------------------------------------------------------------
/src/components/Months/Months.test.tsx:
--------------------------------------------------------------------------------
1 | import { customRender } from 'test/render';
2 |
3 | import { Months } from './Months';
4 |
5 | let root: HTMLElement;
6 |
7 | test('should use the default class name', () => {
8 | const view = customRender(foo, {});
9 | root = view.container.firstChild as HTMLElement;
10 | expect(root).toHaveClass('rdp-months');
11 | });
12 |
13 | test('should use a custom class name', () => {
14 | const view = customRender(foo, {
15 | classNames: { months: 'foo' }
16 | });
17 | root = view.container.firstChild as HTMLElement;
18 | expect(root).toHaveClass('foo');
19 | });
20 |
21 | test('should use a custom style', () => {
22 | const view = customRender(foo, {
23 | styles: { months: { color: 'red' } }
24 | });
25 | root = view.container.firstChild as HTMLElement;
26 | expect(root).toHaveStyle({ color: 'red' });
27 | });
28 |
--------------------------------------------------------------------------------
/src/contexts/Modifiers/utils/matcherToArray.test.ts:
--------------------------------------------------------------------------------
1 | import { matcherToArray } from 'contexts/Modifiers/utils/matcherToArray';
2 | import { Matcher } from 'types/Matchers';
3 |
4 | const matcher: Matcher = jest.fn();
5 |
6 | describe('when a Matcher is passed in', () => {
7 | test('should return an array with the Matcher', () => {
8 | expect(matcherToArray(matcher)).toStrictEqual([matcher]);
9 | });
10 | });
11 |
12 | describe('when an array of Matchers is passed in', () => {
13 | test('should return a copy of the array', () => {
14 | const value = [matcher, matcher];
15 | const result = matcherToArray(value);
16 | expect(result).toStrictEqual(value);
17 | expect(result).not.toBe(value);
18 | });
19 | });
20 |
21 | describe('when undefined is passed in', () => {
22 | test('should return an empty array', () => {
23 | expect(matcherToArray(undefined)).toStrictEqual([]);
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/website/docs/reference.md:
--------------------------------------------------------------------------------
1 | # Summary
2 |
3 | Looking for the props for [DayPicker](/api/functions/DayPicker)? See:
4 |
5 | - [DayPickerBase](/api/interfaces/DayPickerBase) - The base interface for DayPicker props, extended by:
6 | - [DayPickerDefaultProps](/api/interfaces/DayPickerDefaultProps) - when DayPicker is in the default or custom selection mode.
7 | - [DayPickerSingleProps](/api/interfaces/DayPickerSingleProps) - when DayPicker is in single selection mode.
8 | - [DayPickerMultipleProps](/api/interfaces/DayPickerMultipleProps) - when DayPicker is in multiple selection mode.
9 | - [DayPickerRangeProps](/api/interfaces/DayPickerRangeProps) - when DayPicker is in range selection mode.
10 |
11 | ## API Reference
12 |
13 | - [Functions and Components](/api/modules#functions)
14 | - [Interfaces](/api/modules#interfaces)
15 | - [Type Aliases](/api/modules#type-aliases)
16 | - [Variables](/api/modules#variables)
17 |
--------------------------------------------------------------------------------
/src/types/DayPickerSingle.ts:
--------------------------------------------------------------------------------
1 | import { DayPickerProps } from 'DayPicker';
2 |
3 | import { DayPickerContextValue } from 'contexts/DayPicker';
4 |
5 | import { DayPickerBase } from './DayPickerBase';
6 | import { SelectSingleEventHandler } from './EventHandlers';
7 |
8 | /** The props for the {@link DayPicker} component when using `mode="single"`. */
9 | export interface DayPickerSingleProps extends DayPickerBase {
10 | mode: 'single';
11 | /** The selected day. */
12 | selected?: Date | undefined;
13 | /** Event fired when a day is selected. */
14 | onSelect?: SelectSingleEventHandler;
15 | /** Make the selection required. */
16 | required?: boolean;
17 | }
18 |
19 | /** Returns true when the props are of type {@link DayPickerSingleProps}. */
20 | export function isDayPickerSingle(
21 | props: DayPickerProps | DayPickerContextValue
22 | ): props is DayPickerSingleProps {
23 | return props.mode === 'single';
24 | }
25 |
--------------------------------------------------------------------------------
/website/docs/basics/keyboard.md:
--------------------------------------------------------------------------------
1 | # Keyboard Navigation
2 |
3 | When DayPicker is focused, use the following keyboard shortcuts to navigate the calendar:
4 |
5 | - Arrow Top - move to the previous week
6 | - Arrow Right - move to the next day
7 | - Arrow Bottom - move to the next week
8 | - Arrow Left - move to the previous day
9 | - Page Up - move to the previous month
10 | - Page Down - move to the next month
11 | - Shift + Page Up - move to the previous year
12 | - Shift + Page Down - move to the next year
13 | - Home - move to the start of the week
14 | - End - move to the end of the week
15 |
16 | ## Playground
17 |
18 | Press Tab to focus DayPicker and try the keyboard shortcuts above.
19 |
20 | import { RenderExample } from '@site/src/components/RenderExample';
21 |
22 |
23 |
--------------------------------------------------------------------------------
/website/examples/custom-caption.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { format } from 'date-fns';
4 | import { CaptionProps, DayPicker, useNavigation } from 'react-day-picker';
5 |
6 | function CustomCaption(props: CaptionProps) {
7 | const { goToMonth, nextMonth, previousMonth } = useNavigation();
8 | return (
9 |
10 | {format(props.displayMonth, 'MMM yyy')}
11 |
17 |
23 |
24 | );
25 | }
26 |
27 | export default function App() {
28 | return (
29 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/src/hooks/useControlledValue/useControlledValue.ts:
--------------------------------------------------------------------------------
1 | import { Dispatch, SetStateAction, useState } from 'react';
2 |
3 | export type DispatchStateAction = Dispatch>;
4 |
5 | /**
6 | * Helper hook for using controlled/uncontrolled values from a component props.
7 | *
8 | * When the value is not controlled, pass `undefined` as `controlledValue` and
9 | * use the returned setter to update it.
10 | *
11 | * When the value is controlled, pass the controlled value as second
12 | * argument, which will be always returned as `value`.
13 | */
14 | export function useControlledValue(
15 | defaultValue: T,
16 | controlledValue: T | undefined
17 | ): [T, DispatchStateAction] {
18 | const [uncontrolledValue, setValue] = useState(defaultValue);
19 |
20 | const value =
21 | controlledValue === undefined ? uncontrolledValue : controlledValue;
22 |
23 | return [value, setValue] as [T, DispatchStateAction];
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/IconRight/IconRight.tsx:
--------------------------------------------------------------------------------
1 | import { StyledComponent } from 'types/Styles';
2 |
3 | /**
4 | * Render the "next month" button in the navigation.
5 | */
6 | export function IconRight(props: StyledComponent): JSX.Element {
7 | return (
8 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/website/test-integration/examples/numbering-system.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { axe } from '@site/test/axe';
4 | import { freezeBeforeAll } from '@site/test/utils';
5 | import { render, screen } from '@testing-library/react';
6 |
7 | import Example from '@examples/numbering-system';
8 |
9 | const today = new Date(2021, 10, 25);
10 | freezeBeforeAll(today);
11 |
12 | let container: HTMLElement;
13 | beforeEach(() => (container = render().container));
14 |
15 | test('should not have AXE violations', async () => {
16 | expect(await axe(container)).toHaveNoViolations();
17 | });
18 | test('should localize the year', () => {
19 | expect(screen.getByText('نوفمبر ٢٬٠٢١')).toBeInTheDocument();
20 | });
21 | test('should localize the days', () => {
22 | expect(screen.getByText('أحد')).toBeInTheDocument();
23 | });
24 | test('should localize the week numbers', () => {
25 | expect(screen.getByText('٤٥')).toBeInTheDocument();
26 | });
27 |
--------------------------------------------------------------------------------
/website/static/images/sublist-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/components/Day/Day.tsx:
--------------------------------------------------------------------------------
1 | import { useRef } from 'react';
2 |
3 | import { useDayRender } from 'hooks/useDayRender';
4 |
5 | import { Button } from '../Button';
6 |
7 | /** Represent the props used by the {@link Day} component. */
8 | export interface DayProps {
9 | /** The month where the date is displayed. */
10 | displayMonth: Date;
11 | /** The date to render. */
12 | date: Date;
13 | }
14 |
15 | /**
16 | * The content of a day cell – as a button or span element according to its
17 | * modifiers.
18 | */
19 | export function Day(props: DayProps): JSX.Element {
20 | const buttonRef = useRef(null);
21 | const dayRender = useDayRender(props.date, props.displayMonth, buttonRef);
22 |
23 | if (dayRender.isHidden) {
24 | return ;
25 | }
26 | if (!dayRender.isButton) {
27 | return ;
28 | }
29 | return ;
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/IconLeft/IconLeft.tsx:
--------------------------------------------------------------------------------
1 | import { StyledComponent } from 'types/Styles';
2 |
3 | /**
4 | * Render the "previous month" button in the navigation.
5 | */
6 | export function IconLeft(props: StyledComponent): JSX.Element {
7 | return (
8 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/website/examples/numbering-system.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { format } from 'date-fns';
4 | import { arSA } from 'date-fns/locale';
5 | import {
6 | DateFormatter,
7 | DayPicker,
8 | WeekNumberFormatter
9 | } from 'react-day-picker';
10 |
11 | const NU_LOCALE = 'ar-u-nu-arab';
12 |
13 | const formatDay: DateFormatter = (day) =>
14 | day.getDate().toLocaleString(NU_LOCALE);
15 |
16 | const formatWeekNumber: WeekNumberFormatter = (weekNumber) => {
17 | return weekNumber.toLocaleString(NU_LOCALE);
18 | };
19 |
20 | const formatCaption: DateFormatter = (date, options) => {
21 | const y = date.getFullYear().toLocaleString(NU_LOCALE);
22 | const m = format(date, 'LLLL', { locale: options?.locale });
23 | return `${m} ${y}`;
24 | };
25 |
26 | export default function App() {
27 | return (
28 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/website/test-integration/examples/modifiers-custom.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { user } from '@site/test/user';
4 | import { act, render } from '@testing-library/react';
5 |
6 | import { getDayButton, getTableFooter } from 'react-day-picker/test/selectors';
7 |
8 | import Example from '@examples/modifiers-custom';
9 |
10 | const bookedDays = [new Date(2021, 5, 8), new Date(2021, 5, 9)];
11 | const bookedStyle = {
12 | border: '2px solid currentColor'
13 | };
14 | beforeEach(() => {
15 | render();
16 | });
17 |
18 | test.each(bookedDays)('%s should have the booked style', (day) => {
19 | expect(getDayButton(day)).toHaveStyle(bookedStyle);
20 | });
21 |
22 | describe('when the booked day is clicked', () => {
23 | beforeEach(async () => {
24 | await act(() => user.click(getDayButton(bookedDays[1])));
25 | });
26 | test('the footer should be updated', () => {
27 | expect(getTableFooter()).toHaveTextContent('This day is already booked!');
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/src/contexts/Navigation/useNavigationState.ts:
--------------------------------------------------------------------------------
1 | import { startOfMonth } from 'date-fns';
2 |
3 | import { useDayPicker } from 'contexts/DayPicker';
4 | import { useControlledValue } from 'hooks/useControlledValue';
5 |
6 | import { getInitialMonth } from './utils/getInitialMonth';
7 |
8 | export type NavigationState = [
9 | /** The month DayPicker is navigating at */
10 | month: Date,
11 | /** Go to the specified month. */
12 | goToMonth: (month: Date) => void
13 | ];
14 |
15 | /** Controls the navigation state. */
16 | export function useNavigationState(): NavigationState {
17 | const context = useDayPicker();
18 | const initialMonth = getInitialMonth(context);
19 | const [month, setMonth] = useControlledValue(initialMonth, context.month);
20 |
21 | const goToMonth = (date: Date) => {
22 | if (context.disableNavigation) return;
23 | const month = startOfMonth(date);
24 | setMonth(month);
25 | context.onMonthChange?.(month);
26 | };
27 |
28 | return [month, goToMonth];
29 | }
30 |
--------------------------------------------------------------------------------
/src/contexts/Navigation/utils/getInitialMonth.ts:
--------------------------------------------------------------------------------
1 | import { addMonths, differenceInCalendarMonths, startOfMonth } from 'date-fns';
2 |
3 | import { DayPickerContextValue } from 'contexts/DayPicker';
4 |
5 | /** Return the initial month according to the given options. */
6 | export function getInitialMonth(context: Partial): Date {
7 | const { month, defaultMonth, today } = context;
8 | let initialMonth = month || defaultMonth || today || new Date();
9 |
10 | const { toDate, fromDate, numberOfMonths = 1 } = context;
11 |
12 | // Fix the initialMonth if is after the to-date
13 | if (toDate && differenceInCalendarMonths(toDate, initialMonth) < 0) {
14 | const offset = -1 * (numberOfMonths - 1);
15 | initialMonth = addMonths(toDate, offset);
16 | }
17 | // Fix the initialMonth if is before the from-date
18 | if (fromDate && differenceInCalendarMonths(initialMonth, fromDate) < 0) {
19 | initialMonth = fromDate;
20 | }
21 | return startOfMonth(initialMonth);
22 | }
23 |
--------------------------------------------------------------------------------
/src/hooks/useActiveModifiers/useActiveModifiers.test.tsx:
--------------------------------------------------------------------------------
1 | import { addMonths } from 'date-fns';
2 |
3 | import { renderDayPickerHook } from 'test/render';
4 |
5 | import { ActiveModifiers } from 'types/Modifiers';
6 |
7 | import { useActiveModifiers } from './useActiveModifiers';
8 |
9 | const date = new Date(2010, 5, 23);
10 |
11 | describe('when in the same month', () => {
12 | const displayMonth = date;
13 | test('should return the active modifiers', () => {
14 | const result = renderDayPickerHook(() =>
15 | useActiveModifiers(date, displayMonth)
16 | );
17 | expect(result).toBeDefined();
18 | });
19 | });
20 |
21 | describe('when not in the same display month', () => {
22 | const displayMonth = addMonths(date, 1);
23 | test('should return the outside modifier', () => {
24 | const result = renderDayPickerHook(() =>
25 | useActiveModifiers(date, displayMonth)
26 | );
27 | expect(result.current.outside).toBe(true);
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/website/src/theme/CodeBlock/sandpack-app/light.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 1em;
3 | font-family: system-ui, -apple-system, 'Segoe UI', Roboto, Ubuntu, Cantarell,
4 | 'Noto Sans', sans-serif, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial,
5 | sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
6 | -webkit-font-smoothing: antialiased;
7 | -moz-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: antialiased;
9 | text-rendering: optimizeLegibility;
10 | -webkit-tap-highlight-color: transparent;
11 | -webkit-touch-callout: none;
12 | background-color: white;
13 | }
14 |
15 | .dialog-sheet {
16 | background: white;
17 | border-radius: 4px;
18 | box-shadow: 0px 1px 10px rgba(0, 0, 0, 0.04), 0px 4px 5px rgba(0, 0, 0, 0.06),
19 | 0px 2px 4px -1px rgba(0, 0, 0, 0.09);
20 | }
21 | input {
22 | font-size: 1em;
23 | padding: 0.4em 0.6em;
24 | }
25 | button {
26 | font-size: 1em;
27 | padding: 0.4em 0.6em;
28 | }
29 |
30 | input + button {
31 | margin-left: 0.5em;
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/CaptionLabel/CaptionLabel.test.tsx:
--------------------------------------------------------------------------------
1 | import { customRender } from 'test/render';
2 | import { getMonthCaption } from 'test/selectors';
3 | import { freezeBeforeAll } from 'test/utils';
4 |
5 | import { CaptionLabel } from './CaptionLabel';
6 |
7 | const today = new Date(1979, 8);
8 | freezeBeforeAll(today);
9 |
10 | test('should render the formatted display month', () => {
11 | customRender();
12 | expect(getMonthCaption()).toHaveTextContent('September 1979');
13 | });
14 |
15 | test('should apply the `caption_label` class name', () => {
16 | customRender(, {
17 | classNames: { caption_label: 'foo' }
18 | });
19 | expect(getMonthCaption()).toHaveClass('foo');
20 | });
21 |
22 | test('should apply the `caption_label` style', () => {
23 | customRender(, {
24 | styles: { caption_label: { color: 'red' } }
25 | });
26 | expect(getMonthCaption()).toHaveStyle({ color: 'red' });
27 | });
28 |
--------------------------------------------------------------------------------
/website/examples/range.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import { addDays, format } from 'date-fns';
4 | import { DateRange, DayPicker } from 'react-day-picker';
5 |
6 | const pastMonth = new Date(2020, 10, 15);
7 |
8 | export default function App() {
9 | const defaultSelected: DateRange = {
10 | from: pastMonth,
11 | to: addDays(pastMonth, 4)
12 | };
13 | const [range, setRange] = useState(defaultSelected);
14 |
15 | let footer = Please pick the first day.
;
16 | if (range?.from) {
17 | if (!range.to) {
18 | footer = {format(range.from, 'PPP')}
;
19 | } else if (range.to) {
20 | footer = (
21 |
22 | {format(range.from, 'PPP')}–{format(range.to, 'PPP')}
23 |
24 | );
25 | }
26 | }
27 |
28 | return (
29 |
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/utils/parseFromToProps.ts:
--------------------------------------------------------------------------------
1 | import { endOfMonth, startOfDay, startOfMonth } from 'date-fns';
2 |
3 | import { DayPickerBase } from 'types/DayPickerBase';
4 |
5 | /** Return the `fromDate` and `toDate` prop values values parsing the DayPicker props. */
6 | export function parseFromToProps(
7 | props: Pick<
8 | DayPickerBase,
9 | 'fromYear' | 'toYear' | 'fromDate' | 'toDate' | 'fromMonth' | 'toMonth'
10 | >
11 | ): { fromDate: Date | undefined; toDate: Date | undefined } {
12 | const { fromYear, toYear, fromMonth, toMonth } = props;
13 | let { fromDate, toDate } = props;
14 |
15 | if (fromMonth) {
16 | fromDate = startOfMonth(fromMonth);
17 | } else if (fromYear) {
18 | fromDate = new Date(fromYear, 0, 1);
19 | }
20 | if (toMonth) {
21 | toDate = endOfMonth(toMonth);
22 | } else if (toYear) {
23 | toDate = new Date(toYear, 11, 31);
24 | }
25 |
26 | return {
27 | fromDate: fromDate ? startOfDay(fromDate) : undefined,
28 | toDate: toDate ? startOfDay(toDate) : undefined
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/Button/Button.tsx:
--------------------------------------------------------------------------------
1 | import { forwardRef } from 'react';
2 |
3 | import { useDayPicker } from 'contexts/DayPicker';
4 |
5 | /** The props for the {@link Button} component. */
6 | export type ButtonProps = JSX.IntrinsicElements['button'];
7 |
8 | /** Render a button HTML element applying the reset class name. */
9 | export const Button = forwardRef(
10 | (props, ref) => {
11 | const { classNames, styles } = useDayPicker();
12 |
13 | const classNamesArr = [classNames.button_reset, classNames.button];
14 | if (props.className) {
15 | classNamesArr.push(props.className);
16 | }
17 | const className = classNamesArr.join(' ');
18 |
19 | const style = { ...styles.button_reset, ...styles.button };
20 | if (props.style) {
21 | Object.assign(style, props.style);
22 | }
23 |
24 | return (
25 |
32 | );
33 | }
34 | );
35 |
--------------------------------------------------------------------------------
/src/types/DayPickerMultiple.ts:
--------------------------------------------------------------------------------
1 | import { DayPickerProps } from 'DayPicker';
2 |
3 | import { DayPickerContextValue } from 'contexts/DayPicker';
4 |
5 | import { DayPickerBase } from './DayPickerBase';
6 | import { SelectMultipleEventHandler } from './EventHandlers';
7 |
8 | /** The props for the {@link DayPicker} component when using `mode="multiple"`. */
9 | export interface DayPickerMultipleProps extends DayPickerBase {
10 | mode: 'multiple';
11 | /** The selected days. */
12 | selected?: Date[] | undefined;
13 | /** Event fired when a days added or removed to the selection. */
14 | onSelect?: SelectMultipleEventHandler;
15 | /** The minimum amount of days that can be selected. */
16 | min?: number;
17 | /** The maximum amount of days that can be selected. */
18 | max?: number;
19 | }
20 |
21 | /** Returns true when the props are of type {@link DayPickerMultipleProps}. */
22 | export function isDayPickerMultiple(
23 | props: DayPickerProps | DayPickerContextValue
24 | ): props is DayPickerMultipleProps {
25 | return props.mode === 'multiple';
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/IconDropdown/IconDropdown.tsx:
--------------------------------------------------------------------------------
1 | import { StyledComponent } from 'types/Styles';
2 |
3 | /**
4 | * Render the icon in the styled drop-down.
5 | */
6 | export function IconDropdown(props: StyledComponent): JSX.Element {
7 | return (
8 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/src/types/DayPickerRange.ts:
--------------------------------------------------------------------------------
1 | import { DayPickerProps } from 'DayPicker';
2 |
3 | import { DayPickerContextValue } from 'contexts/DayPicker';
4 |
5 | import { DayPickerBase } from './DayPickerBase';
6 | import { SelectRangeEventHandler } from './EventHandlers';
7 | import { DateRange } from './Matchers';
8 |
9 | /** The props for the {@link DayPicker} component when using `mode="range"`. */
10 | export interface DayPickerRangeProps extends DayPickerBase {
11 | mode: 'range';
12 | /** The selected range of days. */
13 | selected?: DateRange | undefined;
14 | /** Event fired when a range (or a part of the range) is selected. */
15 | onSelect?: SelectRangeEventHandler;
16 | /** The minimum amount of days that can be selected. */
17 | min?: number;
18 | /** The maximum amount of days that can be selected. */
19 | max?: number;
20 | }
21 |
22 | /** Returns true when the props are of type {@link DayPickerRangeProps}. */
23 | export function isDayPickerRange(
24 | props: DayPickerProps | DayPickerContextValue
25 | ): props is DayPickerRangeProps {
26 | return props.mode === 'range';
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/DayContent/DayContent.test.tsx:
--------------------------------------------------------------------------------
1 | import { es } from 'date-fns/locale';
2 | import { DayPickerProps } from 'DayPicker';
3 |
4 | import { customRender } from 'test/render';
5 | import { freezeBeforeAll } from 'test/utils';
6 |
7 | import { DayContent, DayContentProps } from 'components/DayContent';
8 |
9 | const today = new Date(2021, 8);
10 |
11 | freezeBeforeAll(today);
12 | let container: HTMLElement;
13 | function setup(props: DayContentProps, dayPickerProps?: DayPickerProps) {
14 | const view = customRender(, dayPickerProps);
15 | container = view.container;
16 | }
17 |
18 | const date = today;
19 | const displayMonth = today;
20 | const props: DayContentProps = {
21 | date: date,
22 | displayMonth,
23 | activeModifiers: {}
24 | };
25 |
26 | const dayPickerProps: DayPickerProps = {
27 | locale: es
28 | };
29 |
30 | describe('when rendered', () => {
31 | beforeEach(() => {
32 | setup(props, dayPickerProps);
33 | });
34 | test('contains the formatted day', () => {
35 | expect(container.firstChild).toHaveTextContent('1');
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/website/docusaurus.sidebars.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 | // @ts-check
3 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
4 | const sidebars = {
5 | docsSidebar: [
6 | 'index',
7 | 'start',
8 | {
9 | 'DayPicker Basics': [
10 | 'basics/navigation',
11 | 'basics/customization',
12 | 'basics/selecting-days',
13 | 'basics/modifiers',
14 | 'basics/styling',
15 | 'basics/localization',
16 | 'basics/keyboard'
17 | ]
18 | },
19 | {
20 | Guides: [
21 | 'guides/formatters',
22 | 'guides/custom-components',
23 | 'guides/input-fields',
24 | 'guides/upgrading'
25 | ]
26 | }
27 | ],
28 | developmentSidebar: [
29 | 'development/index',
30 | 'development/source',
31 | 'development/docs',
32 | 'development/code-of-conduct',
33 | 'changelog',
34 | 'license'
35 | ],
36 | apiSidebar: [
37 | 'reference',
38 | {
39 | type: 'autogenerated',
40 | dirName: 'api'
41 | }
42 | ]
43 | };
44 |
45 | module.exports = sidebars;
46 |
--------------------------------------------------------------------------------
/website/examples/formatters.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { format } from 'date-fns';
4 | import { DateFormatter, DayPicker } from 'react-day-picker';
5 |
6 | const seasonEmoji: Record = {
7 | winter: '⛄️',
8 | spring: '🌸',
9 | summer: '🌻',
10 | autumn: '🍂'
11 | };
12 |
13 | const getSeason = (month: Date): string => {
14 | const monthNumber = month.getMonth();
15 | if (monthNumber >= 0 && monthNumber < 3) return 'winter';
16 | if (monthNumber >= 3 && monthNumber < 6) return 'spring';
17 | if (monthNumber >= 6 && monthNumber < 9) return 'summer';
18 | else return 'autumn';
19 | };
20 |
21 | const formatCaption: DateFormatter = (month, options) => {
22 | const season = getSeason(month);
23 | return (
24 | <>
25 |
26 | {seasonEmoji[season]}
27 | {' '}
28 | {format(month, 'LLLL', { locale: options?.locale })}
29 | >
30 | );
31 | };
32 |
33 | export default function App() {
34 | return (
35 |
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to DayPicker
2 |
3 | You are welcome to join the [DayPicker
4 | contributors](https://github.com/gpbl/react-day-picker/graphs/contributors),
5 | help us building the best date picker for React.
6 |
7 | There are many ways to help the development of DayPicker:
8 |
9 | - Improve our build tools and Github actions
10 | - Opening PRs with new features, bug fixes and improved rendering performance
11 | - Align the source code to the latest Typescript / React practices
12 | - Helping with unit and integration tests
13 | - Proofreading [our website](https://react-day-picker.js.org/) and the
14 | code documentation in the source files
15 | - Making the website more stylish
16 | - Help maintaining [the repository](https://github.com/gpbl/react-day-picker) on Github and triaging issues and PRs
17 | - Answering our [support questions](https://github.com/gpbl/react-day-picker/discussions/categories/support)
18 | - [Sponsoring the project](https://github.com/sponsors/gpbl)
19 |
20 | [Send a
21 | message](https://github.com/gpbl/react-day-picker/discussions)
22 | in our discussions page to present yourself!
23 |
--------------------------------------------------------------------------------
/src/contexts/Modifiers/utils/getActiveModifiers.ts:
--------------------------------------------------------------------------------
1 | import { isSameMonth } from 'date-fns';
2 |
3 | import { ActiveModifiers, Modifiers } from 'types/Modifiers';
4 |
5 | import { isMatch } from './isMatch';
6 |
7 | /** Return the active modifiers for the given day. */
8 | export function getActiveModifiers(
9 | day: Date,
10 | /** The modifiers to match for the given date. */
11 | modifiers: Modifiers,
12 | /** The month where the day is displayed, to add the "outside" modifiers. */
13 | displayMonth?: Date
14 | ): ActiveModifiers {
15 | const matchedModifiers = Object.keys(modifiers).reduce(
16 | (result: string[], key: string): string[] => {
17 | const modifier = modifiers[key];
18 | if (isMatch(day, modifier)) {
19 | result.push(key);
20 | }
21 | return result;
22 | },
23 | []
24 | );
25 | const activeModifiers: ActiveModifiers = {};
26 | matchedModifiers.forEach((modifier) => (activeModifiers[modifier] = true));
27 |
28 | if (displayMonth && !isSameMonth(day, displayMonth)) {
29 | activeModifiers.outside = true;
30 | }
31 |
32 | return activeModifiers;
33 | }
34 |
--------------------------------------------------------------------------------
/website/examples/styling-modifiers.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import { DayPicker } from 'react-day-picker';
4 |
5 | const css = `
6 | .my-selected:not([disabled]) {
7 | font-weight: bold;
8 | border: 2px solid currentColor;
9 | }
10 | .my-selected:hover:not([disabled]) {
11 | border-color: blue;
12 | color: blue;
13 | }
14 | .my-today {
15 | font-weight: bold;
16 | font-size: 140%;
17 | color: red;
18 | }
19 | `;
20 |
21 | export default function App() {
22 | const [selectedDay, setSelectedDay] = useState();
23 | return (
24 | <>
25 |
26 |
40 | >
41 | );
42 | }
43 |
--------------------------------------------------------------------------------
/src/style.css.d.ts:
--------------------------------------------------------------------------------
1 | declare const styles: {
2 | 'rdp': string
3 | 'rdp-vhidden': string
4 | 'rdp-button_reset': string
5 | 'rdp-button': string
6 | 'rdp-day_selected': string
7 | 'rdp-months': string
8 | 'rdp-month': string
9 | 'rdp-table': string
10 | 'rdp-with_weeknumber': string
11 | 'rdp-caption': string
12 | 'rdp-multiple_months': string
13 | 'rdp-caption_dropdowns': string
14 | 'rdp-caption_label': string
15 | 'rdp-nav': string
16 | 'rdp-caption_start': string
17 | 'rdp-caption_end': string
18 | 'rdp-nav_button': string
19 | 'rdp-dropdown_year': string
20 | 'rdp-dropdown_month': string
21 | 'rdp-dropdown': string
22 | 'rdp-dropdown_icon': string
23 | 'rdp-head': string
24 | 'rdp-head_row': string
25 | 'rdp-row': string
26 | 'rdp-head_cell': string
27 | 'rdp-tbody': string
28 | 'rdp-tfoot': string
29 | 'rdp-cell': string
30 | 'rdp-weeknumber': string
31 | 'rdp-day': string
32 | 'rdp-day_today': string
33 | 'rdp-day_outside': string
34 | 'rdp-day_range_start': string
35 | 'rdp-day_range_end': string
36 | 'rdp-day_range_middle': string
37 | }
38 |
39 | export default styles
40 |
--------------------------------------------------------------------------------
/website/src/pages/render.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-var-requires */
2 | import React from 'react';
3 |
4 | import BrowserOnly from '@docusaurus/BrowserOnly';
5 | import { useLocation } from '@docusaurus/router';
6 | import Layout from '@theme/Layout/Provider';
7 |
8 | import { RenderExample } from '../components/RenderExample';
9 |
10 | /**
11 | * Create a page at `/render` URL that renders the examples from the examples
12 | * `directory`.
13 | */
14 | export default function Render(): JSX.Element {
15 | const location = useLocation();
16 | return (
17 |
18 |
19 | {() => {
20 | const name = new URLSearchParams(location.search).get('example');
21 | return (
22 |
31 | );
32 | }}
33 |
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/CaptionLabel/CaptionLabel.tsx:
--------------------------------------------------------------------------------
1 | import { useDayPicker } from 'contexts/DayPicker';
2 |
3 | /** The props for the {@link CaptionLabel} component. */
4 | export interface CaptionLabelProps {
5 | /** The ID for the heading element. Must be the same as the labelled-by in Table. */
6 | id?: string;
7 | /** The month where the caption is displayed. */
8 | displayMonth: Date;
9 | /** The index of the month where the caption is displayed. Older custom components may miss this prop. */
10 | displayIndex?: number | undefined;
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 |
29 | {formatCaption(props.displayMonth, { locale })}
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/src/types/Formatters.ts:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react';
2 |
3 | import { Locale } from 'date-fns';
4 |
5 | /** Represents a function to format a date. */
6 | export type DateFormatter = (
7 | date: Date,
8 | options?: {
9 | locale?: Locale;
10 | }
11 | ) => ReactNode;
12 |
13 | /** Represent a map of formatters used to render localized content. */
14 | export type Formatters = {
15 | /** Format the month in the caption when `captionLayout` is `buttons`. */
16 | formatCaption: DateFormatter;
17 | /** Format the month in the navigation dropdown. */
18 | formatMonthCaption: DateFormatter;
19 | /** Format the year in the navigation dropdown. */
20 | formatYearCaption: DateFormatter;
21 | /** Format the day in the day cell. */
22 | formatDay: DateFormatter;
23 | /** Format the week number. */
24 | formatWeekNumber: WeekNumberFormatter;
25 | /** Format the week day name in the header */
26 | formatWeekdayName: DateFormatter;
27 | };
28 |
29 | /** Represent a function to format the week number. */
30 | export type WeekNumberFormatter = (
31 | weekNumber: number,
32 | options?: {
33 | locale?: Locale;
34 | }
35 | ) => ReactNode;
36 |
--------------------------------------------------------------------------------
/src/components/HeadRow/HeadRow.tsx:
--------------------------------------------------------------------------------
1 | import { useDayPicker } from 'contexts/DayPicker';
2 |
3 | import { getWeekdays } from './utils';
4 |
5 | /**
6 | * Render the HeadRow component - i.e. the table head row with the weekday names.
7 | */
8 | export function HeadRow(): JSX.Element {
9 | const {
10 | classNames,
11 | styles,
12 | showWeekNumber,
13 | locale,
14 | weekStartsOn,
15 | ISOWeek,
16 | formatters: { formatWeekdayName },
17 | labels: { labelWeekday }
18 | } = useDayPicker();
19 |
20 | const weekdays = getWeekdays(locale, weekStartsOn, ISOWeek);
21 |
22 | return (
23 |
24 | {showWeekNumber && (
25 | |
26 | )}
27 | {weekdays.map((weekday, i) => (
28 |
35 | {formatWeekdayName(weekday, { locale })}
36 | |
37 | ))}
38 |
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014-2024 Giampaolo Bellavite and contributors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/website/test-integration/examples/weeknumber.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { axe } from '@site/test/axe';
4 | import { user } from '@site/test/user';
5 | import { freezeBeforeAll } from '@site/test/utils';
6 | import { act, render } from '@testing-library/react';
7 |
8 | import { getTableFooter, getWeekButton } from 'react-day-picker/test/selectors';
9 |
10 | import Example from '@examples/weeknumber';
11 |
12 | const today = new Date(2021, 10, 25);
13 | freezeBeforeAll(today);
14 |
15 | let container: HTMLElement;
16 | beforeEach(() => (container = render().container));
17 |
18 | test('should not have AXE violations', async () => {
19 | expect(await axe(container)).toHaveNoViolations();
20 | });
21 |
22 | describe('when displaying November 2021', () => {
23 | test('should display the 45th week number', () => {
24 | expect(getWeekButton(45)).toBeInTheDocument();
25 | });
26 | describe('when the week button is clicked', () => {
27 | beforeEach(async () => act(() => user.click(getWeekButton(45))));
28 | test('should update the footer', () => {
29 | expect(getTableFooter()).toHaveTextContent('You clicked the week n. 45.');
30 | });
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/website/test-integration/examples/custom-single.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { axe } from '@site/test/axe';
4 | import { user } from '@site/test/user';
5 | import { freezeBeforeAll } from '@site/test/utils';
6 | import { render } from '@testing-library/react';
7 | import { act } from 'react-dom/test-utils';
8 |
9 | import { getDayButton, getTableFooter } from 'react-day-picker/test/selectors';
10 |
11 | import Example from '@examples/custom-single';
12 |
13 | const today = new Date(2021, 10, 25);
14 | freezeBeforeAll(today);
15 |
16 | let container: HTMLElement;
17 | beforeEach(() => {
18 | container = render().container;
19 | });
20 |
21 | test('should not have AXE violations', async () => {
22 | expect(await axe(container)).toHaveNoViolations();
23 | });
24 |
25 | describe('when a day is clicked', () => {
26 | const day = new Date(2021, 10, 1);
27 | beforeEach(() => act(() => user.click(getDayButton(day))));
28 | test('should appear as selected', () => {
29 | expect(getDayButton(day)).toHaveAttribute('aria-selected', 'true');
30 | });
31 | test('should update the footer', () => {
32 | expect(getTableFooter()).toHaveTextContent('You selected Mon Nov 01 2021.');
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/src/contexts/SelectRange/utils/addToRange.ts:
--------------------------------------------------------------------------------
1 | import { isAfter, isBefore, isSameDay } from 'date-fns';
2 |
3 | import { DateRange } from 'types/Matchers';
4 |
5 | /**
6 | * Add a day to an existing range.
7 | *
8 | * The returned range takes in account the `undefined` values and if the added
9 | * day is already present in the range.
10 | */
11 | export function addToRange(
12 | day: Date,
13 | range?: DateRange
14 | ): DateRange | undefined {
15 | const { from, to } = range || {};
16 | if (from && to) {
17 | if (isSameDay(to, day) && isSameDay(from, day)) {
18 | return undefined;
19 | }
20 | if (isSameDay(to, day)) {
21 | return { from: to, to: undefined };
22 | }
23 | if (isSameDay(from, day)) {
24 | return undefined;
25 | }
26 | if (isAfter(from, day)) {
27 | return { from: day, to };
28 | }
29 | return { from, to: day };
30 | }
31 | if (to) {
32 | if (isAfter(day, to)) {
33 | return { from: to, to: day };
34 | }
35 | return { from: day, to };
36 | }
37 | if (from) {
38 | if (isBefore(day, from)) {
39 | return { from: day, to: from };
40 | }
41 | return { from, to: day };
42 | }
43 | return { from: day, to: undefined };
44 | }
45 |
--------------------------------------------------------------------------------
/website/examples/custom-multiple.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import { isSameDay } from 'date-fns';
4 | import { DayClickEventHandler, DayPicker } from 'react-day-picker';
5 |
6 | export default function App() {
7 | const [selectedDays, setSelectedDays] = useState([]);
8 |
9 | const handleDayClick: DayClickEventHandler = (day, modifiers) => {
10 | const newSelectedDays = [...selectedDays];
11 | if (modifiers.selected) {
12 | const index = selectedDays.findIndex((selectedDay) =>
13 | isSameDay(day, selectedDay)
14 | );
15 | newSelectedDays.splice(index, 1);
16 | } else {
17 | newSelectedDays.push(day);
18 | }
19 | setSelectedDays(newSelectedDays);
20 | };
21 |
22 | const handleResetClick = () => setSelectedDays([]);
23 |
24 | let footer = Please pick one or more days.
;
25 |
26 | if (selectedDays.length > 0)
27 | footer = (
28 |
29 | You selected {selectedDays.length} days.{' '}
30 |
31 |
32 | );
33 |
34 | return (
35 |
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/website/test-integration/examples/controlled.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { axe } from '@site/test/axe';
4 | import { user } from '@site/test/user';
5 | import { freezeBeforeAll } from '@site/test/utils';
6 | import { render, screen } from '@testing-library/react';
7 | import { act } from 'react-dom/test-utils';
8 |
9 | import { getMonthCaption } from 'react-day-picker/test/selectors';
10 |
11 | import Example from '@examples/controlled';
12 |
13 | const today = new Date(2022, 5, 10);
14 |
15 | function getTodayButton() {
16 | return screen.getByRole('button', { name: 'Go to Today' });
17 | }
18 |
19 | freezeBeforeAll(today);
20 | test('should not have AXE violations', async () => {
21 | const html = render().container;
22 | expect(await axe(html)).toHaveNoViolations();
23 | });
24 |
25 | describe('when the "Go to today" button is clicked', () => {
26 | beforeEach(async () => {
27 | render();
28 | await act(() => user.click(getTodayButton()));
29 | });
30 | test('the button should be disabled', async () => {
31 | expect(getTodayButton()).toBeDisabled();
32 | });
33 | test('should display the current month', () => {
34 | expect(getMonthCaption()).toHaveTextContent('June 2022');
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/website/docusaurus.navbar.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 | // @ts-check
3 | // Note: type annotations allow type checking and IDEs autocompletion
4 | /** @type {import('@docusaurus/preset-classic').ThemeConfig["navbar"]} */
5 | const navbar = {
6 | title: 'React DayPicker',
7 | logo: {
8 | alt: 'DayPicker Logo',
9 | src: 'images/logo.png'
10 | },
11 | hideOnScroll: true,
12 | items: [
13 | {
14 | to: 'start',
15 | label: 'Documentation'
16 | },
17 | {
18 | to: 'reference',
19 | label: 'API Reference'
20 | },
21 | {
22 | to: 'development',
23 | label: 'Development'
24 | },
25 | {
26 | type: 'docsVersionDropdown',
27 | position: 'right',
28 | dropdownActiveClassDisabled: true,
29 | dropdownItemsBefore: [{ to: 'changelog', label: 'Changelog' }],
30 | dropdownItemsAfter: [
31 | {
32 | href: 'https://react-day-picker-v7.netlify.app',
33 | label: 'v7.4.10 (older version)'
34 | }
35 | ]
36 | },
37 | {
38 | href: 'https://github.com/gpbl/react-day-picker',
39 | position: 'right',
40 | className: 'header-github-link',
41 | 'aria-label': 'GitHub repository'
42 | }
43 | ]
44 | };
45 |
46 | module.exports = navbar;
47 |
--------------------------------------------------------------------------------
/website/test-integration/examples/start.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { axe } from '@site/test/axe';
4 | import { user } from '@site/test/user';
5 | import { freezeBeforeAll } from '@site/test/utils';
6 | import { act, render } from '@testing-library/react';
7 |
8 | import { getDayButton, getTableFooter } from 'react-day-picker/test/selectors';
9 |
10 | import Example from '@examples/start';
11 |
12 | const today = new Date(2021, 10, 25);
13 | freezeBeforeAll(today);
14 |
15 | let container: HTMLElement;
16 | beforeEach(() => (container = render().container));
17 |
18 | test('should not have AXE violations', async () => {
19 | expect(await axe(container)).toHaveNoViolations();
20 | });
21 | describe('when a day is clicked', () => {
22 | const day = new Date(2021, 10, 1);
23 | beforeEach(async () => act(() => user.click(getDayButton(day))));
24 | test('should appear as selected', () => {
25 | expect(getDayButton(day)).toHaveAttribute('aria-selected', 'true');
26 | });
27 | test('should update the footer', () => {
28 | expect(getTableFooter()).toHaveTextContent(`You picked Nov 1, 2021.`);
29 | });
30 | test('should not have AXE violations', async () => {
31 | expect(await axe(container)).toHaveNoViolations();
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/website/test-integration/examples/focus-recursive.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { axe } from '@site/test/axe';
4 | import { user } from '@site/test/user';
5 | import { freezeBeforeAll } from '@site/test/utils';
6 | import { act, render } from '@testing-library/react';
7 |
8 | import {
9 | getDayButton,
10 | getFocusedElement
11 | } from 'react-day-picker/test/selectors';
12 |
13 | import Example from '@examples/focus-recursive';
14 |
15 | const today = new Date(2022, 5, 10);
16 | freezeBeforeAll(today);
17 |
18 | let container: HTMLElement;
19 |
20 | beforeEach(async () => {
21 | container = render().container;
22 | await act(() => user.tab());
23 | await act(() => user.tab());
24 | await act(() => user.tab());
25 | await act(() => user.type(getFocusedElement(), '{arrowdown}'));
26 | await act(() => user.type(getFocusedElement(), '{arrowdown}'));
27 | await act(() => user.type(getFocusedElement(), '{arrowdown}'));
28 | await act(() => user.type(getFocusedElement(), '{arrowdown}'));
29 | });
30 |
31 | test('the first selected day should have focus', () => {
32 | expect(getDayButton(new Date(2022, 5, 22))).toHaveFocus();
33 | });
34 |
35 | test('should not have AXE violations', async () => {
36 | expect(await axe(container)).toHaveNoViolations();
37 | });
38 |
--------------------------------------------------------------------------------
/src/hooks/useDayRender/utils/getDayClassNames.ts:
--------------------------------------------------------------------------------
1 | import { DayPickerContextValue } from 'contexts/DayPicker';
2 | import { ActiveModifiers, InternalModifier } from 'types/Modifiers';
3 |
4 | function isInternalModifier(modifier: string): modifier is InternalModifier {
5 | return Object.values(InternalModifier).includes(modifier as InternalModifier);
6 | }
7 |
8 | /**
9 | * Return the class names for the Day element, according to the given active
10 | * modifiers.
11 | *
12 | * Custom class names are set via `modifiersClassNames` or `classNames`,
13 | * where the first have the precedence.
14 | */
15 | export function getDayClassNames(
16 | dayPicker: Pick,
17 | activeModifiers: ActiveModifiers
18 | ) {
19 | const classNames: string[] = [dayPicker.classNames.day];
20 | Object.keys(activeModifiers).forEach((modifier) => {
21 | const customClassName = dayPicker.modifiersClassNames[modifier];
22 | if (customClassName) {
23 | classNames.push(customClassName);
24 | } else if (isInternalModifier(modifier)) {
25 | const internalClassName = dayPicker.classNames[`day_${modifier}`];
26 | if (internalClassName) {
27 | classNames.push(internalClassName);
28 | }
29 | }
30 | });
31 | return classNames;
32 | }
33 |
--------------------------------------------------------------------------------
/website/docs/license.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: License
3 | hide_title: true
4 | pagination_next: null
5 | pagination_prev: null
6 | ---
7 |
8 | The MIT License (MIT)
9 |
10 | Copyright (c) 2014-2021 gpbl and contributors
11 |
12 | Permission is hereby granted, free of charge, to any person obtaining a copy
13 | of this software and associated documentation files (the "Software"), to deal
14 | in the Software without restriction, including without limitation the rights
15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | copies of the Software, and to permit persons to whom the Software is
17 | furnished to do so, subject to the following conditions:
18 |
19 | The above copyright notice and this permission notice shall be included in all
20 | copies or substantial portions of the Software.
21 |
22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 | SOFTWARE.
29 |
--------------------------------------------------------------------------------
/website/test-integration/examples/dropdown.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { axe } from '@site/test/axe';
4 | import { user } from '@site/test/user';
5 | import { freezeBeforeAll } from '@site/test/utils';
6 | import { act, render } from '@testing-library/react';
7 |
8 | import {
9 | getMonthDropdown,
10 | getMonthGrid,
11 | getYearDropdown
12 | } from 'react-day-picker/test/selectors';
13 |
14 | import Example from '@examples/dropdown';
15 |
16 | const today = new Date(2022, 5, 10);
17 | freezeBeforeAll(today);
18 |
19 | let container: HTMLElement;
20 | beforeEach(() => (container = render().container));
21 |
22 | test('should not have AXE violations', async () => {
23 | expect(await axe(container)).toHaveNoViolations();
24 | });
25 |
26 | test('should display the year dropdown', () => {
27 | expect(getYearDropdown()).toBeInTheDocument();
28 | });
29 | test('should display the month dropdown', () => {
30 | expect(getMonthDropdown()).toBeInTheDocument();
31 | });
32 |
33 | describe('when choosing a month', () => {
34 | const monthName = 'January';
35 | beforeEach(() =>
36 | act(() => user.selectOptions(getMonthDropdown(), monthName))
37 | );
38 | test('should display the month', () => {
39 | expect(getMonthGrid()).toHaveAccessibleName(`${monthName} 2022`);
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/src/hooks/useSelectedDays/useSelectedDays.ts:
--------------------------------------------------------------------------------
1 | import { useDayPicker } from 'contexts/DayPicker';
2 | import { useSelectMultiple } from 'contexts/SelectMultiple';
3 | import { useSelectRange } from 'contexts/SelectRange';
4 | import { useSelectSingle } from 'contexts/SelectSingle';
5 | import { isDayPickerMultiple } from 'types/DayPickerMultiple';
6 | import { isDayPickerRange } from 'types/DayPickerRange';
7 | import { isDayPickerSingle } from 'types/DayPickerSingle';
8 | import { DateRange } from 'types/Matchers';
9 |
10 | export type SelectedDays = Date | Date[] | DateRange | undefined;
11 |
12 | /**
13 | * Return the current selected days when DayPicker is in selection mode. Days
14 | * selected by the custom selection mode are not returned.
15 | *
16 | * This hook is meant to be used inside internal or custom components.
17 | *
18 | */
19 | export function useSelectedDays(): SelectedDays {
20 | const dayPicker = useDayPicker();
21 | const single = useSelectSingle();
22 | const multiple = useSelectMultiple();
23 | const range = useSelectRange();
24 |
25 | const selectedDays = isDayPickerSingle(dayPicker)
26 | ? single.selected
27 | : isDayPickerMultiple(dayPicker)
28 | ? multiple.selected
29 | : isDayPickerRange(dayPicker)
30 | ? range.selected
31 | : undefined;
32 |
33 | return selectedDays;
34 | }
35 |
--------------------------------------------------------------------------------
/src/components/HeadRow/utils/getWeekdays.test.ts:
--------------------------------------------------------------------------------
1 | import { es } from 'date-fns/locale';
2 |
3 | import { freezeBeforeAll } from 'test/utils';
4 |
5 | import { getWeekdays } from './getWeekdays';
6 |
7 | const today = new Date(2022, 1, 12);
8 | const prevSunday = new Date(2022, 1, 6);
9 | const prevMonday = new Date(2022, 1, 7);
10 |
11 | freezeBeforeAll(today);
12 |
13 | let result: Date[];
14 |
15 | describe('when rendered without a locale', () => {
16 | beforeEach(() => {
17 | result = getWeekdays();
18 | });
19 | test('should return 7 days', () => {
20 | expect(result).toHaveLength(7);
21 | });
22 | test('should return Sunday as first day', () => {
23 | expect(result[0]).toEqual(prevSunday);
24 | });
25 | });
26 |
27 | describe.each<0 | 1 | 2 | 3 | 4 | 5 | 6>([0, 1, 2, 3, 4, 5, 6])(
28 | 'when week start on %s',
29 | (weekStartsOn) => {
30 | beforeEach(() => {
31 | result = getWeekdays(es, weekStartsOn);
32 | });
33 | test('the first date should be weekStartsOn', () => {
34 | expect(result[0].getDay()).toBe(weekStartsOn);
35 | });
36 | }
37 | );
38 |
39 | describe('when using ISO week', () => {
40 | beforeEach(() => {
41 | result = getWeekdays(es, 3, true);
42 | });
43 | test('should return Monday as first day', () => {
44 | expect(result[0]).toEqual(prevMonday);
45 | });
46 | });
47 |
--------------------------------------------------------------------------------
/src/contexts/Navigation/utils/getNextMonth.ts:
--------------------------------------------------------------------------------
1 | import { addMonths, differenceInCalendarMonths, startOfMonth } from 'date-fns';
2 |
3 | /**
4 | * Returns the next month the user can navigate to according to the given
5 | * options.
6 | *
7 | * Please note that the next month is not always the next calendar month:
8 | *
9 | * - if after the `toDate` range, is undefined;
10 | * - if the navigation is paged, is the number of months displayed ahead.
11 | *
12 | */
13 | export function getNextMonth(
14 | startingMonth: Date,
15 | options: {
16 | numberOfMonths?: number;
17 | fromDate?: Date;
18 | toDate?: Date;
19 | pagedNavigation?: boolean;
20 | today?: Date;
21 | disableNavigation?: boolean;
22 | }
23 | ): Date | undefined {
24 | if (options.disableNavigation) {
25 | return undefined;
26 | }
27 | const { toDate, pagedNavigation, numberOfMonths = 1 } = options;
28 | const offset = pagedNavigation ? numberOfMonths : 1;
29 | const month = startOfMonth(startingMonth);
30 |
31 | if (!toDate) {
32 | return addMonths(month, offset);
33 | }
34 |
35 | const monthsDiff = differenceInCalendarMonths(toDate, startingMonth);
36 |
37 | if (monthsDiff < numberOfMonths) {
38 | return undefined;
39 | }
40 |
41 | // Jump forward as the number of months when paged navigation
42 | return addMonths(month, offset);
43 | }
44 |
--------------------------------------------------------------------------------
/src/contexts/Navigation/utils/getPreviousMonth.ts:
--------------------------------------------------------------------------------
1 | import { addMonths, differenceInCalendarMonths, startOfMonth } from 'date-fns';
2 |
3 | /**
4 | * Returns the next previous the user can navigate to, according to the given
5 | * options.
6 | *
7 | * Please note that the previous month is not always the previous calendar
8 | * month:
9 | *
10 | * - if before the `fromDate` date, is `undefined`;
11 | * - if the navigation is paged, is the number of months displayed before.
12 | *
13 | */
14 | export function getPreviousMonth(
15 | startingMonth: Date,
16 | options: {
17 | numberOfMonths?: number;
18 | fromDate?: Date;
19 | toDate?: Date;
20 | pagedNavigation?: boolean;
21 | today?: Date;
22 | disableNavigation?: boolean;
23 | }
24 | ): Date | undefined {
25 | if (options.disableNavigation) {
26 | return undefined;
27 | }
28 | const { fromDate, pagedNavigation, numberOfMonths = 1 } = options;
29 | const offset = pagedNavigation ? numberOfMonths : 1;
30 | const month = startOfMonth(startingMonth);
31 | if (!fromDate) {
32 | return addMonths(month, -offset);
33 | }
34 | const monthsDiff = differenceInCalendarMonths(month, fromDate);
35 |
36 | if (monthsDiff <= 0) {
37 | return undefined;
38 | }
39 |
40 | // Jump back as the number of months when paged navigation
41 | return addMonths(month, -offset);
42 | }
43 |
--------------------------------------------------------------------------------
/test/mockedContexts.ts:
--------------------------------------------------------------------------------
1 | import {
2 | FocusContextValue,
3 | SelectMultipleContextValue,
4 | SelectRangeContextValue,
5 | SelectSingleContextValue
6 | } from 'index';
7 |
8 | const singleContext: SelectSingleContextValue = {
9 | selected: new Date(),
10 | onDayClick: jest.fn()
11 | };
12 |
13 | const multipleContext: SelectMultipleContextValue = {
14 | selected: [new Date()],
15 | modifiers: { disabled: [] },
16 | onDayClick: jest.fn()
17 | };
18 |
19 | const rangeContext: SelectRangeContextValue = {
20 | selected: undefined,
21 | modifiers: {
22 | disabled: [],
23 | range_start: [],
24 | range_end: [],
25 | range_middle: []
26 | },
27 | onDayClick: jest.fn()
28 | };
29 |
30 | const focusContext: FocusContextValue = {
31 | focus: jest.fn(),
32 | focusedDay: undefined,
33 | focusTarget: undefined,
34 | blur: jest.fn(),
35 | focusDayAfter: jest.fn(),
36 | focusDayBefore: jest.fn(),
37 | focusWeekBefore: jest.fn(),
38 | focusWeekAfter: jest.fn(),
39 | focusMonthBefore: jest.fn(),
40 | focusMonthAfter: jest.fn(),
41 | focusYearBefore: jest.fn(),
42 | focusYearAfter: jest.fn(),
43 | focusStartOfWeek: jest.fn(),
44 | focusEndOfWeek: jest.fn()
45 | };
46 |
47 | export const mockedContexts = {
48 | single: singleContext,
49 | multiple: multipleContext,
50 | range: rangeContext,
51 | focus: focusContext
52 | };
53 |
--------------------------------------------------------------------------------
/src/types/Labels.ts:
--------------------------------------------------------------------------------
1 | import { Locale } from 'date-fns';
2 |
3 | import { ActiveModifiers } from 'types/Modifiers';
4 |
5 | /** Map of functions to translate ARIA labels for the relative elements. */
6 | export type Labels = {
7 | labelMonthDropdown: () => string;
8 | labelYearDropdown: () => string;
9 | labelNext: NavButtonLabel;
10 | labelPrevious: NavButtonLabel;
11 | /** @deprecated This label is not used anymore and this function will be removed in the future. */
12 | labelDay: DayLabel;
13 | labelWeekday: WeekdayLabel;
14 | labelWeekNumber: WeekNumberLabel;
15 | };
16 |
17 | /** Return the ARIA label for the {@link Day} component. */
18 | export type DayLabel = (
19 | day: Date,
20 | activeModifiers: ActiveModifiers,
21 | options?: {
22 | locale?: Locale;
23 | }
24 | ) => string;
25 |
26 | /** Return the ARIA label for the "next month" / "prev month" buttons in the navigation.*/
27 | export type NavButtonLabel = (
28 | month?: Date,
29 | options?: {
30 | locale?: Locale;
31 | }
32 | ) => string;
33 |
34 | /** Return the ARIA label for the Head component.*/
35 | export type WeekdayLabel = (
36 | day: Date,
37 | options?: {
38 | locale?: Locale;
39 | }
40 | ) => string;
41 |
42 | /** Return the ARIA label of the week number.*/
43 | export type WeekNumberLabel = (
44 | n: number,
45 | options?: {
46 | locale?: Locale;
47 | }
48 | ) => string;
49 |
--------------------------------------------------------------------------------
/src/contexts/RootProvider.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react';
2 |
3 | import { ModifiersProvider } from 'contexts/Modifiers/ModifiersContext';
4 |
5 | import { DayPickerProvider } from './DayPicker';
6 | import { FocusProvider } from './Focus';
7 | import { NavigationProvider } from './Navigation';
8 | import { SelectMultipleProvider } from './SelectMultiple';
9 | import { SelectRangeProvider } from './SelectRange';
10 | import { SelectSingleProvider } from './SelectSingle';
11 |
12 | /** The props of {@link RootProvider}. */
13 | export interface RootContext {
14 | children?: ReactNode;
15 | }
16 |
17 | /** Provide the value for all the context providers. */
18 | export function RootProvider(props: RootContext): JSX.Element {
19 | const { children, ...initialProps } = props;
20 |
21 | return (
22 |
23 |
24 |
25 |
26 |
27 |
28 | {children}
29 |
30 |
31 |
32 |
33 |
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/src/contexts/Focus/utils/getInitialFocusTarget.test.ts:
--------------------------------------------------------------------------------
1 | import { Modifiers } from 'types/Modifiers';
2 |
3 | import { getInitialFocusTarget } from './getInitialFocusTarget';
4 |
5 | describe('when no days are selected is selected', () => {
6 | test('should return the first day of month', () => {
7 | const displayMonth = new Date(2022, 7);
8 | const modifiers: Modifiers = {
9 | outside: [],
10 | disabled: [],
11 | selected: [],
12 | hidden: [],
13 | today: [],
14 | range_start: [],
15 | range_end: [],
16 | range_middle: []
17 | };
18 | const initialFocusTarget = getInitialFocusTarget([displayMonth], modifiers);
19 | expect(initialFocusTarget).toStrictEqual(displayMonth);
20 | });
21 | });
22 |
23 | describe('when a day is selected', () => {
24 | test('should return the selected day', () => {
25 | const displayMonths = [new Date(2022, 7)];
26 | const selectedDay1 = new Date(2022, 7, 17);
27 | const selectedDay2 = new Date(2022, 7, 19);
28 | const modifiers: Modifiers = {
29 | outside: [],
30 | disabled: [],
31 | selected: [selectedDay1, selectedDay2],
32 | hidden: [],
33 | today: [],
34 | range_start: [],
35 | range_end: [],
36 | range_middle: []
37 | };
38 | const initialFocusTarget = getInitialFocusTarget(displayMonths, modifiers);
39 | expect(initialFocusTarget).toStrictEqual(selectedDay1);
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/src/contexts/Modifiers/ModifiersContext.test.ts:
--------------------------------------------------------------------------------
1 | import { DayPickerProps } from 'DayPicker';
2 |
3 | import { renderDayPickerHook } from 'test/render';
4 |
5 | import { useModifiers } from 'contexts/Modifiers';
6 | import { DayModifiers, InternalModifier, Modifiers } from 'types/Modifiers';
7 |
8 | const internalModifiers = Object.values(InternalModifier);
9 |
10 | function renderHook(dayPickerProps: Partial = {}) {
11 | return renderDayPickerHook(useModifiers, dayPickerProps);
12 | }
13 |
14 | describe('when rendered with custom modifiers', () => {
15 | const modifier = new Date(2018, 11, 12);
16 | const dayModifiers: DayModifiers = {
17 | foo: modifier,
18 | today: modifier,
19 | outside: modifier,
20 | disabled: modifier,
21 | selected: modifier,
22 | hidden: modifier,
23 | range_start: modifier,
24 | range_end: modifier,
25 | range_middle: modifier
26 | };
27 | test('should return the custom modifiers', () => {
28 | const result = renderHook({ modifiers: dayModifiers });
29 | expect(result.current.foo).toEqual([dayModifiers.foo]);
30 | });
31 | test.each(internalModifiers)(
32 | 'should override the %s internal modifier',
33 | (internalModifier) => {
34 | const result = renderHook({ modifiers: dayModifiers });
35 | expect(result.current[internalModifier]).toEqual([
36 | dayModifiers[internalModifier]
37 | ]);
38 | }
39 | );
40 | });
41 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 | /** @type {import('eslint').Linter.Config */
3 | const config = {
4 | parser: '@typescript-eslint/parser',
5 | parserOptions: {
6 | ecmaVersion: 2018,
7 | sourceType: 'module'
8 | },
9 | ignorePatterns: ['**/*.css.d.ts', 'dist', 'build'],
10 | plugins: ['@typescript-eslint', 'jest'],
11 | extends: [
12 | 'plugin:jest/recommended',
13 | 'eslint:recommended',
14 | 'plugin:@typescript-eslint/recommended',
15 | 'plugin:react-hooks/recommended',
16 | 'prettier',
17 | 'plugin:prettier/recommended',
18 | 'plugin:import/recommended',
19 | 'plugin:import/typescript'
20 | ],
21 | settings: {
22 | 'import/parsers': {
23 | '@typescript-eslint/parser': ['.ts', '.tsx']
24 | }
25 | },
26 | rules: {
27 | 'import/no-unresolved': 'off',
28 | 'prettier/prettier': 'warn',
29 | 'no-console': 'warn'
30 | },
31 | overrides: [
32 | {
33 | files: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'],
34 | env: {
35 | jest: true
36 | },
37 | extends: ['plugin:jest/recommended', 'plugin:testing-library/react'],
38 | rules: {
39 | 'testing-library/no-render-in-setup': 'off',
40 | 'testing-library/no-node-access': 'off',
41 | 'testing-library/render-result-naming-convention': 'off',
42 | 'testing-library/no-render-in-lifecycle': 'off'
43 | }
44 | }
45 | ]
46 | };
47 |
48 | module.exports = config;
49 |
--------------------------------------------------------------------------------
/website/test-integration/examples/single-required.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { axe } from '@site/test/axe';
4 | import { user } from '@site/test/user';
5 | import { freezeBeforeAll } from '@site/test/utils';
6 | import { act, render } from '@testing-library/react';
7 |
8 | import { getDayButton, getTableFooter } from 'react-day-picker/test/selectors';
9 |
10 | import Example from '@examples/single-required';
11 |
12 | const today = new Date(2021, 10, 25);
13 | freezeBeforeAll(today);
14 | let container: HTMLElement;
15 | beforeEach(() => (container = render().container));
16 |
17 | test('should not have AXE violations', async () => {
18 | expect(await axe(container)).toHaveNoViolations();
19 | });
20 |
21 | describe('when a day is clicked', () => {
22 | const day = new Date(2021, 10, 1);
23 | beforeEach(async () => act(() => user.click(getDayButton(day))));
24 | test('should appear as selected', () => {
25 | expect(getDayButton(day)).toHaveAttribute('aria-selected', 'true');
26 | });
27 | test('should update the footer', () => {
28 | expect(getTableFooter()).toHaveTextContent(
29 | 'You selected November 1st, 2021.'
30 | );
31 | });
32 | describe('when the day is clicked again', () => {
33 | beforeEach(async () => act(() => user.click(getDayButton(day))));
34 | test('should appear as selected', () => {
35 | expect(getDayButton(day)).toHaveAttribute('aria-selected', 'true');
36 | });
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/src/components/YearsDropdown/__snapshots__/YearsDropdown.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`when fromDate and toDate are passed in should render the dropdown element 1`] = `
4 |
7 |
10 | Year:
11 |
12 |
23 |
27 | 2021
28 |
41 |
42 |
43 | `;
44 |
--------------------------------------------------------------------------------
/src/hooks/useSelectedDays/useSelectedDays.test.ts:
--------------------------------------------------------------------------------
1 | import { DayPickerProps } from 'DayPicker';
2 |
3 | import { mockedContexts } from 'test/mockedContexts';
4 | import { renderDayPickerHook } from 'test/render';
5 | import { freezeBeforeAll } from 'test/utils';
6 |
7 | import { useSelectedDays } from './useSelectedDays';
8 |
9 | const today = new Date(2021, 11, 8);
10 | freezeBeforeAll(today);
11 |
12 | function renderHook(dayPickerProps: DayPickerProps) {
13 | return renderDayPickerHook(
14 | () => useSelectedDays(),
15 | dayPickerProps,
16 | mockedContexts
17 | );
18 | }
19 |
20 | describe('when in single selection mode', () => {
21 | const mode = 'single';
22 | test('should return the selection from the single context', () => {
23 | const result = renderHook({ mode, selected: today });
24 | expect(result.current).toBe(mockedContexts.single.selected);
25 | });
26 | });
27 |
28 | describe('when in multiple selection mode', () => {
29 | const mode = 'multiple';
30 | test('should return the selection from the multiple context', () => {
31 | const result = renderHook({ mode });
32 | expect(result.current).toBe(mockedContexts.multiple.selected);
33 | });
34 | });
35 |
36 | describe('when in range selection mode', () => {
37 | const mode = 'range';
38 | test('should return the selection from the range context', () => {
39 | const result = renderHook({ mode });
40 | expect(result.current).toBe(mockedContexts.range.selected);
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/website/test-integration/examples/rtl.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { axe } from '@site/test/axe';
4 | import { user } from '@site/test/user';
5 | import { freezeBeforeAll } from '@site/test/utils';
6 | import { act, render } from '@testing-library/react';
7 |
8 | import {
9 | getMonthCaption,
10 | getNextButton,
11 | getPrevButton
12 | } from 'react-day-picker/test/selectors';
13 |
14 | import Example from '@examples/rtl';
15 |
16 | const today = new Date(2021, 10, 25);
17 | freezeBeforeAll(today);
18 |
19 | let container: HTMLElement;
20 | beforeEach(() => {
21 | container = render().container;
22 | });
23 | test('should not have AXE violations', async () => {
24 | expect(await axe(container)).toHaveNoViolations();
25 | });
26 |
27 | test('should have the rtl attribute', () => {
28 | expect(container.firstChild).toHaveAttribute('dir', 'rtl');
29 | });
30 |
31 | describe('when clicking the next month button', () => {
32 | beforeEach(async () => {
33 | await act(() => user.click(getNextButton()));
34 | });
35 | test('should display the next month', () => {
36 | expect(getMonthCaption()).toHaveTextContent('ديسمبر 2021');
37 | });
38 | });
39 |
40 | describe('when clicking the previous month button', () => {
41 | beforeEach(async () => {
42 | await act(() => user.click(getPrevButton()));
43 | });
44 | test('should display the previous month', () => {
45 | expect(getMonthCaption()).toHaveTextContent('أكتوبر 2021');
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/defaultContextValues.ts:
--------------------------------------------------------------------------------
1 | import { enUS } from 'date-fns/locale';
2 |
3 | import { CaptionLayout } from 'components/Caption';
4 | import { DayPickerContextValue } from 'contexts/DayPicker';
5 |
6 | import { defaultClassNames } from './defaultClassNames';
7 | import * as formatters from './formatters';
8 | import * as labels from './labels';
9 |
10 | export type DefaultContextProps =
11 | | 'captionLayout'
12 | | 'classNames'
13 | | 'formatters'
14 | | 'locale'
15 | | 'labels'
16 | | 'modifiersClassNames'
17 | | 'modifiers'
18 | | 'numberOfMonths'
19 | | 'styles'
20 | | 'today'
21 | | 'mode';
22 |
23 | export type DefaultContextValues = Pick<
24 | DayPickerContextValue,
25 | DefaultContextProps
26 | >;
27 | /**
28 | * Returns the default values to use in the DayPickerContext, in case they are
29 | * not passed down with the DayPicker initial props.
30 | */
31 | export function getDefaultContextValues(): DefaultContextValues {
32 | const captionLayout: CaptionLayout = 'buttons';
33 | const classNames = defaultClassNames;
34 | const locale = enUS;
35 | const modifiersClassNames = {};
36 | const modifiers = {};
37 | const numberOfMonths = 1;
38 | const styles = {};
39 | const today = new Date();
40 |
41 | return {
42 | captionLayout,
43 | classNames,
44 | formatters,
45 | labels,
46 | locale,
47 | modifiersClassNames,
48 | modifiers,
49 | numberOfMonths,
50 | styles,
51 | today,
52 | mode: 'default'
53 | };
54 | }
55 |
--------------------------------------------------------------------------------
/src/contexts/Navigation/useNavigationState.test.ts:
--------------------------------------------------------------------------------
1 | import { act } from '@testing-library/react';
2 | import { addMonths, startOfMonth } from 'date-fns';
3 | import { DayPickerProps } from 'DayPicker';
4 |
5 | import { renderDayPickerHook } from 'test/render';
6 | import { freezeBeforeAll } from 'test/utils';
7 |
8 | import { NavigationState, useNavigationState } from './useNavigationState';
9 |
10 | const today = new Date(2021, 11, 8);
11 | freezeBeforeAll(today);
12 |
13 | function renderHook(props: Partial = {}) {
14 | return renderDayPickerHook(useNavigationState, props);
15 | }
16 |
17 | describe('when goToMonth is called', () => {
18 | test('should set the month in state', () => {
19 | const onMonthChange = jest.fn();
20 | const result = renderHook({ onMonthChange });
21 | const month = addMonths(today, 2);
22 | act(() => result.current[1](month));
23 | expect(result.current[0]).toEqual(startOfMonth(month));
24 | expect(onMonthChange).toHaveBeenCalledWith(startOfMonth(month));
25 | });
26 | describe('when navigation is disabled', () => {
27 | test('should not set the month in state', () => {
28 | const onMonthChange = jest.fn();
29 | const result = renderHook({ disableNavigation: true, onMonthChange });
30 | const month = addMonths(today, 2);
31 | result.current[1](month);
32 | expect(result.current[0]).toEqual(startOfMonth(today));
33 | expect(onMonthChange).not.toHaveBeenCalled();
34 | });
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/src/hooks/useId/useIsomorphicLayoutEffect.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useLayoutEffect } from 'react';
2 |
3 | import { canUseDOM } from './useId';
4 |
5 | /**
6 | * React currently throws a warning when using useLayoutEffect on the server. To
7 | * get around it, we can conditionally useEffect on the server (no-op) and
8 | * useLayoutEffect in the browser. We occasionally need useLayoutEffect to
9 | * ensure we don't get a render flash for certain operations, but we may also
10 | * need affected components to render on the server. One example is when setting
11 | * a component's descendants to retrieve their index values.
12 | *
13 | * Important to note that using this hook as an escape hatch will break the
14 | * eslint dependency warnings unless you rename the import to `useLayoutEffect`.
15 | * Use sparingly only when the effect won't effect the rendered HTML to avoid
16 | * any server/client mismatch.
17 | *
18 | * If a useLayoutEffect is needed and the result would create a mismatch, it's
19 | * likely that the component in question shouldn't be rendered on the server at
20 | * all, so a better approach would be to lazily render those in a parent
21 | * component after client-side hydration.
22 | *
23 | * https://gist.github.com/gaearon/e7d97cdf38a2907924ea12e4ebdf3c85
24 | * https://github.com/reduxjs/react-redux/blob/master/src/utils/useIsomorphicLayoutEffect.js
25 | *
26 | * @param effect
27 | * @param deps
28 | */
29 | export const useIsomorphicLayoutEffect = canUseDOM()
30 | ? useLayoutEffect
31 | : useEffect;
32 |
--------------------------------------------------------------------------------
/website/test-integration/examples/from-to-month.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { axe } from '@site/test/axe';
4 | import { user } from '@site/test/user';
5 | import { act, render } from '@testing-library/react';
6 | import { differenceInMonths } from 'date-fns';
7 |
8 | import { getNextButton, getPrevButton } from 'react-day-picker/test/selectors';
9 |
10 | import Example from '@examples/from-to-month';
11 |
12 | const fromDate = new Date(2015, 5);
13 | const toDate = new Date(2015, 10);
14 | let container: HTMLElement;
15 | beforeEach(() => (container = render().container));
16 |
17 | test('should not have AXE violations', async () => {
18 | expect(await axe(container)).toHaveNoViolations();
19 | });
20 |
21 | test('the previous month button should be disabled', () => {
22 | expect(getPrevButton()).toBeDisabled();
23 | });
24 |
25 | test('the next month button should not be disabled', () => {
26 | expect(getNextButton()).not.toBeDisabled();
27 | });
28 |
29 | describe('when navigating to the last month', () => {
30 | const nOfMonths = differenceInMonths(toDate, fromDate);
31 | beforeEach(async () => {
32 | for (let i = 0; i < nOfMonths; i++) {
33 | await act(() => user.click(getNextButton()));
34 | }
35 | });
36 |
37 | test('the previous month button should not be disabled', () => {
38 | expect(getPrevButton()).not.toBeDisabled();
39 | });
40 |
41 | test('the next month button should be disabled', () => {
42 | expect(getNextButton()).toBeDisabled();
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/src/contexts/Modifiers/utils/isDateInRange.test.ts:
--------------------------------------------------------------------------------
1 | import { addDays } from 'date-fns';
2 | import { DateRange } from 'index';
3 |
4 | import { isDateInRange } from './isDateInRange';
5 |
6 | const date = new Date();
7 |
8 | describe('when range is missing the "from" date', () => {
9 | const range: DateRange = { from: undefined };
10 | const result = isDateInRange(date, range);
11 | test('should return false', () => {
12 | expect(result).toBe(false);
13 | });
14 | });
15 |
16 | describe('when range is missing the "to" date', () => {
17 | const result = isDateInRange(date, { from: date, to: undefined });
18 | test('should return true', () => {
19 | expect(result).toBe(true);
20 | });
21 | });
22 |
23 | describe('when the range dates are the same as date', () => {
24 | const range: DateRange = { from: date, to: date };
25 | const result = isDateInRange(date, range);
26 | test('should return true', () => {
27 | expect(result).toBe(true);
28 | });
29 | });
30 |
31 | describe('when the range dates are the same but not as date', () => {
32 | const range: DateRange = { from: date, to: date };
33 | const result = isDateInRange(addDays(date, 1), range);
34 | test('should return false', () => {
35 | expect(result).toBe(false);
36 | });
37 | });
38 |
39 | describe('when the range is inverted', () => {
40 | const range: DateRange = { from: addDays(date, 1), to: date };
41 | const result = isDateInRange(date, range);
42 | test('should return true', () => {
43 | expect(result).toBe(true);
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/src/components/Button/Button.test.tsx:
--------------------------------------------------------------------------------
1 | import { screen } from '@testing-library/react';
2 |
3 | import { customRender } from 'test/render';
4 |
5 | import { Button } from './Button';
6 |
7 | let button: HTMLButtonElement;
8 |
9 | describe('when rendered without props', () => {
10 | beforeEach(() => {
11 | customRender();
12 | button = screen.getByRole('button');
13 | });
14 | test('should render a button with type "button"', () => {
15 | expect(button).toHaveAttribute('type', 'button');
16 | });
17 | test('should render a button with the button class name', () => {
18 | expect(button).toHaveClass('rdp-button');
19 | });
20 | test('should render a button with the reset class name', () => {
21 | expect(button).toHaveClass('rdp-button_reset');
22 | });
23 | test('should add the class name', () => {
24 | expect(button).toHaveClass('foo');
25 | });
26 | test('should apply the style', () => {
27 | expect(button).toHaveStyle({ color: 'blue' });
28 | });
29 | });
30 |
31 | describe('when using class names and styles from context', () => {
32 | beforeEach(() => {
33 | customRender(, {
34 | classNames: { button: 'foo' },
35 | styles: { button: { color: 'red' } }
36 | });
37 | button = screen.getByRole('button');
38 | });
39 | test('should apply the style', () => {
40 | expect(button).toHaveStyle({ color: 'red' });
41 | });
42 | test('should apply the class name', () => {
43 | expect(button).toHaveClass('foo');
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/src/components/MonthsDropdown/__snapshots__/MonthsDropdown.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`when fromDate and toDate are passed in should render the dropdown element 1`] = `
4 |
7 |
10 | Month:
11 |
12 |
28 |
32 | January
33 |
46 |
47 |
48 | `;
49 |
--------------------------------------------------------------------------------
/website/test-integration/examples/single.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { axe } from '@site/test/axe';
4 | import { user } from '@site/test/user';
5 | import { freezeBeforeAll } from '@site/test/utils';
6 | import { act, render } from '@testing-library/react';
7 |
8 | import { getDayButton, getTableFooter } from 'react-day-picker/test/selectors';
9 |
10 | import Example from '@examples/single';
11 |
12 | const today = new Date(2021, 10, 25);
13 | freezeBeforeAll(today);
14 | let container: HTMLElement;
15 | beforeEach(() => (container = render().container));
16 |
17 | test('should not have AXE violations', async () => {
18 | expect(await axe(container)).toHaveNoViolations();
19 | });
20 |
21 | describe('when a day is clicked', () => {
22 | const day = new Date(2021, 10, 1);
23 | beforeEach(async () => act(() => user.click(getDayButton(day))));
24 | test('should appear as selected', () => {
25 | expect(getDayButton(day)).toHaveAttribute('aria-selected', 'true');
26 | });
27 | test('should update the footer', () => {
28 | expect(getTableFooter()).toHaveTextContent(
29 | 'You selected November 1st, 2021.'
30 | );
31 | });
32 | describe('when the day is clicked again', () => {
33 | beforeEach(async () => act(() => user.click(getDayButton(day))));
34 | test('should appear as not selected', () => {
35 | expect(getDayButton(day)).not.toHaveAttribute('aria-selected');
36 | });
37 | test('should not have AXE violations', async () => {
38 | expect(await axe(container)).toHaveNoViolations();
39 | });
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/src/components/Table/utils/daysToMonthWeeks.ts:
--------------------------------------------------------------------------------
1 | import {
2 | addDays,
3 | differenceInCalendarDays,
4 | endOfISOWeek,
5 | endOfWeek,
6 | getISOWeek,
7 | getWeek,
8 | Locale,
9 | startOfISOWeek,
10 | startOfWeek
11 | } from 'date-fns';
12 |
13 | import { MonthWeek } from './getMonthWeeks';
14 |
15 | /** Return the weeks between two dates. */
16 | export function daysToMonthWeeks(
17 | fromDate: Date,
18 | toDate: Date,
19 | options?: {
20 | ISOWeek?: boolean;
21 | locale?: Locale;
22 | weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
23 | firstWeekContainsDate?: 1 | 4;
24 | }
25 | ): MonthWeek[] {
26 | const toWeek = options?.ISOWeek
27 | ? endOfISOWeek(toDate)
28 | : endOfWeek(toDate, options);
29 | const fromWeek = options?.ISOWeek
30 | ? startOfISOWeek(fromDate)
31 | : startOfWeek(fromDate, options);
32 |
33 | const nOfDays = differenceInCalendarDays(toWeek, fromWeek);
34 | const days: Date[] = [];
35 |
36 | for (let i = 0; i <= nOfDays; i++) {
37 | days.push(addDays(fromWeek, i));
38 | }
39 |
40 | const weeksInMonth = days.reduce((result: MonthWeek[], date) => {
41 | const weekNumber = options?.ISOWeek
42 | ? getISOWeek(date)
43 | : getWeek(date, options);
44 |
45 | const existingWeek = result.find(
46 | (value) => value.weekNumber === weekNumber
47 | );
48 | if (existingWeek) {
49 | existingWeek.dates.push(date);
50 | return result;
51 | }
52 | result.push({
53 | weekNumber,
54 | dates: [date]
55 | });
56 | return result;
57 | }, []);
58 |
59 | return weeksInMonth;
60 | }
61 |
--------------------------------------------------------------------------------
/src/contexts/Focus/utils/getInitialFocusTarget.ts:
--------------------------------------------------------------------------------
1 | import { addDays, endOfMonth, startOfMonth } from 'date-fns';
2 |
3 | import { getActiveModifiers } from 'contexts/Modifiers';
4 | import { Modifiers } from 'types/Modifiers';
5 |
6 | /**
7 | * Returns the day that should be the target of the focus when DayPicker is
8 | * rendered the first time.
9 | *
10 | * TODO: this function doesn't consider if the day is outside the month. We
11 | * implemented this check in `useDayRender` but it should probably go here. See
12 | * https://github.com/gpbl/react-day-picker/pull/1576
13 | */
14 | export function getInitialFocusTarget(
15 | displayMonths: Date[],
16 | modifiers: Modifiers
17 | ) {
18 | const firstDayInMonth = startOfMonth(displayMonths[0]);
19 | const lastDayInMonth = endOfMonth(displayMonths[displayMonths.length - 1]);
20 |
21 | // TODO: cleanup code
22 | let firstFocusableDay;
23 | let today;
24 | let date = firstDayInMonth;
25 | while (date <= lastDayInMonth) {
26 | const activeModifiers = getActiveModifiers(date, modifiers);
27 | const isFocusable = !activeModifiers.disabled && !activeModifiers.hidden;
28 | if (!isFocusable) {
29 | date = addDays(date, 1);
30 | continue;
31 | }
32 | if (activeModifiers.selected) {
33 | return date;
34 | }
35 | if (activeModifiers.today && !today) {
36 | today = date;
37 | }
38 | if (!firstFocusableDay) {
39 | firstFocusableDay = date;
40 | }
41 | date = addDays(date, 1);
42 | }
43 | if (today) {
44 | return today;
45 | } else {
46 | return firstFocusableDay;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/components/Row/Row.tsx:
--------------------------------------------------------------------------------
1 | import { getUnixTime } from 'date-fns';
2 |
3 | import { Day } from 'components/Day';
4 | import { WeekNumber } from 'components/WeekNumber';
5 | import { useDayPicker } from 'contexts/DayPicker';
6 |
7 | /**
8 | * The props for the {@link Row} component.
9 | */
10 | export interface RowProps {
11 | /** The month where the row is displayed. */
12 | displayMonth: Date;
13 | /** The number of the week to render. */
14 | weekNumber: number;
15 | /** The days contained in the week. */
16 | dates: Date[];
17 | }
18 |
19 | /** Render a row in the calendar, with the days and the week number. */
20 | export function Row(props: RowProps): JSX.Element {
21 | const { styles, classNames, showWeekNumber, components } = useDayPicker();
22 |
23 | const DayComponent = components?.Day ?? Day;
24 | const WeeknumberComponent = components?.WeekNumber ?? WeekNumber;
25 |
26 | let weekNumberCell;
27 | if (showWeekNumber) {
28 | weekNumberCell = (
29 |
30 |
31 | |
32 | );
33 | }
34 |
35 | return (
36 |
37 | {weekNumberCell}
38 | {props.dates.map((date) => (
39 | |
45 |
46 | |
47 | ))}
48 |
49 | );
50 | }
51 |
--------------------------------------------------------------------------------
/src/components/Table/utils/getMonthWeeks.ts:
--------------------------------------------------------------------------------
1 | import {
2 | addWeeks,
3 | endOfMonth,
4 | getWeeksInMonth,
5 | Locale,
6 | startOfMonth
7 | } from 'date-fns';
8 |
9 | import { daysToMonthWeeks } from './daysToMonthWeeks';
10 |
11 | /** Represents a week in the month.*/
12 | export type MonthWeek = {
13 | /** The week number from the start of the year. */
14 | weekNumber: number;
15 | /** The dates in the week. */
16 | dates: Date[];
17 | };
18 |
19 | /**
20 | * Return the weeks belonging to the given month, adding the "outside days" to
21 | * the first and last week.
22 | */
23 | export function getMonthWeeks(
24 | month: Date,
25 | options: {
26 | locale: Locale;
27 | useFixedWeeks?: boolean;
28 | weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
29 | firstWeekContainsDate?: 1 | 4;
30 | ISOWeek?: boolean;
31 | }
32 | ): MonthWeek[] {
33 | const weeksInMonth: MonthWeek[] = daysToMonthWeeks(
34 | startOfMonth(month),
35 | endOfMonth(month),
36 | options
37 | );
38 |
39 | if (options?.useFixedWeeks) {
40 | // Add extra weeks to the month, up to 6 weeks
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 | }
56 |
--------------------------------------------------------------------------------
/website/test-integration/examples/modifiers-today.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { user } from '@site/test/user';
4 | import { freezeBeforeAll } from '@site/test/utils';
5 | import { act, render } from '@testing-library/react';
6 | import { addDays } from 'date-fns';
7 |
8 | import { getDayButton, getTableFooter } from 'react-day-picker/test/selectors';
9 |
10 | import Example from '@examples/modifiers-today';
11 |
12 | const today = new Date(2022, 5, 10);
13 | freezeBeforeAll(today);
14 |
15 | let container: HTMLElement;
16 | beforeEach(() => {
17 | container = render().container;
18 | });
19 |
20 | describe('when rendering a month that contains today', () => {
21 | test('it should add the default class name for today', () => {
22 | expect(getDayButton(today)).toHaveClass('rdp-day_today');
23 | });
24 | test('it should have exactly one ".rdp-day_today" class', () => {
25 | const todays = container.querySelectorAll('.rdp-day_today');
26 | expect(todays).toHaveLength(1);
27 | });
28 | });
29 |
30 | describe('when the today date is clicked', () => {
31 | beforeEach(async () => act(() => user.click(getDayButton(today))));
32 | test('should update the footer', () => {
33 | expect(getTableFooter()).toHaveTextContent('You clicked today’s date');
34 | });
35 | });
36 |
37 | describe('when another date is clicked', () => {
38 | const date = addDays(today, 1);
39 | beforeEach(async () => act(() => user.click(getDayButton(date))));
40 | test('should update the footer', () => {
41 | expect(getTableFooter()).toHaveTextContent('Try clicking today’s date.');
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/src/components/WeekNumber/WeekNumber.tsx:
--------------------------------------------------------------------------------
1 | import { MouseEventHandler } from 'react';
2 |
3 | import { useDayPicker } from 'contexts/DayPicker';
4 |
5 | import { Button } from '../Button';
6 |
7 | /**
8 | * The props for the {@link 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 |
37 | {content}
38 |
39 | );
40 | }
41 |
42 | const label = labelWeekNumber(Number(weekNumber), { locale });
43 |
44 | const handleClick: MouseEventHandler = function (e) {
45 | onWeekNumberClick(weekNumber, dates, e);
46 | };
47 |
48 | return (
49 |
58 | );
59 | }
60 |
--------------------------------------------------------------------------------
/src/components/CaptionNavigation/CaptionNavigation.tsx:
--------------------------------------------------------------------------------
1 | import { MouseEventHandler } from 'react';
2 |
3 | import { isSameMonth } from 'date-fns';
4 |
5 | import { CaptionProps } from 'components/Caption/Caption';
6 | import { Navigation } from 'components/Navigation';
7 | import { useDayPicker } from 'contexts/DayPicker';
8 | import { useNavigation } from 'contexts/Navigation';
9 |
10 | /**
11 | * Render a caption with a button-based navigation.
12 | */
13 | export function CaptionNavigation(props: CaptionProps): JSX.Element {
14 | const { numberOfMonths } = useDayPicker();
15 | const { previousMonth, nextMonth, goToMonth, displayMonths } =
16 | useNavigation();
17 |
18 | const displayIndex = displayMonths.findIndex((month) =>
19 | isSameMonth(props.displayMonth, month)
20 | );
21 |
22 | const isFirst = displayIndex === 0;
23 | const isLast = displayIndex === displayMonths.length - 1;
24 |
25 | const hideNext = numberOfMonths > 1 && (isFirst || !isLast);
26 | const hidePrevious = numberOfMonths > 1 && (isLast || !isFirst);
27 |
28 | const handlePreviousClick: MouseEventHandler = () => {
29 | if (!previousMonth) return;
30 | goToMonth(previousMonth);
31 | };
32 |
33 | const handleNextClick: MouseEventHandler = () => {
34 | if (!nextMonth) return;
35 | goToMonth(nextMonth);
36 | };
37 |
38 | return (
39 |
48 | );
49 | }
50 |
--------------------------------------------------------------------------------
/website/docs/guides/input-fields.md:
--------------------------------------------------------------------------------
1 | # Input Fields
2 |
3 | It is a common scenario to bind the date picker with an input field. Since these kind of implementations may have different requirements, DayPicker doesn't come with an input field component.
4 |
5 | It is easy to build a custom input field that works together with DayPicker:
6 |
7 | 1. when the input field changes, parse its value and set the selected day in DayPicker
8 | 2. when a day is selected from DayPicker, set the input value formatting the selected date.
9 |
10 | ## Example: Date Picker Dialog
11 |
12 | Implement a DayPicker dialog according to the WAI-ARIA’s [dialog pattern](https://www.w3.org/TR/wai-aria-practices/examples/dialog-modal/datepicker-dialog.html) for date pickers.
13 |
14 | The following example uses [react-popper](https://popper.js.org/react-popper/) for the popover.
15 |
16 | ```include-example dependencies=popper,react-popper,@popperjs/core,focus-trap-react,prop-types
17 | date-picker-dialog
18 | ```
19 |
20 | ## Example: Range Selection
21 |
22 | ```include-example
23 | input-range
24 | ```
25 |
26 | ## Example: Time Selection
27 |
28 | DayPicker can also be used alongside a time input field, by setting the time to the selected date.
29 |
30 | ```include-example
31 | input-time
32 | ```
33 |
34 | ## Using the `useInput` Hook
35 |
36 | To bind DayPicker to an input field, DayPicker includes the [useInput hook](/api/functions/useinput), returning props to bind DayPicker with a single input field. Should you need something more sophisticated, give a look to the [useInput](/api/functions/useinput) source to implement your own hook.
37 |
38 | ```include-example
39 | useinput.tsx
40 | ```
41 |
--------------------------------------------------------------------------------
/website/src/theme/CodeBlock/sandpack-app/dark.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 1em;
3 | font-family: system-ui, -apple-system, 'Segoe UI', Roboto, Ubuntu, Cantarell,
4 | 'Noto Sans', sans-serif, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial,
5 | sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
6 | -webkit-font-smoothing: antialiased;
7 | -moz-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: antialiased;
9 | text-rendering: optimizeLegibility;
10 | -webkit-tap-highlight-color: transparent;
11 | -webkit-touch-callout: none;
12 | background-color: #191c1f;
13 | color: #f5f6f7;
14 | }
15 |
16 | input {
17 | font-size: 1em;
18 | padding: 0.4em 0.6em;
19 | }
20 | button {
21 | font-size: 1em;
22 | padding: 0.4em 0.6em;
23 | }
24 | input + button {
25 | margin-left: 0.5em;
26 | }
27 |
28 | .rdp {
29 | --rdp-accent-color-light: var(--rdp-accent-color-dark);
30 | --rdp-accent-color-lighter: var(--rdp-accent-color-darker);
31 | --rdp-accent-color-lightest: var(--rdp-accent-color-darkest);
32 | --rdp-background-color: var(--rdp-background-color-dark);
33 | }
34 |
35 | .dialog-sheet {
36 | background: #191c1f;
37 | border-radius: 4px;
38 | box-shadow: 0px 1px 10px rgba(0, 0, 0, 0.2), 0px 4px 5px rgba(0, 0, 0, 0.3),
39 | 0px 2px 4px -1px rgba(0, 0, 0, 0.4);
40 | }
41 |
42 | .bg-near-black {
43 | background-color: #eeeeee;
44 | }
45 | .bg-near-white {
46 | background-color: #111111;
47 | }
48 | .black {
49 | color: #ffffff;
50 | }
51 | .white {
52 | color: #eeeeee;
53 | }
54 | .dark-gray {
55 | color: #cccccc;
56 | }
57 | .bg-white {
58 | background-color: #111111;
59 | }
60 | .ba {
61 | border-color: #333;
62 | }
63 |
--------------------------------------------------------------------------------
/website/examples/range-shift-key.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { isSameDay } from 'date-fns';
4 | import {
5 | Button,
6 | DateRange,
7 | DayPicker,
8 | DayProps,
9 | useDayRender
10 | } from 'react-day-picker';
11 |
12 | function DayWithShiftKey(props: DayProps) {
13 | const buttonRef = React.useRef(null);
14 | const dayRender = useDayRender(props.date, props.displayMonth, buttonRef);
15 |
16 | if (dayRender.isHidden) {
17 | return <>>;
18 | }
19 | if (!dayRender.isButton) {
20 | return ;
21 | }
22 |
23 | const handleClick: React.MouseEventHandler = (e) => {
24 | if (
25 | !dayRender.selectedDays ||
26 | dayRender.activeModifiers.selected ||
27 | e.shiftKey
28 | ) {
29 | dayRender.buttonProps?.onClick?.(e);
30 | }
31 | };
32 |
33 | return (
34 |
35 | );
36 | }
37 |
38 | export default function App() {
39 | const [range, setRange] = React.useState();
40 |
41 | let footer = Please pick a day.
;
42 |
43 | if (range?.from && range?.to) {
44 | if (isSameDay(range.from, range.to)) {
45 | footer = Press Shift to choose more days.
;
46 | } else {
47 | footer = (
48 |
49 | {range.from.toLocaleDateString()}—{range.to.toLocaleDateString()}.
50 |
51 | );
52 | }
53 | }
54 | return (
55 |
64 | );
65 | }
66 |
--------------------------------------------------------------------------------
/src/contexts/DayPicker/defaultClassNames.ts:
--------------------------------------------------------------------------------
1 | import { ClassNames } from 'types/Styles';
2 |
3 | /**
4 | * The name of the default CSS classes.
5 | */
6 | export const defaultClassNames: Required = {
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 | };
59 |
--------------------------------------------------------------------------------
/src/components/CaptionDropdowns/CaptionDropdowns.tsx:
--------------------------------------------------------------------------------
1 | import { addMonths } from 'date-fns';
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, components } = useDayPicker();
16 | const { goToMonth } = useNavigation();
17 |
18 | const handleMonthChange: MonthChangeEventHandler = (newMonth) => {
19 | goToMonth(
20 | addMonths(newMonth, props.displayIndex ? -props.displayIndex : 0)
21 | );
22 | };
23 | const CaptionLabelComponent = components?.CaptionLabel ?? CaptionLabel;
24 | const captionLabel = (
25 |
26 | );
27 | return (
28 |
32 | {/* Caption label is visually hidden but for a11y. */}
33 |
{captionLabel}
34 |
38 |
42 |
43 | );
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/Head/Head.test.tsx:
--------------------------------------------------------------------------------
1 | import { RenderResult, screen } from '@testing-library/react';
2 | import { DayPickerProps } from 'DayPicker';
3 |
4 | import { customRender } from 'test/render';
5 |
6 | import { Head } from './Head';
7 |
8 | let container: HTMLElement;
9 | let view: RenderResult;
10 |
11 | function setup(dayPickerProps: DayPickerProps = {}) {
12 | view = customRender(
13 |
15 | ,
16 | dayPickerProps
17 | );
18 | container = view.container.firstChild as HTMLTableCellElement;
19 | }
20 |
21 | const dayPickerProps = {
22 | styles: {
23 | head: { color: 'red' },
24 | head_row: { color: 'blue' },
25 | head_cell: { color: 'green' }
26 | },
27 | classNames: {
28 | head: 'foo',
29 | head_row: 'foo_row',
30 | head_cell: 'foo_head-cell'
31 | }
32 | };
33 |
34 | describe('when rendered', () => {
35 | beforeEach(() => {
36 | setup(dayPickerProps);
37 | });
38 |
39 | test('thead should have the `head` style', () => {
40 | expect(container.firstChild).toHaveStyle(dayPickerProps.styles.head);
41 | });
42 |
43 | test('thead should have the `head` class', () => {
44 | expect(container.firstChild).toHaveClass(dayPickerProps.classNames.head);
45 | });
46 | });
47 |
48 | describe('when using a custom HeadRow component', () => {
49 | beforeEach(() => {
50 | setup({
51 | ...dayPickerProps,
52 | components: {
53 | HeadRow: () => (
54 |
55 | | custom head |
56 |
57 | )
58 | }
59 | });
60 | });
61 |
62 | test('should render the custom component', () => {
63 | expect(screen.getByText('custom head')).toBeInTheDocument();
64 | });
65 | });
66 |
--------------------------------------------------------------------------------
/src/contexts/Modifiers/utils/getActiveModifiers.test.ts:
--------------------------------------------------------------------------------
1 | import { addMonths } from 'date-fns';
2 |
3 | import {
4 | InternalModifier,
5 | InternalModifiers,
6 | Modifiers
7 | } from 'types/Modifiers';
8 |
9 | import { getActiveModifiers } from './getActiveModifiers';
10 |
11 | const day = new Date();
12 |
13 | const internalModifiers: InternalModifiers = {
14 | [InternalModifier.Outside]: [],
15 | [InternalModifier.Disabled]: [],
16 | [InternalModifier.Selected]: [],
17 | [InternalModifier.Hidden]: [],
18 | [InternalModifier.Today]: [],
19 | [InternalModifier.RangeStart]: [],
20 | [InternalModifier.RangeEnd]: [],
21 | [InternalModifier.RangeMiddle]: []
22 | };
23 | describe('when the day matches a modifier', () => {
24 | const modifiers: Modifiers = {
25 | ...internalModifiers,
26 | foo: [day]
27 | };
28 | const result = getActiveModifiers(day, modifiers);
29 | test('should return the modifier as active', () => {
30 | expect(result.foo).toBe(true);
31 | });
32 | });
33 | describe('when the day does not match a modifier', () => {
34 | const modifiers: Modifiers = {
35 | ...internalModifiers,
36 | foo: []
37 | };
38 | const result = getActiveModifiers(day, modifiers);
39 | test('should not return the modifier as active', () => {
40 | expect(result.foo).toBeUndefined();
41 | });
42 | });
43 |
44 | describe('when the day is not in the same display month', () => {
45 | const modifiers: Modifiers = {
46 | ...internalModifiers
47 | };
48 | const displayMonth = addMonths(day, 1);
49 | const result = getActiveModifiers(day, modifiers, displayMonth);
50 | test('should not return the modifier as active', () => {
51 | expect(result.outside).toBe(true);
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './DayPicker';
2 | import './style.css';
3 |
4 | export * from 'components/Button';
5 | export * from 'components/Caption';
6 | export * from 'components/CaptionDropdowns';
7 | export * from 'components/CaptionLabel';
8 | export * from 'components/CaptionNavigation';
9 | export * from 'components/Day';
10 | export * from 'components/DayContent';
11 | export * from 'components/Dropdown';
12 | export * from 'components/Footer';
13 | export * from 'components/Head';
14 | export * from 'components/HeadRow';
15 | export * from 'components/IconDropdown';
16 | export * from 'components/IconRight';
17 | export * from 'components/IconLeft';
18 | export * from 'components/Months';
19 | export * from 'components/Row';
20 | export * from 'components/WeekNumber';
21 |
22 | export * from 'hooks/useInput';
23 | export * from 'hooks/useDayRender';
24 | export * from 'hooks/useActiveModifiers';
25 |
26 | export * from 'contexts/DayPicker';
27 | export * from 'contexts/Focus';
28 | export * from 'contexts/Navigation';
29 | export * from 'contexts/RootProvider';
30 | export * from 'contexts/SelectMultiple';
31 | export * from 'contexts/SelectRange';
32 | export * from 'contexts/SelectSingle';
33 |
34 | export * from 'types/DayPickerBase';
35 | export * from 'types/DayPickerDefault';
36 | export * from 'types/DayPickerMultiple';
37 | export * from 'types/DayPickerRange';
38 | export * from 'types/DayPickerSingle';
39 | export * from 'types/EventHandlers';
40 | export * from 'types/Formatters';
41 | export * from 'types/Labels';
42 | export * from 'types/Matchers';
43 | export * from 'types/Modifiers';
44 | export * from 'types/Styles';
45 |
46 | export * from 'contexts/Modifiers/utils/isMatch';
47 | export * from 'contexts/SelectRange/utils/addToRange';
48 |
--------------------------------------------------------------------------------
/website/test-integration/examples/dropdown-buttons.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { axe } from '@site/test/axe';
4 | import { user } from '@site/test/user';
5 | import { freezeBeforeAll } from '@site/test/utils';
6 | import { act, render } from '@testing-library/react';
7 |
8 | import {
9 | getMonthDropdown,
10 | getMonthGrid,
11 | getNextButton,
12 | getPrevButton,
13 | getYearDropdown
14 | } from 'react-day-picker/test/selectors';
15 |
16 | import Example from '@examples/dropdown-buttons';
17 |
18 | const today = new Date(2022, 5, 10);
19 | freezeBeforeAll(today);
20 |
21 | let container: HTMLElement;
22 | beforeEach(() => {
23 | container = render().container;
24 | });
25 |
26 | test('should not have AXE violations', async () => {
27 | expect(await axe(container)).toHaveNoViolations();
28 | });
29 |
30 | test('should display the year dropdown', () => {
31 | expect(getYearDropdown()).toBeInTheDocument();
32 | });
33 | test('should display the month dropdown', () => {
34 | expect(getMonthDropdown()).toBeInTheDocument();
35 | });
36 | test('should render the next month button', () => {
37 | expect(getNextButton()).toBeInTheDocument();
38 | });
39 | test('should render the previous month button', () => {
40 | expect(getPrevButton()).toBeInTheDocument();
41 | });
42 |
43 | describe('when choosing a month', () => {
44 | const monthName = 'January';
45 | beforeEach(() =>
46 | act(() => user.selectOptions(getMonthDropdown(), monthName))
47 | );
48 | test('should not have AXE violations', async () => {
49 | expect(await axe(container)).toHaveNoViolations();
50 | });
51 | test('should display the month', () => {
52 | expect(getMonthGrid()).toHaveAccessibleName(`${monthName} 2022`);
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/website/test-integration/examples/from-to-year.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { axe } from '@site/test/axe';
4 | import { user } from '@site/test/user';
5 | import { freezeBeforeAll } from '@site/test/utils';
6 | import { act, render } from '@testing-library/react';
7 | import { differenceInMonths } from 'date-fns';
8 |
9 | import { getNextButton, getPrevButton } from 'react-day-picker/test/selectors';
10 |
11 | import Example from '@examples/from-to-year';
12 |
13 | const fromDate = new Date(2015, 0);
14 | const toDate = new Date(2018, 11);
15 | const today = new Date(2021, 10, 25);
16 | freezeBeforeAll(today);
17 |
18 | let container: HTMLElement;
19 | beforeEach(() => (container = render().container));
20 |
21 | test('should not have AXE violations', async () => {
22 | expect(await axe(container)).toHaveNoViolations();
23 | });
24 | test('the previous month button should be disabled', () => {
25 | expect(getPrevButton()).toBeDisabled();
26 | });
27 | test('the next month button should not be disabled', () => {
28 | expect(getNextButton()).not.toBeDisabled();
29 | });
30 |
31 | describe('when navigating to the last month', () => {
32 | const nOfMonths = differenceInMonths(toDate, fromDate);
33 | beforeEach(async () => {
34 | for (let i = 0; i < nOfMonths; i++) {
35 | await act(() => user.click(getNextButton()));
36 | }
37 | });
38 | test('should not have AXE violations', async () => {
39 | expect(await axe(container)).toHaveNoViolations();
40 | });
41 | test('the previous month button should not be disabled', () => {
42 | expect(getPrevButton()).not.toBeDisabled();
43 | });
44 | test('the next month button should be disabled', () => {
45 | expect(getNextButton()).toBeDisabled();
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/website/test-integration/examples/multiple.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { axe } from '@site/test/axe';
4 | import { user } from '@site/test/user';
5 | import { freezeBeforeAll } from '@site/test/utils';
6 | import { act, render } from '@testing-library/react';
7 |
8 | import { getDayButton, getTableFooter } from 'react-day-picker/test/selectors';
9 |
10 | import Example from '@examples/multiple';
11 |
12 | const today = new Date(2021, 10, 25);
13 | freezeBeforeAll(today);
14 |
15 | let container: HTMLElement;
16 | beforeEach(() => (container = render().container));
17 |
18 | test('should not have AXE violations', async () => {
19 | expect(await axe(container)).toHaveNoViolations();
20 | });
21 |
22 | describe('when a day is clicked', () => {
23 | const day1 = new Date(2021, 10, 1);
24 | beforeEach(async () => act(() => user.click(getDayButton(day1))));
25 | test('should appear as selected', () => {
26 | expect(getDayButton(day1)).toHaveAttribute('aria-selected', 'true');
27 | });
28 | test('should update the footer', () => {
29 | expect(getTableFooter()).toHaveTextContent('You selected 1 day(s).');
30 | });
31 | describe('when a second day is clicked', () => {
32 | const day2 = new Date(2021, 10, 2);
33 | beforeEach(async () => act(() => user.click(getDayButton(day2))));
34 | test('the first day should appear as selected', () => {
35 | expect(getDayButton(day1)).toHaveAttribute('aria-selected', 'true');
36 | });
37 | test('the second day should appear as selected', () => {
38 | expect(getDayButton(day2)).toHaveAttribute('aria-selected', 'true');
39 | });
40 | test('should update the footer', () => {
41 | expect(getTableFooter()).toHaveTextContent('You selected 2 day(s).');
42 | });
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/src/hooks/useControlledValue/useControlledValue.test.ts:
--------------------------------------------------------------------------------
1 | import { act } from 'react-dom/test-utils';
2 |
3 | import { renderDayPickerHook } from 'test/render';
4 |
5 | import { useControlledValue } from './useControlledValue';
6 |
7 | function renderHook(defaultValue: string, controlledValue: string | undefined) {
8 | return renderDayPickerHook(() =>
9 | useControlledValue(defaultValue, controlledValue)
10 | );
11 | }
12 |
13 | describe('when the value is controlled', () => {
14 | const defaultValue = 'foo'; // not controlled
15 | const controlledValue = 'bar'; // now controlled
16 | test('should return the controlled value', () => {
17 | const result = renderHook(defaultValue, controlledValue);
18 | expect(result.current[0]).toBe(controlledValue);
19 | });
20 | describe('when setting a new value', () => {
21 | const newValue = 'taz';
22 | test('should return the controlled value instead', () => {
23 | const result = renderHook(defaultValue, controlledValue);
24 | act(() => result.current[1](newValue));
25 | expect(result.current[0]).toBe(controlledValue);
26 | });
27 | });
28 | });
29 |
30 | describe('when the value is not controlled', () => {
31 | const defaultValue = 'foo';
32 | const controlledValue = undefined;
33 | test('should return the value', () => {
34 | const result = renderHook(defaultValue, controlledValue);
35 | expect(result.current[0]).toBe(defaultValue);
36 | });
37 | describe('when setting a new value', () => {
38 | const newValue = 'bar';
39 | test('should return the new value', async () => {
40 | const result = renderHook(defaultValue, controlledValue);
41 | await act(() => result.current[1](newValue));
42 | expect(result.current[0]).toBe(newValue);
43 | });
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/website/docs/basics/localization.md:
--------------------------------------------------------------------------------
1 | # Localization
2 |
3 | ## Changing locale
4 |
5 | To change the locale, pass to the `locale` prop a date-fns [Locale object](http://date-fns.org/docs/Locale).
6 |
7 | For example, to localize the calendar in Spanish, import the locale object from date-fns and pass it to the component:
8 |
9 | ```include-example
10 | spanish
11 | ```
12 |
13 | ### Overriding the first day of the week
14 |
15 | Use the `weekStartsOn` prop to change the first day of the week:
16 |
17 | ```include-example
18 | spanish-week-starts-on
19 | ```
20 |
21 | ### First week of the year
22 |
23 | To override the date in the first week of the year, use `firstWeekContainsDate`. Use this prop to change the week number calculation according to [date-fns getWeek](https://date-fns.org/docs/getWeek) function.
24 |
25 | ```include-example
26 | weeknumber-custom
27 | ```
28 |
29 | ## Switching to ISO week dates
30 |
31 | By default, week numbers and week days follow the DayPicker's locale. Use the `ISOWeek` prop to switch to [ISO week dates](https://en.wikipedia.org/wiki/ISO_week_date).
32 |
33 | ```include-example
34 | week-iso
35 | ```
36 |
37 | ## Switching to right-to-left direction
38 |
39 | To add right-to-left text direction, set the `dir` prop to `rtl`.
40 |
41 | ```include-example
42 | rtl
43 | ```
44 |
45 | ## Other numbering systems
46 |
47 | Use [formatters](/guides/formatters) to change the numbering system used in the calendar.
48 |
49 | For example, to switch to hindu-arabic using [toLocaleString](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString):
50 |
51 | ```include-example
52 | numbering-system
53 | ```
54 |
55 | ## ARIA labels translations
56 |
57 | Use the [labels prop](/api/interfaces/daypickerdefaultprops#labels) to translate the labels used for ARIA.
58 |
--------------------------------------------------------------------------------
/tea.yaml:
--------------------------------------------------------------------------------
1 | # https://tea.xyz/what-is-this-file
2 | ---
3 | version: 1.0.0
4 | codeOwners:
5 | - '0xa4B775c3c99CfFaBf82773c57D293cfF8Aac14Fd'
6 | - '0xa0F9a70473cA850F7c60ef7698a1dAfF321bdf2a'
7 | - '0x7F5F5b4742BF1A04DbCD1f83E742cdd658597D1f'
8 | - '0x13d76fC6D16f502afe95e2fC6A95927C905BeC7E'
9 | - '0x63E4aD3Fe18097C228d12Af0Aa78dd861EC70F5b'
10 | - '0xBDe8ADD4a2034475d4F501228b23D5c9F0b195CB'
11 | - '0x45D17eD7917852B9cB4b62Fb08c7227BF5f150F6'
12 | - '0x6CC238840Cb28A6435bE7047f3D47DfAa2e5DbC1'
13 | - '0x73404449Ac35684780bDf917D176fe33Bd71424D'
14 | - '0xa93E9D62cbab2fc753fB009c3a23A64AfaB4c219'
15 | - '0x1264283bCA528E46C21A12A0042a5Ea4951dA9b1'
16 | - '0xBd7B8Cce141E51aaad8dAA9Bb2b49a5fe4c67376'
17 | - '0x1ac6C7Cf2A38fC46d55E22ba25e742117cb6B466'
18 | - '0x99F103FE54DD01238e0678d0578c3581a318b53a'
19 | - '0x7581cCa5dd243e32530f7ba283e73fFe87F91df1'
20 | - '0x215f95C3C7e4892C7FA8f1c56c0a8B5669111987'
21 | - '0x6B4487CCEF6c73d1042E573b1c6494b7A948bd48'
22 | - '0xd0db12E391031d46e071c3154DfCC2e9F1deADb5'
23 | - '0x7a55936cC6B2DA272Aaa69157b27585F63d832C2'
24 | - '0x51302DDA4E29c812d7660049c178d8fEAACA7Df3'
25 | - '0xe7021F0f7aF722D06A6328b864101153Cf864B2a'
26 | - '0xdf611328D77640bE47caa9b381Beb344c4079E7f'
27 | - '0x77bb6B58e8fa740c76534Fa0B0f2bCc03BfB8Da9'
28 | - '0x2c359D2d50589E9CF1cC05bd4FD2b5b66f0ea339'
29 | - '0xE0B36B9bf128715a7528D98ba7431776CbedF611'
30 | - '0x2185E70dBfc350331d97473015114194E9892f35'
31 | - '0xbB8c92bE8F41c538982a5AEaf5559b858E628E1F'
32 | - '0x9722d98924A669d14A889fe0C195ed25D8ed9a1e'
33 | - '0x049e20f78cE63eE6EA79CD0004554FF6526D1f5A'
34 | - '0x9d8bbBD4CC6c49130037741d3f29e4554798198c'
35 | - '0x622F5A30F3862B1Ff91c9e1259B458d9607a1B7f'
36 | - '0xC288049F4dB53288976661Eb87a2986e404EdF16'
37 | - '0x8cA9D137F0da46b7890cA6E0f671745eb97d5735'
38 | quorum: 1
39 |
--------------------------------------------------------------------------------
/website/test-integration/examples/multiple-months-paged.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { axe } from '@site/test/axe';
4 | import { user } from '@site/test/user';
5 | import { freezeBeforeAll } from '@site/test/utils';
6 | import { act, render, screen } from '@testing-library/react';
7 |
8 | import { getPrevButton } from 'react-day-picker/test/selectors';
9 |
10 | import Example from '@examples/multiple-months-paged';
11 |
12 | const today = new Date(2021, 10, 25);
13 | freezeBeforeAll(today);
14 |
15 | let container: HTMLElement;
16 | beforeEach(() => (container = render().container));
17 |
18 | test('should not have AXE violations', async () => {
19 | expect(await axe(container)).toHaveNoViolations();
20 | });
21 |
22 | describe('when rendering November 2021', () => {
23 | test('should render 2 grids', () => {
24 | expect(screen.getAllByRole('grid')).toHaveLength(2);
25 | });
26 | test('the first grid should be November', () => {
27 | const grids = screen.getAllByRole('grid');
28 | expect(grids[0]).toHaveAccessibleName('November 2021');
29 | });
30 | test('the second grid should be December', () => {
31 | expect(screen.getAllByRole('grid')[1]).toHaveAccessibleName(
32 | 'December 2021'
33 | );
34 | });
35 | // Test pagination
36 | describe('when the previous month button is clicked', () => {
37 | beforeEach(async () => act(() => user.click(getPrevButton())));
38 | test('the first month should be October', () => {
39 | const grids = screen.getAllByRole('grid');
40 | expect(grids[0]).toHaveAccessibleName('September 2021');
41 | });
42 | test('the month caption should be November', () => {
43 | const grids = screen.getAllByRole('grid');
44 | expect(grids[1]).toHaveAccessibleName('October 2021');
45 | });
46 | });
47 | });
48 |
--------------------------------------------------------------------------------