The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .babelrc
├── .eslintrc
├── .github
    ├── ISSUE_TEMPLATE
    │   └── bug_report.md
    └── workflows
    │   ├── node-pretest.yml
    │   ├── node.yml
    │   ├── rebase.yml
    │   └── require-allow-edits.yml
├── .gitignore
├── .lgtm
├── .npmignore
├── .npmrc
├── .nycrc
├── .storybook-css
    ├── .eslintrc
    ├── addons.js
    ├── config.js
    └── webpack.config.js
├── .storybook
    ├── .eslintrc
    ├── addons.js
    ├── config.js
    └── webpack.config.js
├── .travis.yml
├── CHANGELOG.md
├── INTHEWILD.md
├── LICENSE
├── MAINTAINERS
├── README.md
├── constants.js
├── css
    └── storybook.scss
├── examples
    ├── .eslintrc
    ├── DateRangePickerWrapper.jsx
    ├── DayPickerRangeControllerWrapper.jsx
    ├── DayPickerSingleDateControllerWrapper.jsx
    ├── PresetDateRangePicker.jsx
    └── SingleDatePickerWrapper.jsx
├── index.js
├── initialize.js
├── karma.conf.js
├── package.json
├── react-dates-demo.gif
├── scripts
    ├── .eslintrc
    ├── buildCSS.js
    ├── pure-component-fallback.js
    └── renderAllComponents.jsx
├── src
    ├── components
    │   ├── CalendarDay.jsx
    │   ├── CalendarIcon.jsx
    │   ├── CalendarMonth.jsx
    │   ├── CalendarMonthGrid.jsx
    │   ├── CalendarWeek.jsx
    │   ├── ChevronDown.jsx
    │   ├── ChevronUp.jsx
    │   ├── CloseButton.jsx
    │   ├── CustomizableCalendarDay.jsx
    │   ├── DateInput.jsx
    │   ├── DateRangePicker.jsx
    │   ├── DateRangePickerInput.jsx
    │   ├── DateRangePickerInputController.jsx
    │   ├── DayPicker.jsx
    │   ├── DayPickerKeyboardShortcuts.jsx
    │   ├── DayPickerNavigation.jsx
    │   ├── DayPickerRangeController.jsx
    │   ├── DayPickerSingleDateController.jsx
    │   ├── KeyboardShortcutRow.jsx
    │   ├── LeftArrow.jsx
    │   ├── RightArrow.jsx
    │   ├── SingleDatePicker.jsx
    │   ├── SingleDatePickerInput.jsx
    │   └── SingleDatePickerInputController.jsx
    ├── constants.js
    ├── defaultPhrases.js
    ├── index.js
    ├── initialize.js
    ├── shapes
    │   ├── AnchorDirectionShape.js
    │   ├── CalendarInfoPositionShape.js
    │   ├── DateRangePickerShape.js
    │   ├── DayOfWeekShape.js
    │   ├── DisabledShape.js
    │   ├── FocusedInputShape.js
    │   ├── IconPositionShape.js
    │   ├── ModifiersShape.js
    │   ├── NavPositionShape.js
    │   ├── OpenDirectionShape.js
    │   ├── OrientationShape.js
    │   ├── ScrollableOrientationShape.js
    │   └── SingleDatePickerShape.js
    ├── svg
    │   ├── arrow-left.svg
    │   ├── arrow-right.svg
    │   ├── calendar.svg
    │   ├── chevron-down.svg
    │   ├── chevron-up.svg
    │   └── close.svg
    ├── theme
    │   └── DefaultTheme.js
    └── utils
    │   ├── calculateDimension.js
    │   ├── disableScroll.js
    │   ├── getActiveElement.js
    │   ├── getCalendarDaySettings.js
    │   ├── getCalendarMonthWeeks.js
    │   ├── getCalendarMonthWidth.js
    │   ├── getDetachedContainerStyles.js
    │   ├── getInputHeight.js
    │   ├── getNumberOfCalendarMonthWeeks.js
    │   ├── getPhrase.jsx
    │   ├── getPhrasePropTypes.js
    │   ├── getPooledMoment.js
    │   ├── getPreviousMonthMemoLast.js
    │   ├── getResponsiveContainerStyles.js
    │   ├── getSelectedDateOffset.js
    │   ├── getTransformStyles.js
    │   ├── getVisibleDays.js
    │   ├── isAfterDay.js
    │   ├── isBeforeDay.js
    │   ├── isDayVisible.js
    │   ├── isInclusivelyAfterDay.js
    │   ├── isInclusivelyBeforeDay.js
    │   ├── isNextDay.js
    │   ├── isNextMonth.js
    │   ├── isPrevMonth.js
    │   ├── isPreviousDay.js
    │   ├── isSameDay.js
    │   ├── isSameMonth.js
    │   ├── isTransitionEndSupported.js
    │   ├── modifiers.js
    │   ├── noflip.js
    │   ├── registerCSSInterfaceWithDefaultTheme.js
    │   ├── registerInterfaceWithDefaultTheme.js
    │   ├── toISODateString.js
    │   ├── toISOMonthString.js
    │   ├── toLocalizedDateString.js
    │   └── toMomentObject.js
├── stories
    ├── .eslintrc
    ├── DateRangePicker.js
    ├── DateRangePicker_calendar.js
    ├── DateRangePicker_day.js
    ├── DateRangePicker_input.js
    ├── DayPicker.js
    ├── DayPickerRangeController.js
    ├── DayPickerSingleDateController.js
    ├── InfoPanelDecorator.js
    ├── PresetDateRangePicker.js
    ├── SingleDatePicker.js
    ├── SingleDatePicker_calendar.js
    ├── SingleDatePicker_day.js
    ├── SingleDatePicker_input.js
    └── withStyles.js
└── test
    ├── _helpers
        ├── describeIfWindow.js
        ├── enzymeSetup.js
        ├── registerReactWithStylesInterface.js
        ├── restoreSinonStubs.js
        └── withTouchSupport.js
    ├── browser-main.js
    ├── components
        ├── CalendarDay_spec.jsx
        ├── CalendarMonthGrid_spec.jsx
        ├── CalendarMonth_spec.jsx
        ├── CalendarWeek_spec.jsx
        ├── CustomizableCalendarDay_spec.jsx
        ├── DateInput_spec.jsx
        ├── DateRangePickerInputController_spec.jsx
        ├── DateRangePickerInput_spec.jsx
        ├── DateRangePicker_spec.jsx
        ├── DayPickerKeyboardShortcuts_spec.jsx
        ├── DayPickerNavigation_spec.jsx
        ├── DayPickerRangeController_spec.jsx
        ├── DayPickerSingleDateController_spec.jsx
        ├── DayPicker_spec.jsx
        ├── KeyboardShortcutRow_spec.jsx
        ├── SingleDatePickerInputController_spec.jsx
        ├── SingleDatePickerInput_spec.jsx
        └── SingleDatePicker_spec.jsx
    ├── mocha.opts
    └── utils
        ├── calculateDimension_spec.js
        ├── disableScroll_spec.js
        ├── getActiveElement_spec.js
        ├── getCalendarDaySettings_spec.js
        ├── getCalendarMonthWeeks_spec.js
        ├── getCalendarMonthWidth_spec.js
        ├── getDetachedContainerStyles_spec.js
        ├── getInputHeight_spec.js
        ├── getNumberOfCalendarMonthWeeks_spec.js
        ├── getPhrasePropTypes_spec.js
        ├── getPhrase_spec.js
        ├── getPooledMoment_spec.js
        ├── getResponsiveContainerStyles_spec.js
        ├── getSelectedDateOffset_spec.js
        ├── getTransformStyles_spec.js
        ├── getVisibleDays_spec.js
        ├── isAfterDay_spec.js
        ├── isBeforeDay_spec.js
        ├── isDayVisible_spec.js
        ├── isInclusivelyAfterDay_spec.js
        ├── isInclusivelyBeforeDay_spec.js
        ├── isNextDay_spec.js
        ├── isNextMonth_spec.js
        ├── isPrevMonth_spec.js
        ├── isPreviousDay_spec.js
        ├── isSameDay_spec.js
        ├── isSameMonth_spec.js
        ├── isTransitionEndSupported_spec.js
        ├── noflip_spec.js
        ├── toISODateString_spec.js
        ├── toISOMonthString_spec.js
        ├── toLocalizedDateString_spec.js
        └── toMomentObject_spec.js


/.babelrc:
--------------------------------------------------------------------------------
 1 | {
 2 |   "env": {
 3 |     "test": {
 4 |       "presets": [["airbnb", { looseClasses: true }]],
 5 |       "plugins": [
 6 |         ["import-path-replace", {
 7 |           "rules": [
 8 |             {
 9 |               "match": "../src/",
10 |               "replacement": "../lib/"
11 |             }
12 |           ]
13 |         }],
14 |         ["inline-react-svg", {
15 |           "svgo": false
16 |         }],
17 |         ["transform-replace-object-assign", { "moduleSpecifier": "object.assign" }],
18 |         "./scripts/pure-component-fallback.js",
19 |         "istanbul",
20 |       ]
21 |     },
22 | 
23 |     "development": {
24 |       "presets": [["airbnb", { looseClasses: true }]],
25 |       "plugins": [
26 |         ["inline-react-svg", {
27 |           "svgo": false
28 |         }],
29 |         ["transform-replace-object-assign", { "moduleSpecifier": "object.assign" }],
30 |         "./scripts/pure-component-fallback.js",
31 |       ],
32 |     },
33 | 
34 |     "production": {
35 |       "presets": [["airbnb", { looseClasses: true, removePropTypes: true }]],
36 |       "plugins": [
37 |         ["inline-react-svg", {
38 |           "svgo": false
39 |         }],
40 |         ["transform-replace-object-assign", { "moduleSpecifier": "object.assign" }],
41 |         "./scripts/pure-component-fallback.js",
42 |       ],
43 |     },
44 | 
45 |     "cjs": {
46 |       "presets": [["airbnb", { looseClasses: true, removePropTypes: true }]],
47 |       "plugins": [
48 |         ["inline-react-svg", {
49 |           "svgo": false
50 |         }],
51 |         ["transform-replace-object-assign", { "moduleSpecifier": "object.assign" }],
52 |         "./scripts/pure-component-fallback.js",
53 |       ],
54 |     },
55 | 
56 |     "esm": {
57 |       "presets": [["airbnb", { looseClasses: true, modules: false, removePropTypes: true }]],
58 |       "plugins": [
59 |         ["inline-react-svg", {
60 |           "svgo": false
61 |         }],
62 |         ["transform-replace-object-assign", { "moduleSpecifier": "object.assign" }],
63 |         "./scripts/pure-component-fallback.js",
64 |       ],
65 |     },
66 |   },
67 | }
68 | 


--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
 1 | {
 2 |   "root": true,
 3 | 
 4 |   "extends": [
 5 |     "airbnb",
 6 |     "plugin:react-with-styles/recommended",
 7 |   ],
 8 | 
 9 |   "plugins": [
10 |     "react-with-styles",
11 |   ],
12 | 
13 |   "env": {
14 |     "browser": true,
15 |     "node": true,
16 |   },
17 | 
18 |   "ignorePatterns": [
19 |     "lib/",
20 |     ".storybook/",
21 |     "test/_helpers/",
22 |     "webpack.config.js",
23 |   ],
24 | 
25 |   "parser": "@babel/eslint-parser",
26 | 
27 |   "rules": {
28 |     "max-len": "off",
29 | 
30 |     "react/forbid-foreign-prop-types": 2, // For babel-plugin-transform-react-remove-prop-types
31 | 
32 |     "jsx-a11y/click-events-have-key-events": 1, // TODO: enable
33 | 
34 |     "react-with-styles/no-unused-styles": 2,
35 | 
36 |     "no-restricted-imports": 0, // TODO: enable with full RTL support
37 | 
38 |     "react/jsx-props-no-spreading": 0, // TODO: re-evaluate
39 | 
40 |     "react/no-deprecated": 1, // TODO: update to UNSAFE_componentWillReceiveProps
41 |   },
42 | 
43 |   "overrides": [
44 |     {
45 |       "files": "test/**/*",
46 |       "env": {
47 |         "mocha": true,
48 |       },
49 |       "extends": "airbnb",
50 |       "rules": {
51 |         "react/jsx-props-no-spreading": 0,
52 |         //"import/no-extraneous-dependencies": [2, {
53 |           //"devDependencies": true
54 |         //}],
55 |         "indent": [2, 2, {
56 |           "MemberExpression": "off",
57 |         }],
58 |         "react/function-component-definition": "off",
59 |       },
60 |     },
61 |   ],
62 | 
63 |   "settings": {
64 |     "propWrapperFunctions": ["forbidExtraProps", "exact", "Object.freeze"],
65 |   },
66 | }
67 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | name: Bug report
 3 | about: Create a report to help us improve
 4 | title: ''
 5 | labels: ''
 6 | assignees: ''
 7 | 
 8 | ---
 9 | 
10 | **react-dates version**
11 | e.g. react-dates@18.3.1
12 | 
13 | **Describe the bug**
14 | A clear and concise description of what the bug is.
15 | 
16 | **Source code (including props configuration)**
17 | Steps to reproduce the behavior:
18 | ```
19 | <DateRangePicker
20 |   startDate={this.state.startDate}
21 |   startDateId="your_unique_start_date_id"
22 |   endDate={this.state.endDate}
23 |   endDateId="your_unique_end_date_id"
24 |   onDatesChange={({ startDate, endDate }) => this.setState({ startDate, endDate })}
25 |   focusedInput={this.state.focusedInput}
26 |   onFocusChange={focusedInput => this.setState({ focusedInput })}
27 | />
28 | ```
29 | If you have custom methods that you are passing into a `react-dates` component, e.g. `onDatesChange`, `onFocusChange`, `renderMonth`, `isDayBlocked`, etc., please include the source for those as well.
30 | 
31 | **Screenshots/Gifs**
32 | If applicable, add screenshots or gifs to help explain your problem.
33 | 
34 | **Desktop (please complete the following information):**
35 |  - OS: [e.g. iOS]
36 |  - Browser [e.g. chrome, safari]
37 |  - Version [e.g. 22]
38 | 
39 | **Smartphone (please complete the following information):**
40 |  - Device: [e.g. iPhone6]
41 |  - OS: [e.g. iOS8.1]
42 |  - Browser [e.g. stock browser, safari]
43 |  - Version [e.g. 22]
44 | 
45 | **Is the issue reproducible in Storybook?**
46 | Please link to the relevant storybook example
47 | 
48 | **Additional context**
49 | Add any other context about the problem here.
50 | 


--------------------------------------------------------------------------------
/.github/workflows/node-pretest.yml:
--------------------------------------------------------------------------------
 1 | name: 'Tests: pretest/posttest/build'
 2 | 
 3 | on: [pull_request, push]
 4 | 
 5 | jobs:
 6 |   pretest:
 7 |     runs-on: ubuntu-latest
 8 | 
 9 |     steps:
10 |       - uses: actions/checkout@v2
11 |       - uses: ljharb/actions/node/install@main
12 |         name: 'nvm install lts/* && npm install'
13 |         with:
14 |           node-version: 'lts/*'
15 |       - run: npm run pretest
16 | 
17 |   # posttest:
18 |   #   runs-on: ubuntu-latest
19 | 
20 |   #   steps:
21 |   #     - uses: actions/checkout@v2
22 |   #     - uses: ljharb/actions/node/install@main
23 |   #       name: 'nvm install lts/* && npm install'
24 |   #       with:
25 |   #         node-version: 'lts/*'
26 |   #     - run: npm run posttest
27 | 
28 |   build:
29 |     runs-on: ubuntu-latest
30 | 
31 |     steps:
32 |       - uses: actions/checkout@v2
33 |       - uses: ljharb/actions/node/install@main
34 |         name: 'nvm install lts/* && npm install'
35 |         with:
36 |           node-version: 'lts/*'
37 |       - run: npm run build
38 | 


--------------------------------------------------------------------------------
/.github/workflows/node.yml:
--------------------------------------------------------------------------------
 1 | name: 'Tests: node.js'
 2 | 
 3 | on: [pull_request, push]
 4 | 
 5 | jobs:
 6 |   matrix:
 7 |     runs-on: ubuntu-latest
 8 |     outputs:
 9 |       latest: ${{ steps.set-matrix.outputs.requireds }}
10 |       minors: ${{ steps.set-matrix.outputs.optionals }}
11 |     steps:
12 |       - uses: ljharb/actions/node/matrix@main
13 |         id: set-matrix
14 |         with:
15 |           type: 'majors'
16 |           versionsAsRoot: true
17 |           preset: '>=4'
18 | 
19 |   build:
20 |     runs-on: ubuntu-latest
21 |     steps:
22 |       - uses: actions/checkout@v2
23 |       - uses: ljharb/actions/node/install@main
24 |         with:
25 |           node-version: 'lts/*'
26 |       - uses: actions/cache@v2
27 |         id: cache
28 |         with:
29 |           path: |
30 |             lib
31 |             esm
32 |             test-build
33 |           key: ${{ runner.os }}-${{ hashFiles('package.json', 'src/**', 'test/**', 'scripts/buildCSS.js') }}
34 |       - run: npm run build
35 |         if: steps.cache.outputs.cache-hit != 'true'
36 |       - run: npm run build:test
37 |         if: steps.cache.outputs.cache-hit != 'true'
38 | 
39 | 
40 |   majors:
41 |     needs: [build, matrix]
42 |     name: 'latest minors'
43 |     runs-on: ubuntu-latest
44 | 
45 |     strategy:
46 |       fail-fast: false
47 |       matrix:
48 |         node-version: ${{ fromJson(needs.matrix.outputs.latest) }}
49 |         react:
50 |           - '16'
51 |           - '16.9'
52 |           - '16.3'
53 |           - '16.0'
54 |           - '15'
55 |           - '15.5'
56 |           - '15.0'
57 |           - '0.14'
58 | 
59 |     steps:
60 |       - uses: actions/checkout@v2
61 |       - uses: ljharb/actions/node/install@main
62 |         name: 'nvm install ${{ matrix.node-version }} && npm install'
63 |         with:
64 |           node-version: ${{ matrix.node-version }}
65 |           skip-ls-check: ${{ !startsWith(matrix.node-version, '5.') && !startsWith(matrix.node-version, '4.') && 'true' || 'false' }}
66 |       - uses: actions/cache@v2
67 |         id: cache
68 |         with:
69 |           path: |
70 |             lib
71 |             esm
72 |             test-build
73 |           key: ${{ runner.os }}-${{ hashFiles('package.json', 'src/**', 'test/**', 'scripts/buildCSS.js') }}
74 |       - run: npm run react
75 |         env:
76 |           REACT: ${{ matrix.react }}
77 |       - run: npm run tests-only
78 |         env:
79 |           REACT: ${{ matrix.react }}
80 |       - uses: codecov/codecov-action@v2
81 | 
82 |   node:
83 |     name: 'node.js'
84 |     needs: [majors]
85 |     runs-on: ubuntu-latest
86 |     steps:
87 |       - run: 'echo tests completed'
88 | 


--------------------------------------------------------------------------------
/.github/workflows/rebase.yml:
--------------------------------------------------------------------------------
 1 | name: Automatic Rebase
 2 | 
 3 | on: [pull_request_target]
 4 | 
 5 | jobs:
 6 |   _:
 7 |     name: "Automatic Rebase"
 8 | 
 9 |     runs-on: ubuntu-latest
10 | 
11 |     steps:
12 |     - uses: actions/checkout@v2
13 |     - uses: ljharb/rebase@master
14 |       env:
15 |         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
16 | 


--------------------------------------------------------------------------------
/.github/workflows/require-allow-edits.yml:
--------------------------------------------------------------------------------
 1 | name: Require “Allow Edits”
 2 | 
 3 | on: [pull_request_target]
 4 | 
 5 | jobs:
 6 |   _:
 7 |     name: "Require “Allow Edits”"
 8 | 
 9 |     runs-on: ubuntu-latest
10 | 
11 |     steps:
12 |     - uses: ljharb/require-allow-edits@main
13 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | # Logs
 2 | logs
 3 | *.log
 4 | npm-debug.log*
 5 | 
 6 | # Runtime data
 7 | pids
 8 | *.pid
 9 | *.seed
10 | 
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 | 
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 | 
17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
18 | .grunt
19 | 
20 | # node-waf configuration
21 | .lock-wscript
22 | 
23 | # Compiled binary addons (http://nodejs.org/api/addons.html)
24 | build/Release
25 | 
26 | # Dependency directory
27 | node_modules
28 | 
29 | # Optional npm cache directory
30 | .npm
31 | 
32 | # Optional REPL history
33 | .node_repl_history
34 | 
35 | # build folder
36 | lib
37 | esm
38 | test-build
39 | .idea
40 | 
41 | # gh-pages
42 | _gh-pages
43 | 
44 | .nyc_output
45 | 
46 | # Only apps should have lockfiles
47 | yarn.lock
48 | package-lock.json
49 | 
50 | css/styles.css
51 | 
52 | # Cache
53 | .cache/
54 | 


--------------------------------------------------------------------------------
/.lgtm:
--------------------------------------------------------------------------------
1 | approvals = 1
2 | pattern = "(?i):shipit:|:\\+1:|LGTM"
3 | 


--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
 1 | # Logs
 2 | logs
 3 | *.log
 4 | npm-debug.log*
 5 | 
 6 | # Runtime data
 7 | pids
 8 | *.pid
 9 | *.seed
10 | 
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 | 
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 | 
17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
18 | .grunt
19 | 
20 | # node-waf configuration
21 | .lock-wscript
22 | 
23 | # Compiled binary addons (http://nodejs.org/api/addons.html)
24 | build/Release
25 | 
26 | # Dependency directory
27 | node_modules
28 | 
29 | # Optional npm cache directory
30 | .npm
31 | 
32 | # Optional REPL history
33 | .node_repl_history
34 | 
35 | .cache
36 | .github
37 | .lgtm
38 | .storybook
39 | .storybook-css
40 | _gh-pages
41 | examples
42 | MAINTAINERS
43 | public
44 | react-dates-demo.gif
45 | stories
46 | test
47 | test-build
48 | css
49 | !lib/css
50 | 


--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 | 


--------------------------------------------------------------------------------
/.nycrc:
--------------------------------------------------------------------------------
 1 | {
 2 |   "extension": [
 3 |     ".js",
 4 |     ".jsx"
 5 |   ],
 6 |   "include": [
 7 |     "src"
 8 |   ],
 9 |   "require": [
10 |   ],
11 |   "reporter": [
12 |     "text",
13 |     "html",
14 |     "lcov"
15 |   ],
16 |   "all": true,
17 |   "sourceMap": false,
18 |   "instrument": false
19 | }
20 | 


--------------------------------------------------------------------------------
/.storybook-css/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 |   "rules": {
3 |     "import/no-extraneous-dependencies": [2, {
4 |       "devDependencies": true
5 |     }],
6 |     "global-require": 2,
7 |   }
8 | }
9 | 


--------------------------------------------------------------------------------
/.storybook-css/addons.js:
--------------------------------------------------------------------------------
1 | import '@storybook/addon-actions/register';
2 | import '@storybook/addon-links/register';
3 | 


--------------------------------------------------------------------------------
/.storybook-css/config.js:
--------------------------------------------------------------------------------
 1 | import React from 'react';
 2 | import moment from 'moment';
 3 | 
 4 | import { configure, addDecorator, setAddon } from '@storybook/react';
 5 | import infoAddon from '@storybook/addon-info';
 6 | import { setOptions } from '@storybook/addon-options';
 7 | 
 8 | import registerCSSInterfaceWithDefaultTheme from '../src/utils/registerCSSInterfaceWithDefaultTheme';
 9 | 
10 | import '../css/storybook.scss';
11 | import '../css/styles.css';
12 | 
13 | registerCSSInterfaceWithDefaultTheme();
14 | 
15 | addDecorator((story) => {
16 |   moment.locale('en');
17 |   return story();
18 | });
19 | 
20 | function getLink(href, text) {
21 |   return `<a href=${href} rel="noopener noreferrer" target="_blank">${text}</a>`;
22 | }
23 | 
24 | const README = getLink('https://github.com/react-dates/react-dates/blob/HEAD/README.md', 'README');
25 | const wrapperSource = getLink('https://github.com/react-dates/react-dates/tree/HEAD/examples', 'wrapper source');
26 | 
27 | const helperText = `All examples are built using a wrapper component that is not exported by
28 |   react-dates. Please see the ${README} for more information about minimal setup or explore
29 |   the ${wrapperSource} to see how to integrate react-dates into your own app.`;
30 | 
31 | addDecorator(story => (
32 |   <div>
33 |     <div
34 |       style={{
35 |         background: '#fff',
36 |         height: 6 * 8,
37 |         width: '100%',
38 |         position: 'fixed',
39 |         top: 0,
40 |         left: 0,
41 |         padding: '8px 40px 8px 8px',
42 |         overflow: 'scroll',
43 |       }}
44 |       dangerouslySetInnerHTML={{ __html: helperText }}
45 |     />
46 | 
47 |     <div style={{ marginTop: 7 * 8 }}>
48 |       {story()}
49 |     </div>
50 |   </div>
51 | ));
52 | 
53 | setOptions({
54 |   name: 'REACT-DATES',
55 |   url: 'https://github.com/react-dates/react-dates',
56 | });
57 | 
58 | function loadStories() {
59 |   require('../stories/DateRangePicker');
60 |   require('../stories/DateRangePicker_input');
61 |   require('../stories/DateRangePicker_calendar');
62 |   require('../stories/DateRangePicker_day');
63 |   require('../stories/SingleDatePicker');
64 |   require('../stories/SingleDatePicker_input');
65 |   require('../stories/SingleDatePicker_calendar');
66 |   require('../stories/SingleDatePicker_day');
67 |   require('../stories/DayPickerRangeController');
68 |   require('../stories/DayPickerSingleDateController');
69 |   require('../stories/DayPicker');
70 |   require('../stories/PresetDateRangePicker');
71 | }
72 | 
73 | setAddon(infoAddon);
74 | 
75 | configure(loadStories, module);
76 | 


--------------------------------------------------------------------------------
/.storybook-css/webpack.config.js:
--------------------------------------------------------------------------------
 1 | const path = require('path');
 2 | 
 3 | module.exports = {
 4 |   module: {
 5 |     rules: [
 6 |       {
 7 |         test: /\.s?css$/,
 8 |         use: ['style-loader', 'raw-loader', 'sass-loader'],
 9 |         include: [
10 |           path.resolve(__dirname, '../css/'),
11 |           /@storybook\/addon-info/,
12 |         ],
13 |       },
14 |       {
15 |         test: /\.svg$/,
16 |         use: [
17 |           {
18 |             loader: 'babel-loader',
19 |             query: {
20 |               presets: ['airbnb'],
21 |             },
22 |           },
23 |         ],
24 |       },
25 |       {
26 |         test: /\.jsx$/,
27 |         use: [
28 |           {
29 |             loader: 'babel-loader',
30 |             query: {
31 |               presets: ['airbnb'],
32 |             },
33 |           },
34 |         ],
35 |       },
36 |     ],
37 |   },
38 |   resolve: {
39 |     extensions: ['.js', '.jsx'],
40 |   },
41 | };
42 | 


--------------------------------------------------------------------------------
/.storybook/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 |   "rules": {
3 |     "import/no-extraneous-dependencies": [2, {
4 |       "devDependencies": true
5 |     }],
6 |     "global-require": 2,
7 |   }
8 | }
9 | 


--------------------------------------------------------------------------------
/.storybook/addons.js:
--------------------------------------------------------------------------------
1 | import '@storybook/addon-actions/register';
2 | import '@storybook/addon-links/register';
3 | 


--------------------------------------------------------------------------------
/.storybook/config.js:
--------------------------------------------------------------------------------
 1 | import React from 'react';
 2 | 
 3 | if (process.env.NODE_ENV !== 'production') {
 4 |   const whyDidYouRender = require('@welldone-software/why-did-you-render');
 5 |   whyDidYouRender(React);
 6 | }
 7 | 
 8 | import moment from 'moment';
 9 | import aphroditeInterface from 'react-with-styles-interface-aphrodite';
10 | 
11 | import { configure, addDecorator, setAddon } from '@storybook/react';
12 | import infoAddon from '@storybook/addon-info';
13 | import { setOptions } from '@storybook/addon-options';
14 | 
15 | import registerInterfaceWithDefaultTheme from '../src/utils/registerInterfaceWithDefaultTheme';
16 | 
17 | import '../css/storybook.scss';
18 | 
19 | registerInterfaceWithDefaultTheme(aphroditeInterface);
20 | 
21 | addDecorator((story) => {
22 |   moment.locale('en');
23 |   return story();
24 | });
25 | 
26 | function getLink(href, text) {
27 |   return `<a href=${href} rel="noopener noreferrer" target="_blank">${text}</a>`;
28 | }
29 | 
30 | const README = getLink('https://github.com/react-dates/react-dates/blob/HEAD/README.md', 'README');
31 | const wrapperSource = getLink('https://github.com/react-dates/react-dates/tree/HEAD/examples', 'wrapper source');
32 | 
33 | const helperText = `All examples are built using a wrapper component that is not exported by
34 |   react-dates. Please see the ${README} for more information about minimal setup or explore
35 |   the ${wrapperSource} to see how to integrate react-dates into your own app.`;
36 | 
37 | addDecorator(story => (
38 |   <div>
39 |     <div
40 |       style={{
41 |         background: '#fff',
42 |         height: 6 * 8,
43 |         width: '100%',
44 |         position: 'fixed',
45 |         top: 0,
46 |         left: 0,
47 |         padding: '8px 40px 8px 8px',
48 |         overflow: 'scroll',
49 |       }}
50 |       dangerouslySetInnerHTML={{ __html: helperText }}
51 |     />
52 | 
53 |     <div style={{ marginTop: 7 * 8 }}>
54 |       {story()}
55 |     </div>
56 |   </div>
57 | ));
58 | 
59 | setOptions({
60 |   name: 'REACT-DATES',
61 |   url: 'https://github.com/react-dates/react-dates',
62 | });
63 | 
64 | function loadStories() {
65 |   require('../stories/DateRangePicker');
66 |   require('../stories/DateRangePicker_input');
67 |   require('../stories/DateRangePicker_calendar');
68 |   require('../stories/DateRangePicker_day');
69 |   require('../stories/SingleDatePicker');
70 |   require('../stories/SingleDatePicker_input');
71 |   require('../stories/SingleDatePicker_calendar');
72 |   require('../stories/SingleDatePicker_day');
73 |   require('../stories/DayPickerRangeController');
74 |   require('../stories/DayPickerSingleDateController');
75 |   require('../stories/DayPicker');
76 |   require('../stories/PresetDateRangePicker');
77 | }
78 | 
79 | setAddon(infoAddon);
80 | 
81 | configure(loadStories, module);
82 | 


--------------------------------------------------------------------------------
/.storybook/webpack.config.js:
--------------------------------------------------------------------------------
 1 | const path = require('path');
 2 | 
 3 | module.exports = ({ config }) => {
 4 |   config.module.rules.push(
 5 |     {
 6 |       test: /\.s?css$/,
 7 |       use: ['style-loader', 'raw-loader', 'sass-loader'],
 8 |       include: [path.resolve(__dirname, '../css/')],
 9 |     },
10 |     {
11 |       test: /\.svg$/,
12 |       use: [
13 |         {
14 |           loader: 'babel-loader',
15 |           query: {
16 |             presets: ['airbnb'],
17 |           },
18 |         },
19 |       ],
20 |     },
21 |     {
22 |       test: /\.jsx$/,
23 |       use: [
24 |         {
25 |           loader: 'babel-loader',
26 |           query: {
27 |             presets: ['airbnb'],
28 |           },
29 |         },
30 |       ],
31 |     },
32 |   );
33 |   config.resolve.extensions = ['.js', '.jsx'];
34 |   return config;
35 | };
36 | 


--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
 1 | language: node_js
 2 | node_js: lts/*
 3 | before_install:
 4 |   - nvm install-latest-npm
 5 | services:
 6 |   - xvfb
 7 | script:
 8 |   - npm run tests-karma
 9 | env:
10 |   global:
11 |     - REACT=16
12 |     - DISPLAY=:99.0
13 | sudo: false
14 | 


--------------------------------------------------------------------------------
/INTHEWILD.md:
--------------------------------------------------------------------------------
 1 | Please use [pull requests](https://github.com/react-dates/react-dates/pull/new) to add your organization and/or project to this document!
 2 | 
 3 | Organizations
 4 | ----------
 5 |  - [Airbnb](https://github.com/airbnb)
 6 |  - [Стрелочка](https://strelchka.ru)
 7 | 
 8 | Projects
 9 | ----------
10 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT License (MIT)
 2 | 
 3 | Copyright (c) 2016 Airbnb
 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 | 


--------------------------------------------------------------------------------
/MAINTAINERS:
--------------------------------------------------------------------------------
 1 | gdborton
 2 | goatslacker
 3 | iancmyers
 4 | kesne
 5 | lelandrichardson
 6 | lencioni
 7 | ljharb
 8 | majapw
 9 | mikefowler
10 | mstorus
11 | spikebrehm
12 | wyattdanger
13 | 


--------------------------------------------------------------------------------
/constants.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/no-unresolved
2 | module.exports = require('./lib/constants');
3 | 


--------------------------------------------------------------------------------
/css/storybook.scss:
--------------------------------------------------------------------------------
 1 | body {
 2 |   background-color: rgba(0, 0, 0, 0.05);
 3 |   background-image: repeating-linear-gradient(0deg, transparent, transparent 7px, rgba(0, 0, 0, 0.2) 1px, transparent 8px), repeating-linear-gradient(90deg, transparent, transparent 7px, rgba(0, 0, 0, 0.2) 1px, transparent 8px);
 4 |   background-size: 8px 8px;
 5 | }
 6 | 
 7 | html {
 8 |   box-sizing: border-box;
 9 |   font-family: Helvetica, "sans-serif";
10 |   font-size: 14px;
11 | }
12 | 
13 | *, *:before, *:after {
14 |   box-sizing: inherit;
15 | }
16 | 
17 | a {
18 |   color: #008489;
19 |   font-weight: bold;
20 | }
21 | 
22 | .foo-bar {
23 |   background: red !important;
24 | }
25 | 


--------------------------------------------------------------------------------
/examples/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 |   "rules": {
3 |     "import/no-extraneous-dependencies": [2, {
4 |       "devDependencies": true
5 |     }],
6 |   }
7 | }
8 | 


--------------------------------------------------------------------------------
/examples/DateRangePickerWrapper.jsx:
--------------------------------------------------------------------------------
  1 | import React from 'react';
  2 | import PropTypes from 'prop-types';
  3 | import momentPropTypes from 'react-moment-proptypes';
  4 | import moment from 'moment';
  5 | import omit from 'lodash/omit';
  6 | 
  7 | import DateRangePicker from '../src/components/DateRangePicker';
  8 | 
  9 | import { DateRangePickerPhrases } from '../src/defaultPhrases';
 10 | import DateRangePickerShape from '../src/shapes/DateRangePickerShape';
 11 | import {
 12 |   START_DATE,
 13 |   END_DATE,
 14 |   HORIZONTAL_ORIENTATION,
 15 |   ANCHOR_LEFT,
 16 |   NAV_POSITION_TOP,
 17 |   OPEN_DOWN,
 18 | } from '../src/constants';
 19 | import isInclusivelyAfterDay from '../src/utils/isInclusivelyAfterDay';
 20 | 
 21 | const propTypes = {
 22 |   // example props for the demo
 23 |   autoFocus: PropTypes.bool,
 24 |   autoFocusEndDate: PropTypes.bool,
 25 |   stateDateWrapper: PropTypes.func,
 26 |   initialStartDate: momentPropTypes.momentObj,
 27 |   initialEndDate: momentPropTypes.momentObj,
 28 | 
 29 |   ...omit(DateRangePickerShape, [
 30 |     'startDate',
 31 |     'endDate',
 32 |     'onDatesChange',
 33 |     'focusedInput',
 34 |     'onFocusChange',
 35 |   ]),
 36 | };
 37 | 
 38 | const defaultProps = {
 39 |   // example props for the demo
 40 |   autoFocus: false,
 41 |   autoFocusEndDate: false,
 42 |   initialStartDate: null,
 43 |   initialEndDate: null,
 44 | 
 45 |   // input related props
 46 |   startDateId: START_DATE,
 47 |   startDatePlaceholderText: 'Start Date',
 48 |   endDateId: END_DATE,
 49 |   endDatePlaceholderText: 'End Date',
 50 |   disabled: false,
 51 |   required: false,
 52 |   screenReaderInputMessage: '',
 53 |   showClearDates: false,
 54 |   showDefaultInputIcon: false,
 55 |   customInputIcon: null,
 56 |   customArrowIcon: null,
 57 |   customCloseIcon: null,
 58 |   block: false,
 59 |   small: false,
 60 |   regular: false,
 61 |   autoComplete: 'off',
 62 | 
 63 |   // calendar presentation and interaction related props
 64 |   renderMonthText: null,
 65 |   orientation: HORIZONTAL_ORIENTATION,
 66 |   anchorDirection: ANCHOR_LEFT,
 67 |   horizontalMargin: 0,
 68 |   withPortal: false,
 69 |   withFullScreenPortal: false,
 70 |   initialVisibleMonth: null,
 71 |   numberOfMonths: 2,
 72 |   keepOpenOnDateSelect: false,
 73 |   reopenPickerOnClearDates: false,
 74 |   isRTL: false,
 75 |   openDirection: OPEN_DOWN,
 76 | 
 77 |   // navigation related props
 78 |   navPosition: NAV_POSITION_TOP,
 79 |   navPrev: null,
 80 |   navNext: null,
 81 |   onPrevMonthClick() {},
 82 |   onNextMonthClick() {},
 83 |   onClose() {},
 84 | 
 85 |   // day presentation and interaction related props
 86 |   renderCalendarDay: undefined,
 87 |   renderDayContents: null,
 88 |   minimumNights: 1,
 89 |   enableOutsideDays: false,
 90 |   isDayBlocked: () => false,
 91 |   isOutsideRange: day => !isInclusivelyAfterDay(day, moment()),
 92 |   isDayHighlighted: () => false,
 93 | 
 94 |   // internationalization
 95 |   displayFormat: () => moment.localeData().longDateFormat('L'),
 96 |   monthFormat: 'MMMM YYYY',
 97 |   phrases: DateRangePickerPhrases,
 98 | 
 99 |   stateDateWrapper: date => date,
100 | };
101 | 
102 | class DateRangePickerWrapper extends React.Component {
103 |   constructor(props) {
104 |     super(props);
105 | 
106 |     let focusedInput = null;
107 |     if (props.autoFocus) {
108 |       focusedInput = START_DATE;
109 |     } else if (props.autoFocusEndDate) {
110 |       focusedInput = END_DATE;
111 |     }
112 | 
113 |     this.state = {
114 |       focusedInput,
115 |       startDate: props.initialStartDate,
116 |       endDate: props.initialEndDate,
117 |     };
118 | 
119 |     this.onDatesChange = this.onDatesChange.bind(this);
120 |     this.onFocusChange = this.onFocusChange.bind(this);
121 |   }
122 | 
123 |   onDatesChange({ startDate, endDate }) {
124 |     const { stateDateWrapper } = this.props;
125 |     this.setState({
126 |       startDate: startDate && stateDateWrapper(startDate),
127 |       endDate: endDate && stateDateWrapper(endDate),
128 |     });
129 |   }
130 | 
131 |   onFocusChange(focusedInput) {
132 |     this.setState({ focusedInput });
133 |   }
134 | 
135 |   render() {
136 |     const { focusedInput, startDate, endDate } = this.state;
137 | 
138 |     // autoFocus, autoFocusEndDate, initialStartDate and initialEndDate are helper props for the
139 |     // example wrapper but are not props on the SingleDatePicker itself and
140 |     // thus, have to be omitted.
141 |     const props = omit(this.props, [
142 |       'autoFocus',
143 |       'autoFocusEndDate',
144 |       'initialStartDate',
145 |       'initialEndDate',
146 |       'stateDateWrapper',
147 |     ]);
148 | 
149 |     return (
150 |       <div>
151 |         <DateRangePicker
152 |           {...props}
153 |           onDatesChange={this.onDatesChange}
154 |           onFocusChange={this.onFocusChange}
155 |           focusedInput={focusedInput}
156 |           startDate={startDate}
157 |           endDate={endDate}
158 |         />
159 |       </div>
160 |     );
161 |   }
162 | }
163 | 
164 | DateRangePickerWrapper.propTypes = propTypes;
165 | DateRangePickerWrapper.defaultProps = defaultProps;
166 | 
167 | export default DateRangePickerWrapper;
168 | 


--------------------------------------------------------------------------------
/examples/DayPickerRangeControllerWrapper.jsx:
--------------------------------------------------------------------------------
  1 | /* eslint-disable react/no-unused-prop-types */
  2 | import React from 'react';
  3 | import PropTypes from 'prop-types';
  4 | import momentPropTypes from 'react-moment-proptypes';
  5 | import { forbidExtraProps } from 'airbnb-prop-types';
  6 | import moment from 'moment';
  7 | import omit from 'lodash/omit';
  8 | 
  9 | import DayPickerRangeController from '../src/components/DayPickerRangeController';
 10 | 
 11 | import ScrollableOrientationShape from '../src/shapes/ScrollableOrientationShape';
 12 | 
 13 | import { START_DATE, END_DATE, HORIZONTAL_ORIENTATION } from '../src/constants';
 14 | import isInclusivelyAfterDay from '../src/utils/isInclusivelyAfterDay';
 15 | 
 16 | const propTypes = forbidExtraProps({
 17 |   // example props for the demo
 18 |   autoFocusEndDate: PropTypes.bool,
 19 |   initialStartDate: momentPropTypes.momentObj,
 20 |   initialEndDate: momentPropTypes.momentObj,
 21 |   startDateOffset: PropTypes.func,
 22 |   endDateOffset: PropTypes.func,
 23 |   showInputs: PropTypes.bool,
 24 |   minDate: momentPropTypes.momentObj,
 25 |   maxDate: momentPropTypes.momentObj,
 26 | 
 27 |   keepOpenOnDateSelect: PropTypes.bool,
 28 |   minimumNights: PropTypes.number,
 29 |   isOutsideRange: PropTypes.func,
 30 |   isDayBlocked: PropTypes.func,
 31 |   isDayHighlighted: PropTypes.func,
 32 |   daysViolatingMinNightsCanBeClicked: PropTypes.bool,
 33 | 
 34 |   // DayPicker props
 35 |   enableOutsideDays: PropTypes.bool,
 36 |   numberOfMonths: PropTypes.number,
 37 |   orientation: ScrollableOrientationShape,
 38 |   verticalHeight: PropTypes.number,
 39 |   withPortal: PropTypes.bool,
 40 |   initialVisibleMonth: PropTypes.func,
 41 |   renderCalendarInfo: PropTypes.func,
 42 |   renderMonthElement: PropTypes.func,
 43 |   renderMonthText: PropTypes.func,
 44 | 
 45 |   navPrev: PropTypes.node,
 46 |   navNext: PropTypes.node,
 47 |   renderNavPrevButton: PropTypes.func,
 48 |   renderNavNextButton: PropTypes.func,
 49 | 
 50 |   onPrevMonthClick: PropTypes.func,
 51 |   onNextMonthClick: PropTypes.func,
 52 |   onOutsideClick: PropTypes.func,
 53 |   renderCalendarDay: PropTypes.func,
 54 |   renderDayContents: PropTypes.func,
 55 |   renderKeyboardShortcutsButton: PropTypes.func,
 56 |   renderKeyboardShortcutsPanel: PropTypes.func,
 57 | 
 58 |   // i18n
 59 |   monthFormat: PropTypes.string,
 60 | 
 61 |   isRTL: PropTypes.bool,
 62 | });
 63 | 
 64 | const defaultProps = {
 65 |   // example props for the demo
 66 |   autoFocusEndDate: false,
 67 |   initialStartDate: null,
 68 |   initialEndDate: null,
 69 |   startDateOffset: undefined,
 70 |   endDateOffset: undefined,
 71 |   showInputs: false,
 72 |   minDate: null,
 73 |   maxDate: null,
 74 | 
 75 |   // day presentation and interaction related props
 76 |   renderCalendarDay: undefined,
 77 |   renderDayContents: null,
 78 |   minimumNights: 1,
 79 |   isDayBlocked: () => false,
 80 |   isOutsideRange: day => !isInclusivelyAfterDay(day, moment()),
 81 |   isDayHighlighted: () => false,
 82 |   enableOutsideDays: false,
 83 |   daysViolatingMinNightsCanBeClicked: false,
 84 | 
 85 |   // calendar presentation and interaction related props
 86 |   orientation: HORIZONTAL_ORIENTATION,
 87 |   verticalHeight: undefined,
 88 |   withPortal: false,
 89 |   initialVisibleMonth: null,
 90 |   numberOfMonths: 2,
 91 |   onOutsideClick() {},
 92 |   keepOpenOnDateSelect: false,
 93 |   renderCalendarInfo: null,
 94 |   isRTL: false,
 95 |   renderMonthText: null,
 96 |   renderMonthElement: null,
 97 |   renderKeyboardShortcutsButton: undefined,
 98 |   renderKeyboardShortcutsPanel: undefined,
 99 | 
100 |   // navigation related props
101 |   navPrev: null,
102 |   navNext: null,
103 |   renderNavPrevButton: null,
104 |   renderNavNextButton: null,
105 |   onPrevMonthClick() {},
106 |   onNextMonthClick() {},
107 | 
108 |   // internationalization
109 |   monthFormat: 'MMMM YYYY',
110 | };
111 | 
112 | class DayPickerRangeControllerWrapper extends React.Component {
113 |   constructor(props) {
114 |     super(props);
115 | 
116 |     this.state = {
117 |       errorMessage: null,
118 |       focusedInput: props.autoFocusEndDate ? END_DATE : START_DATE,
119 |       startDate: props.initialStartDate,
120 |       endDate: props.initialEndDate,
121 |     };
122 | 
123 |     this.onDatesChange = this.onDatesChange.bind(this);
124 |     this.onFocusChange = this.onFocusChange.bind(this);
125 |   }
126 | 
127 |   onDatesChange({ startDate, endDate }) {
128 |     const { daysViolatingMinNightsCanBeClicked, minimumNights } = this.props;
129 |     let doesNotMeetMinNights = false;
130 |     if (daysViolatingMinNightsCanBeClicked && startDate && endDate) {
131 |       const dayDiff = endDate.diff(startDate.clone().startOf('day').hour(12), 'days');
132 |       doesNotMeetMinNights = dayDiff < minimumNights && dayDiff >= 0;
133 |     }
134 |     this.setState({
135 |       startDate,
136 |       endDate: doesNotMeetMinNights ? null : endDate,
137 |       errorMessage: doesNotMeetMinNights
138 |         ? 'That day does not meet the minimum nights requirement'
139 |         : null,
140 |     });
141 |   }
142 | 
143 |   onFocusChange(focusedInput) {
144 |     this.setState({
145 |       // Force the focusedInput to always be truthy so that dates are always selectable
146 |       focusedInput: !focusedInput ? START_DATE : focusedInput,
147 |     });
148 |   }
149 | 
150 |   render() {
151 |     const { renderCalendarInfo: renderCalendarInfoProp, showInputs } = this.props;
152 |     const {
153 |       errorMessage,
154 |       focusedInput,
155 |       startDate,
156 |       endDate,
157 |     } = this.state;
158 | 
159 |     const props = omit(this.props, [
160 |       'autoFocus',
161 |       'autoFocusEndDate',
162 |       'initialStartDate',
163 |       'initialEndDate',
164 |       'showInputs',
165 |     ]);
166 | 
167 |     const startDateString = startDate && startDate.format('YYYY-MM-DD');
168 |     const endDateString = endDate && endDate.format('YYYY-MM-DD');
169 |     const renderCalendarInfo = errorMessage ? () => <div>{errorMessage}</div> : renderCalendarInfoProp;
170 | 
171 |     return (
172 |       <div style={{ height: '100%' }}>
173 |         {showInputs && (
174 |           <div style={{ marginBottom: 16 }}>
175 |             <input type="text" name="start date" value={startDateString} readOnly />
176 |             <input type="text" name="end date" value={endDateString} readOnly />
177 |           </div>
178 |         )}
179 | 
180 |         <DayPickerRangeController
181 |           {...props}
182 |           onDatesChange={this.onDatesChange}
183 |           onFocusChange={this.onFocusChange}
184 |           focusedInput={focusedInput}
185 |           startDate={startDate}
186 |           endDate={endDate}
187 |           renderCalendarInfo={renderCalendarInfo}
188 |         />
189 |       </div>
190 |     );
191 |   }
192 | }
193 | 
194 | DayPickerRangeControllerWrapper.propTypes = propTypes;
195 | DayPickerRangeControllerWrapper.defaultProps = defaultProps;
196 | 
197 | export default DayPickerRangeControllerWrapper;
198 | 


--------------------------------------------------------------------------------
/examples/DayPickerSingleDateControllerWrapper.jsx:
--------------------------------------------------------------------------------
  1 | /* eslint-disable react/no-unused-prop-types */
  2 | import React from 'react';
  3 | import PropTypes from 'prop-types';
  4 | import momentPropTypes from 'react-moment-proptypes';
  5 | import { forbidExtraProps } from 'airbnb-prop-types';
  6 | import moment from 'moment';
  7 | import omit from 'lodash/omit';
  8 | 
  9 | import DayPickerSingleDateController from '../src/components/DayPickerSingleDateController';
 10 | 
 11 | import ScrollableOrientationShape from '../src/shapes/ScrollableOrientationShape';
 12 | 
 13 | import { HORIZONTAL_ORIENTATION } from '../src/constants';
 14 | import isInclusivelyAfterDay from '../src/utils/isInclusivelyAfterDay';
 15 | 
 16 | const propTypes = forbidExtraProps({
 17 |   // example props for the demo
 18 |   autoFocus: PropTypes.bool,
 19 |   initialDate: momentPropTypes.momentObj,
 20 |   showInput: PropTypes.bool,
 21 | 
 22 |   allowUnselect: PropTypes.bool,
 23 |   keepOpenOnDateSelect: PropTypes.bool,
 24 |   isOutsideRange: PropTypes.func,
 25 |   isDayBlocked: PropTypes.func,
 26 |   isDayHighlighted: PropTypes.func,
 27 | 
 28 |   // DayPicker props
 29 |   enableOutsideDays: PropTypes.bool,
 30 |   numberOfMonths: PropTypes.number,
 31 |   orientation: ScrollableOrientationShape,
 32 |   withPortal: PropTypes.bool,
 33 |   initialVisibleMonth: PropTypes.func,
 34 |   renderCalendarInfo: PropTypes.func,
 35 | 
 36 |   navPrev: PropTypes.node,
 37 |   navNext: PropTypes.node,
 38 |   renderNavPrevButton: PropTypes.func,
 39 |   renderNavNextButton: PropTypes.func,
 40 | 
 41 |   onPrevMonthClick: PropTypes.func,
 42 |   onNextMonthClick: PropTypes.func,
 43 |   onOutsideClick: PropTypes.func,
 44 |   renderCalendarDay: PropTypes.func,
 45 |   renderDayContents: PropTypes.func,
 46 | 
 47 |   // i18n
 48 |   monthFormat: PropTypes.string,
 49 | 
 50 |   isRTL: PropTypes.bool,
 51 | });
 52 | 
 53 | const defaultProps = {
 54 |   // example props for the demo
 55 |   autoFocus: false,
 56 |   initialDate: null,
 57 |   showInput: false,
 58 | 
 59 |   // day presentation and interaction related props
 60 |   allowUnselect: false,
 61 |   renderCalendarDay: undefined,
 62 |   renderDayContents: null,
 63 |   isDayBlocked: () => false,
 64 |   isOutsideRange: day => !isInclusivelyAfterDay(day, moment()),
 65 |   isDayHighlighted: () => false,
 66 |   enableOutsideDays: false,
 67 | 
 68 |   // calendar presentation and interaction related props
 69 |   orientation: HORIZONTAL_ORIENTATION,
 70 |   withPortal: false,
 71 |   initialVisibleMonth: null,
 72 |   numberOfMonths: 2,
 73 |   onOutsideClick() {},
 74 |   keepOpenOnDateSelect: false,
 75 |   renderCalendarInfo: null,
 76 |   isRTL: false,
 77 | 
 78 |   // navigation related props
 79 |   navPrev: null,
 80 |   navNext: null,
 81 |   renderNavPrevButton: null,
 82 |   renderNavNextButton: null,
 83 |   onPrevMonthClick() {},
 84 |   onNextMonthClick() {},
 85 | 
 86 |   // internationalization
 87 |   monthFormat: 'MMMM YYYY',
 88 | };
 89 | 
 90 | class DayPickerSingleDateControllerWrapper extends React.Component {
 91 |   constructor(props) {
 92 |     super(props);
 93 | 
 94 |     this.state = {
 95 |       focused: true,
 96 |       date: props.initialDate,
 97 |     };
 98 | 
 99 |     this.onDateChange = this.onDateChange.bind(this);
100 |     this.onFocusChange = this.onFocusChange.bind(this);
101 |   }
102 | 
103 |   onDateChange(date) {
104 |     this.setState({ date });
105 |   }
106 | 
107 |   onFocusChange() {
108 |     // Force the focused states to always be truthy so that date is always selectable
109 |     this.setState({ focused: true });
110 |   }
111 | 
112 |   render() {
113 |     const { showInput } = this.props;
114 |     const { focused, date } = this.state;
115 | 
116 |     const props = omit(this.props, [
117 |       'autoFocus',
118 |       'initialDate',
119 |       'showInput',
120 |     ]);
121 | 
122 |     const dateString = date && date.format('YYYY-MM-DD');
123 | 
124 |     return (
125 |       <div>
126 |         {showInput && (
127 |           <div style={{ marginBottom: 16 }}>
128 |             <input type="text" name="start date" value={dateString || ''} readOnly />
129 |           </div>
130 |         )}
131 | 
132 |         <DayPickerSingleDateController
133 |           {...props}
134 |           onDateChange={this.onDateChange}
135 |           onFocusChange={this.onFocusChange}
136 |           focused={focused}
137 |           date={date}
138 |         />
139 |       </div>
140 |     );
141 |   }
142 | }
143 | 
144 | DayPickerSingleDateControllerWrapper.propTypes = propTypes;
145 | DayPickerSingleDateControllerWrapper.defaultProps = defaultProps;
146 | 
147 | export default DayPickerSingleDateControllerWrapper;
148 | 


--------------------------------------------------------------------------------
/examples/PresetDateRangePicker.jsx:
--------------------------------------------------------------------------------
  1 | import React from 'react';
  2 | import PropTypes from 'prop-types';
  3 | import momentPropTypes from 'react-moment-proptypes';
  4 | import moment from 'moment';
  5 | import omit from 'lodash/omit';
  6 | 
  7 | import { withStyles, withStylesPropTypes, css } from 'react-with-styles';
  8 | 
  9 | import DateRangePicker from '../src/components/DateRangePicker';
 10 | 
 11 | import { DateRangePickerPhrases } from '../src/defaultPhrases';
 12 | import DateRangePickerShape from '../src/shapes/DateRangePickerShape';
 13 | import { START_DATE, END_DATE, HORIZONTAL_ORIENTATION, ANCHOR_LEFT } from '../src/constants';
 14 | import isSameDay from '../src/utils/isSameDay';
 15 | 
 16 | const propTypes = {
 17 |   ...withStylesPropTypes,
 18 | 
 19 |   // example props for the demo
 20 |   autoFocus: PropTypes.bool,
 21 |   autoFocusEndDate: PropTypes.bool,
 22 |   initialStartDate: momentPropTypes.momentObj,
 23 |   initialEndDate: momentPropTypes.momentObj,
 24 |   presets: PropTypes.arrayOf(PropTypes.shape({
 25 |     text: PropTypes.string,
 26 |     start: momentPropTypes.momentObj,
 27 |     end: momentPropTypes.momentObj,
 28 |   })),
 29 | 
 30 |   ...omit(DateRangePickerShape, [
 31 |     'startDate',
 32 |     'endDate',
 33 |     'onDatesChange',
 34 |     'focusedInput',
 35 |     'onFocusChange',
 36 |   ]),
 37 | };
 38 | 
 39 | const defaultProps = {
 40 |   // example props for the demo
 41 |   autoFocus: false,
 42 |   autoFocusEndDate: false,
 43 |   initialStartDate: null,
 44 |   initialEndDate: null,
 45 |   presets: [],
 46 | 
 47 |   // input related props
 48 |   startDateId: START_DATE,
 49 |   startDatePlaceholderText: 'Start Date',
 50 |   endDateId: END_DATE,
 51 |   endDatePlaceholderText: 'End Date',
 52 |   disabled: false,
 53 |   required: false,
 54 |   screenReaderInputMessage: '',
 55 |   showClearDates: false,
 56 |   showDefaultInputIcon: false,
 57 |   customInputIcon: null,
 58 |   customArrowIcon: null,
 59 |   customCloseIcon: null,
 60 | 
 61 |   // calendar presentation and interaction related props
 62 |   renderMonthText: null,
 63 |   orientation: HORIZONTAL_ORIENTATION,
 64 |   anchorDirection: ANCHOR_LEFT,
 65 |   horizontalMargin: 0,
 66 |   withPortal: false,
 67 |   withFullScreenPortal: false,
 68 |   initialVisibleMonth: null,
 69 |   numberOfMonths: 2,
 70 |   keepOpenOnDateSelect: false,
 71 |   reopenPickerOnClearDates: false,
 72 |   isRTL: false,
 73 | 
 74 |   // navigation related props
 75 |   navPrev: null,
 76 |   navNext: null,
 77 |   onPrevMonthClick() {},
 78 |   onNextMonthClick() {},
 79 |   onClose() {},
 80 | 
 81 |   // day presentation and interaction related props
 82 |   renderDayContents: null,
 83 |   minimumNights: 0,
 84 |   enableOutsideDays: false,
 85 |   isDayBlocked: () => false,
 86 |   isOutsideRange: day => false,
 87 |   isDayHighlighted: () => false,
 88 | 
 89 |   // internationalization
 90 |   displayFormat: () => moment.localeData().longDateFormat('L'),
 91 |   monthFormat: 'MMMM YYYY',
 92 |   phrases: DateRangePickerPhrases,
 93 | };
 94 | 
 95 | class DateRangePickerWrapper extends React.Component {
 96 |   constructor(props) {
 97 |     super(props);
 98 | 
 99 |     let focusedInput = null;
100 |     if (props.autoFocus) {
101 |       focusedInput = START_DATE;
102 |     } else if (props.autoFocusEndDate) {
103 |       focusedInput = END_DATE;
104 |     }
105 | 
106 |     this.state = {
107 |       focusedInput,
108 |       startDate: props.initialStartDate,
109 |       endDate: props.initialEndDate,
110 |     };
111 | 
112 |     this.onDatesChange = this.onDatesChange.bind(this);
113 |     this.onFocusChange = this.onFocusChange.bind(this);
114 |     this.renderDatePresets = this.renderDatePresets.bind(this);
115 |   }
116 | 
117 |   onDatesChange({ startDate, endDate }) {
118 |     this.setState({ startDate, endDate });
119 |   }
120 | 
121 |   onFocusChange(focusedInput) {
122 |     this.setState({ focusedInput });
123 |   }
124 | 
125 |   renderDatePresets() {
126 |     const { presets, styles } = this.props;
127 |     const { startDate, endDate } = this.state;
128 | 
129 |     return (
130 |       <div {...css(styles.PresetDateRangePicker_panel)}>
131 |         {presets.map(({ text, start, end }) => {
132 |           const isSelected = isSameDay(start, startDate) && isSameDay(end, endDate);
133 |           return (
134 |             <button
135 |               key={text}
136 |               {...css(
137 |                 styles.PresetDateRangePicker_button,
138 |                 isSelected && styles.PresetDateRangePicker_button__selected,
139 |               )}
140 |               type="button"
141 |               onClick={() => this.onDatesChange({ startDate: start, endDate: end })}
142 |             >
143 |               {text}
144 |             </button>
145 |           );
146 |         })}
147 |       </div>
148 |     );
149 |   }
150 | 
151 |   render() {
152 |     const { focusedInput, startDate, endDate } = this.state;
153 | 
154 |     // autoFocus, autoFocusEndDate, initialStartDate and initialEndDate are helper props for the
155 |     // example wrapper but are not props on the SingleDatePicker itself and
156 |     // thus, have to be omitted.
157 |     const props = omit(this.props, [
158 |       'autoFocus',
159 |       'autoFocusEndDate',
160 |       'initialStartDate',
161 |       'initialEndDate',
162 |       'presets',
163 |     ]);
164 | 
165 |     return (
166 |       <div>
167 |         <DateRangePicker
168 |           {...props}
169 |           renderCalendarInfo={this.renderDatePresets}
170 |           onDatesChange={this.onDatesChange}
171 |           onFocusChange={this.onFocusChange}
172 |           focusedInput={focusedInput}
173 |           startDate={startDate}
174 |           endDate={endDate}
175 |         />
176 |       </div>
177 |     );
178 |   }
179 | }
180 | 
181 | DateRangePickerWrapper.propTypes = propTypes;
182 | DateRangePickerWrapper.defaultProps = defaultProps;
183 | 
184 | export default withStyles(({ reactDates: { color } }) => ({
185 |   PresetDateRangePicker_panel: {
186 |     padding: '0 22px 11px 22px',
187 |   },
188 | 
189 |   PresetDateRangePicker_button: {
190 |     position: 'relative',
191 |     height: '100%',
192 |     textAlign: 'center',
193 |     background: 'none',
194 |     border: `2px solid ${color.core.primary}`,
195 |     color: color.core.primary,
196 |     padding: '4px 12px',
197 |     marginRight: 8,
198 |     font: 'inherit',
199 |     fontWeight: 700,
200 |     lineHeight: 'normal',
201 |     overflow: 'visible',
202 |     boxSizing: 'border-box',
203 |     cursor: 'pointer',
204 | 
205 |     ':active': {
206 |       outline: 0,
207 |     },
208 |   },
209 | 
210 |   PresetDateRangePicker_button__selected: {
211 |     color: color.core.white,
212 |     background: color.core.primary,
213 |   },
214 | }))(DateRangePickerWrapper);
215 | 


--------------------------------------------------------------------------------
/examples/SingleDatePickerWrapper.jsx:
--------------------------------------------------------------------------------
  1 | import React from 'react';
  2 | import PropTypes from 'prop-types';
  3 | import momentPropTypes from 'react-moment-proptypes';
  4 | import moment from 'moment';
  5 | import omit from 'lodash/omit';
  6 | 
  7 | import SingleDatePicker from '../src/components/SingleDatePicker';
  8 | 
  9 | import { SingleDatePickerPhrases } from '../src/defaultPhrases';
 10 | import SingleDatePickerShape from '../src/shapes/SingleDatePickerShape';
 11 | import { HORIZONTAL_ORIENTATION, ANCHOR_LEFT, OPEN_DOWN } from '../src/constants';
 12 | import isInclusivelyAfterDay from '../src/utils/isInclusivelyAfterDay';
 13 | 
 14 | const propTypes = {
 15 |   // example props for the demo
 16 |   autoFocus: PropTypes.bool,
 17 |   initialDate: momentPropTypes.momentObj,
 18 | 
 19 |   ...omit(SingleDatePickerShape, [
 20 |     'date',
 21 |     'onDateChange',
 22 |     'focused',
 23 |     'onFocusChange',
 24 |   ]),
 25 | };
 26 | 
 27 | const defaultProps = {
 28 |   // example props for the demo
 29 |   autoFocus: false,
 30 |   initialDate: null,
 31 | 
 32 |   // input related props
 33 |   id: 'date',
 34 |   placeholder: 'Date',
 35 |   disabled: false,
 36 |   required: false,
 37 |   screenReaderInputMessage: '',
 38 |   showClearDate: false,
 39 |   showDefaultInputIcon: false,
 40 |   customInputIcon: null,
 41 |   block: false,
 42 |   small: false,
 43 |   regular: false,
 44 |   verticalSpacing: undefined,
 45 |   keepFocusOnInput: false,
 46 |   autoComplete: 'off',
 47 | 
 48 |   // calendar presentation and interaction related props
 49 |   renderMonthText: null,
 50 |   orientation: HORIZONTAL_ORIENTATION,
 51 |   anchorDirection: ANCHOR_LEFT,
 52 |   horizontalMargin: 0,
 53 |   withPortal: false,
 54 |   withFullScreenPortal: false,
 55 |   initialVisibleMonth: null,
 56 |   numberOfMonths: 2,
 57 |   keepOpenOnDateSelect: false,
 58 |   reopenPickerOnClearDate: false,
 59 |   isRTL: false,
 60 |   openDirection: OPEN_DOWN,
 61 | 
 62 |   // navigation related props
 63 |   navPrev: null,
 64 |   navNext: null,
 65 |   onPrevMonthClick() {},
 66 |   onNextMonthClick() {},
 67 |   onClose() {},
 68 | 
 69 |   // day presentation and interaction related props
 70 |   renderCalendarDay: undefined,
 71 |   renderDayContents: null,
 72 |   enableOutsideDays: false,
 73 |   isDayBlocked: () => false,
 74 |   isOutsideRange: day => !isInclusivelyAfterDay(day, moment()),
 75 |   isDayHighlighted: () => {},
 76 | 
 77 |   // internationalization props
 78 |   displayFormat: () => moment.localeData().longDateFormat('L'),
 79 |   monthFormat: 'MMMM YYYY',
 80 |   phrases: SingleDatePickerPhrases,
 81 | };
 82 | 
 83 | class SingleDatePickerWrapper extends React.Component {
 84 |   constructor(props) {
 85 |     super(props);
 86 |     this.state = {
 87 |       focused: props.autoFocus,
 88 |       date: props.initialDate,
 89 |     };
 90 | 
 91 |     this.onDateChange = this.onDateChange.bind(this);
 92 |     this.onFocusChange = this.onFocusChange.bind(this);
 93 |   }
 94 | 
 95 |   onDateChange(date) {
 96 |     this.setState({ date });
 97 |   }
 98 | 
 99 |   onFocusChange({ focused }) {
100 |     this.setState({ focused });
101 |   }
102 | 
103 |   render() {
104 |     const { focused, date } = this.state;
105 | 
106 |     // autoFocus and initialDate are helper props for the example wrapper but are not
107 |     // props on the SingleDatePicker itself and thus, have to be omitted.
108 |     const props = omit(this.props, [
109 |       'autoFocus',
110 |       'initialDate',
111 |     ]);
112 | 
113 |     return (
114 |       <SingleDatePicker
115 |         {...props}
116 |         id="date_input"
117 |         date={date}
118 |         focused={focused}
119 |         onDateChange={this.onDateChange}
120 |         onFocusChange={this.onFocusChange}
121 |       />
122 |     );
123 |   }
124 | }
125 | 
126 | SingleDatePickerWrapper.propTypes = propTypes;
127 | SingleDatePickerWrapper.defaultProps = defaultProps;
128 | 
129 | export default SingleDatePickerWrapper;
130 | 


--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/no-unresolved
2 | module.exports = require('./lib');
3 | 


--------------------------------------------------------------------------------
/initialize.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/no-unresolved
2 | require('./lib/initialize');
3 | 


--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
 1 | /* eslint no-param-reassign:0, import/no-extraneous-dependencies:0 */
 2 | const path = require('path');
 3 | const webpack = require('webpack');
 4 | 
 5 | module.exports = (config) => {
 6 |   config.set({
 7 |     basePath: '',
 8 | 
 9 |     frameworks: ['mocha', 'sinon', 'chai'],
10 | 
11 |     files: ['test/browser-main.js'],
12 | 
13 |     webpack: {
14 |       mode: 'development',
15 |       externals: {
16 |         sinon: true,
17 |       },
18 |       plugins: [
19 |         // https://github.com/cheeriojs/cheerio/issues/836
20 |         new webpack.NormalModuleReplacementPlugin(/^\.\/package$/, (result) => {
21 |           if (/cheerio/.test(result.context)) {
22 |             result.request = './package.json';
23 |           }
24 |         }),
25 |       ],
26 |       module: {
27 |         rules: [
28 |           {
29 |             test: /\.jsx?$/,
30 |             loader: 'babel-loader',
31 |             include: [
32 |               path.join(__dirname, 'src'),
33 |               path.join(__dirname, 'test'),
34 |               require.resolve('airbnb-js-shims'),
35 |             ],
36 |             options: {
37 |               presets: [
38 |                 // setting modules to false so it does not transform twice
39 |                 ['airbnb', { modules: false }],
40 |                 // transform to cjs so sinon can stub properly
41 |                 ['@babel/preset-env', { modules: 'cjs' }],
42 |               ],
43 |             },
44 |           },
45 | 
46 |           // Inject the Airbnb shims into the bundle
47 |           { test: /test\/_helpers/, loader: 'imports-loader?shims=airbnb-js-shims' },
48 |         ],
49 |       },
50 |       resolve: {
51 |         extensions: ['.js', '.jsx'],
52 |       },
53 |     },
54 | 
55 |     webpackMiddleware: {
56 |       progress: false,
57 |       stats: false,
58 |       debug: false,
59 |       quiet: true,
60 |     },
61 | 
62 |     preprocessors: {
63 |       'test/**/*': ['webpack'],
64 |     },
65 | 
66 |     reporters: ['progress'],
67 | 
68 |     port: 9876,
69 | 
70 |     colors: true,
71 | 
72 |     logLevel: config.LOG_INFO,
73 | 
74 |     autoWatch: false,
75 | 
76 |     browsers: ['Firefox'],
77 | 
78 |     singleRun: true,
79 | 
80 |     concurrency: Infinity,
81 |   });
82 | };
83 | 


--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
  1 | {
  2 |   "name": "react-dates",
  3 |   "version": "21.8.0",
  4 |   "description": "A responsive and accessible date range picker component built with React",
  5 |   "main": "index.js",
  6 |   "scripts": {
  7 |     "prebuild": "npm run clean",
  8 |     "build": "npm run build:cjs && npm run build:esm && npm run build:css -- --optimize",
  9 |     "postbuild": "npm run build:test",
 10 |     "build:cjs": "BABEL_ENV=cjs babel src/ -d lib/",
 11 |     "build:esm": "BABEL_ENV=esm babel src/ -d esm/",
 12 |     "build:test": "BABEL_ENV=test babel test/ -d test-build/",
 13 |     "prebuild:css": "rimraf lib/css && mkdirp lib/css",
 14 |     "build:css": "node scripts/buildCSS.js",
 15 |     "clean": "rimraf lib esm test-build",
 16 |     "lint": "eslint --ext .js,.jsx src test",
 17 |     "mocha": "mocha ./test-build/_helpers",
 18 |     "storybook:uninstall": "npm uninstall --no-save @storybook/react && rimraf node_modules/@storybook node_modules/react-modal node_modules/react-dom-factories",
 19 |     "react": "NPM_CONFIG_LEGACY_PEER_DEPS=true enzyme-adapter-react-install 16",
 20 |     "pretest": "npm run --silent lint",
 21 |     "tests-only": "cross-env NODE_ENV=test nyc npm run mocha --silent",
 22 |     "pretests-karma": "npm run react",
 23 |     "tests-karma": "karma start",
 24 |     "test": "npm run react && npm run build && npm run build:test && npm run tests-only",
 25 |     "storybook": "start-storybook -p 6006",
 26 |     "storybook:css": "npm run build:css && start-storybook -p 6006 -c .storybook-css",
 27 |     "tag": "git tag v$npm_package_version",
 28 |     "gh-pages:clean": "rimraf _gh-pages",
 29 |     "gh-pages:build": "$(npm bin)/build-storybook -o _gh-pages",
 30 |     "gh-pages:publish": "$(npm bin)/git-directory-deploy --directory _gh-pages",
 31 |     "gh-pages": "npm run gh-pages:clean && npm run gh-pages:build && npm run gh-pages:publish",
 32 |     "version:patch": "npm --no-git-tag-version version patch",
 33 |     "version:minor": "npm --no-git-tag-version version minor",
 34 |     "version:major": "npm --no-git-tag-version version major",
 35 |     "preversion": "npm run test && npm run check-changelog && npm run check-only-changelog-changed",
 36 |     "postversion": "git commit package.json CHANGELOG.md -m \"Version $npm_package_version\" && npm run tag && git push && git push --tags && npm publish --registry=https://registry.npmjs.org/",
 37 |     "prepublish": "in-publish && safe-publish-latest && npm run build || not-in-publish",
 38 |     "postpublish": "[ \"${npm_config_tag:-latest}\" != latest ] || npm run gh-pages",
 39 |     "check-changelog": "expr $(git status --porcelain 2>/dev/null| grep \"^\\s*M.*CHANGELOG.md\" | wc -l) >/dev/null || (echo 'Please edit CHANGELOG.md' && exit 1)",
 40 |     "check-only-changelog-changed": "(expr $(git status --porcelain 2>/dev/null| grep -v \"CHANGELOG.md\" | wc -l) >/dev/null && echo 'Only CHANGELOG.md may have uncommitted changes' && exit 1) || exit 0"
 41 |   },
 42 |   "repository": {
 43 |     "type": "git",
 44 |     "url": "git+https://github.com/react-dates/react-dates.git"
 45 |   },
 46 |   "author": "Maja Wichrowska <maja.wichrowska@airbnb.com>",
 47 |   "license": "MIT",
 48 |   "bugs": {
 49 |     "url": "https://github.com/react-dates/react-dates/issues"
 50 |   },
 51 |   "homepage": "https://github.com/react-dates/react-dates#readme",
 52 |   "devDependencies": {
 53 |     "@babel/cli": "^7.16.8",
 54 |     "@babel/core": "^7.16.12",
 55 |     "@babel/eslint-parser": "^7.16.5",
 56 |     "@babel/register": "^7.16.9",
 57 |     "@babel/runtime": "^7.16.7",
 58 |     "@storybook/addon-actions": "^5.3.21",
 59 |     "@storybook/addon-info": "^5.3.21",
 60 |     "@storybook/addon-links": "^5.3.21",
 61 |     "@storybook/addon-options": "^5.3.21",
 62 |     "@storybook/addons": "^5.3.21",
 63 |     "@storybook/react": "^5.3.21",
 64 |     "@welldone-software/why-did-you-render": "^3.6.0",
 65 |     "airbnb-js-shims": "^2.2.1",
 66 |     "aphrodite": "^2.4.0",
 67 |     "babel-loader": "^8.2.2",
 68 |     "babel-plugin-import-path-replace": "^0.1.0",
 69 |     "babel-plugin-inline-react-svg": "^1.1.2",
 70 |     "babel-plugin-inline-svg": "^1.2.0",
 71 |     "babel-plugin-istanbul": "^5.2.0",
 72 |     "babel-plugin-transform-replace-object-assign": "^2.0.0",
 73 |     "babel-preset-airbnb": "^4.5.0",
 74 |     "chai": "^4.2.0",
 75 |     "cheerio": "=1.0.0-rc.3",
 76 |     "clean-css": "^4.2.3",
 77 |     "cross-env": "^5.2.1",
 78 |     "enzyme": "^3.11.0",
 79 |     "enzyme-adapter-react-helper": "^1.3.9",
 80 |     "eslint": "^8.7.0",
 81 |     "eslint-config-airbnb": "^19.0.4",
 82 |     "eslint-plugin-import": "^2.25.4",
 83 |     "eslint-plugin-jsx-a11y": "^6.5.1",
 84 |     "eslint-plugin-react": "^7.28.0",
 85 |     "eslint-plugin-react-hooks": "^4.3.0",
 86 |     "eslint-plugin-react-with-styles": "^2.4.0",
 87 |     "git-directory-deploy": "^1.5.1",
 88 |     "imports-loader": "^0.8.0",
 89 |     "in-publish": "^2.0.1",
 90 |     "karma": "^6.3.11",
 91 |     "karma-chai": "^0.1.0",
 92 |     "karma-firefox-launcher": "^1.2.0",
 93 |     "karma-mocha": "^2.0.1",
 94 |     "karma-sinon": "^1.0.5",
 95 |     "karma-webpack": "^4.0.2",
 96 |     "mkdirp": "^0.5.5",
 97 |     "mocha": "^3.5.3",
 98 |     "mocha-wrap": "^2.1.2",
 99 |     "moment": "^2.29.1",
100 |     "moment-jalaali": "^0.7.4",
101 |     "nyc": "^10.3.2",
102 |     "raw-loader": "^0.5.1",
103 |     "react": "^0.14 || ^15.5.4 || ^16.1.1",
104 |     "react-dom": "^0.14 || ^15.5.4 || ^16.1.1",
105 |     "react-with-styles-interface-aphrodite": "^6.0.1",
106 |     "react-with-styles-interface-css-compiler": "^2.2.0",
107 |     "rimraf": "^2.7.1",
108 |     "safe-publish-latest": "^1.1.4",
109 |     "sass-loader": "^7.3.1",
110 |     "sinon": "^7.5.0",
111 |     "sinon-sandbox": "^2.0.6",
112 |     "style-loader": "^0.20.3",
113 |     "typescript": "*",
114 |     "webpack": "^4.46.0"
115 |   },
116 |   "dependencies": {
117 |     "airbnb-prop-types": "^2.16.0",
118 |     "color2k": "~1.1.1",
119 |     "consolidated-events": "^1.1.1 || ^2.0.0",
120 |     "enzyme-shallow-equal": "^1.0.4",
121 |     "is-touch-device": "^1.0.1",
122 |     "lodash": "^4.1.1",
123 |     "object.assign": "^4.1.2",
124 |     "object.values": "^1.1.5",
125 |     "prop-types": "^15.7.2",
126 |     "raf": "^3.4.1",
127 |     "react-moment-proptypes": "^1.8.1",
128 |     "react-outside-click-handler": "^1.3.0",
129 |     "react-portal": "^4.2.1",
130 |     "react-with-direction": "^1.4.0",
131 |     "react-with-styles": "~4.1.0",
132 |     "react-with-styles-interface-css": "^6.0.0"
133 |   },
134 |   "peerDependencies": {
135 |     "@babel/runtime": "^7.0.0",
136 |     "moment": "^2.18.1",
137 |     "react": "^0.14 || ^15.5.4 || ^16.1.1",
138 |     "react-dom": "^0.14 || ^15.5.4 || ^16.1.1"
139 |   },
140 |   "engines": {
141 |     "node": ">=0.10"
142 |   }
143 | }
144 | 


--------------------------------------------------------------------------------
/react-dates-demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/react-dates/react-dates/b7bad38dcff024d374ee98f972b55b3de9e61289/react-dates-demo.gif


--------------------------------------------------------------------------------
/scripts/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 |   "rules": {
3 |     "import/no-extraneous-dependencies": [2, {
4 |       "devDependencies": true
5 |     }],
6 |   }
7 | }
8 | 


--------------------------------------------------------------------------------
/scripts/buildCSS.js:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env node
 2 | 
 3 | const fs = require('fs');
 4 | const CleanCSS = require('clean-css');
 5 | 
 6 | const compileCSS = require('react-with-styles-interface-css-compiler');
 7 | 
 8 | const registerMaxSpecificity = require('react-with-styles-interface-css/dist/utils/registerMaxSpecificity').default;
 9 | const registerCSSInterfaceWithDefaultTheme = require('../src/utils/registerCSSInterfaceWithDefaultTheme').default;
10 | 
11 | console.error = msg => { throw new SyntaxError(msg); };
12 | console.warn = msg => { throw new SyntaxError(msg); };
13 | 
14 | const args = process.argv.slice(2);
15 | const optimizeForProduction = args.includes('-o') || args.includes('--optimize');
16 | 
17 | registerMaxSpecificity(0);
18 | registerCSSInterfaceWithDefaultTheme();
19 | 
20 | const path = './scripts/renderAllComponents.jsx';
21 | const CSS = compileCSS(path);
22 | 
23 | if (CSS === '') {
24 |   throw new Error('Failed to compile CSS');
25 | } else {
26 |   console.log('CSS compilation complete.');
27 | }
28 | 
29 | const format = new CleanCSS({
30 |   level: optimizeForProduction ? 2 : 0,
31 |   format: 'beautify',
32 |   inline: ['none'],
33 | });
34 | const { styles: formattedCSS } = format.minify(CSS);
35 | 
36 | const outputFilePath = optimizeForProduction ? './lib/css/_datepicker.css' : './css/styles.css';
37 | fs.writeFileSync(outputFilePath, formattedCSS, 'utf8');
38 | 


--------------------------------------------------------------------------------
/scripts/pure-component-fallback.js:
--------------------------------------------------------------------------------
 1 | module.exports = function pureComponentFallback({ types: t, template }) {
 2 |   const buildPureOrNormalSuperclass = () => t.LogicalExpression('||',
 3 |     t.memberExpression(t.identifier('React'), t.identifier('PureComponent')),
 4 |     t.memberExpression(t.identifier('React'), t.identifier('Component')));
 5 | 
 6 |   const buildShouldComponentUpdate = () => {
 7 |     const method = t.classMethod(
 8 |       'method',
 9 |       t.logicalExpression(
10 |         '&&',
11 |         t.unaryExpression('!', t.memberExpression(t.identifier('React'), t.identifier('PureComponent'))),
12 |         t.stringLiteral('shouldComponentUpdate')
13 |       ),
14 |       [t.identifier('nextProps'), t.identifier('nextState')],
15 |       t.blockStatement([template('return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState);')()]),
16 |       true
17 |     );
18 |     return method;
19 |   };
20 | 
21 |   function superclassIsPureComponent(path) {
22 |     const superclass = path.get('superClass');
23 |     // check for PureComponent
24 |     if (t.isIdentifier(superclass)) {
25 |       return superclass.node.name === 'PureComponent';
26 |     }
27 | 
28 |     if (t.isMemberExpression(superclass)) {
29 |       // Check for React.PureComponent
30 |       return superclass.get('object').node.name === 'React'
31 |         && superclass.get('property').node.name === 'PureComponent';
32 |     }
33 | 
34 |     return false;
35 |   }
36 | 
37 |   return {
38 |     visitor: {
39 |       Program: {
40 |         exit({ node }, { file }) {
41 |           if (file.get('addShallowEqualImport')) {
42 |             const shallowEqualImportDeclaration = t.importDeclaration([
43 |               t.importDefaultSpecifier(t.identifier('shallowEqual')),
44 |             ], t.stringLiteral('enzyme-shallow-equal'));
45 |             node.body.unshift(shallowEqualImportDeclaration);
46 |           }
47 |         },
48 |       },
49 | 
50 |       ClassDeclaration(path, { file }) {
51 |         if (superclassIsPureComponent(path)) {
52 |           const superclassPath = path.get('superClass');
53 | 
54 |           // Replace the superclass
55 |           superclassPath.replaceWith(buildPureOrNormalSuperclass());
56 | 
57 |           // Only add an SCU if one doesn't already exist
58 |           const existingSCU = path.get('body').get('body').find((
59 |             p => p.isClassMethod() && p.get('key').node.name === 'shouldComponentUpdate'
60 |           ));
61 | 
62 |           if (!existingSCU) {
63 |             file.set('addShallowEqualImport', true);
64 |             path.get('body').unshiftContainer('body', buildShouldComponentUpdate());
65 |           }
66 |         }
67 |       },
68 |     },
69 |   };
70 | };
71 | 


--------------------------------------------------------------------------------
/scripts/renderAllComponents.jsx:
--------------------------------------------------------------------------------
 1 | import React from 'react';
 2 | import ReactDOM from 'react-dom';
 3 | 
 4 | import DateRangePickerWrapper from '../examples/DateRangePickerWrapper';
 5 | import SingleDatePickerWrapper from '../examples/SingleDatePickerWrapper';
 6 | import PresetDateRangePickerWrapper from '../examples/PresetDateRangePicker';
 7 | 
 8 | if (!document.getElementById('root')) {
 9 |   // Make sure the #root element is defined
10 |   const root = document.createElement('div');
11 |   root.id = 'root';
12 |   document.body.appendChild(root);
13 | }
14 | 
15 | function App() {
16 |   return (
17 |     <div>
18 |       <DateRangePickerWrapper autoFocus />
19 |       <SingleDatePickerWrapper autoFocus />
20 |       <PresetDateRangePickerWrapper autoFocus />
21 |     </div>
22 |   );
23 | }
24 | 
25 | ReactDOM.render(<App />, document.querySelector('#root'));
26 | 


--------------------------------------------------------------------------------
/src/components/CalendarIcon.jsx:
--------------------------------------------------------------------------------
1 | import CalendarIcon from '../svg/calendar.svg';
2 | 
3 | export default CalendarIcon;
4 | 


--------------------------------------------------------------------------------
/src/components/CalendarWeek.jsx:
--------------------------------------------------------------------------------
 1 | import React from 'react';
 2 | import PropTypes from 'prop-types';
 3 | import { forbidExtraProps } from 'airbnb-prop-types';
 4 | 
 5 | const propTypes = forbidExtraProps({
 6 |   children: PropTypes.node.isRequired,
 7 | });
 8 | 
 9 | export default function CalendarWeek({ children }) {
10 |   return (
11 |     <tr>
12 |       {children}
13 |     </tr>
14 |   );
15 | }
16 | 
17 | CalendarWeek.propTypes = propTypes;
18 | 


--------------------------------------------------------------------------------
/src/components/ChevronDown.jsx:
--------------------------------------------------------------------------------
1 | import ChevronDown from '../svg/chevron-down.svg';
2 | 
3 | export default ChevronDown;
4 | 


--------------------------------------------------------------------------------
/src/components/ChevronUp.jsx:
--------------------------------------------------------------------------------
1 | import ChevronUp from '../svg/chevron-up.svg';
2 | 
3 | export default ChevronUp;
4 | 


--------------------------------------------------------------------------------
/src/components/CloseButton.jsx:
--------------------------------------------------------------------------------
1 | import CloseButton from '../svg/close.svg';
2 | 
3 | export default CloseButton;
4 | 


--------------------------------------------------------------------------------
/src/components/KeyboardShortcutRow.jsx:
--------------------------------------------------------------------------------
 1 | import React from 'react';
 2 | import PropTypes from 'prop-types';
 3 | import { forbidExtraProps } from 'airbnb-prop-types';
 4 | import { withStyles, withStylesPropTypes } from 'react-with-styles';
 5 | 
 6 | const propTypes = forbidExtraProps({
 7 |   ...withStylesPropTypes,
 8 |   unicode: PropTypes.string.isRequired,
 9 |   label: PropTypes.string.isRequired,
10 |   action: PropTypes.string.isRequired,
11 |   block: PropTypes.bool,
12 | });
13 | 
14 | const defaultProps = {
15 |   block: false,
16 | };
17 | 
18 | function KeyboardShortcutRow({
19 |   unicode,
20 |   label,
21 |   action,
22 |   block,
23 |   css,
24 |   styles,
25 | }) {
26 |   return (
27 |     <li
28 |       {...css(
29 |         styles.KeyboardShortcutRow,
30 |         block && styles.KeyboardShortcutRow__block,
31 |       )}
32 |     >
33 |       <div
34 |         {...css(
35 |           styles.KeyboardShortcutRow_keyContainer,
36 |           block && styles.KeyboardShortcutRow_keyContainer__block,
37 |         )}
38 |       >
39 |         <span
40 |           {...css(styles.KeyboardShortcutRow_key)}
41 |           role="img"
42 |           aria-label={`${label},`} // add comma so screen readers will pause before reading action
43 |         >
44 |           {unicode}
45 |         </span>
46 |       </div>
47 | 
48 |       <div {...css(styles.KeyboardShortcutRow_action)}>
49 |         {action}
50 |       </div>
51 |     </li>
52 |   );
53 | }
54 | 
55 | KeyboardShortcutRow.propTypes = propTypes;
56 | KeyboardShortcutRow.defaultProps = defaultProps;
57 | 
58 | export default withStyles(({ reactDates: { color } }) => ({
59 |   KeyboardShortcutRow: {
60 |     listStyle: 'none',
61 |     margin: '6px 0',
62 |   },
63 | 
64 |   KeyboardShortcutRow__block: {
65 |     marginBottom: 16,
66 |   },
67 | 
68 |   KeyboardShortcutRow_keyContainer: {
69 |     display: 'inline-block',
70 |     whiteSpace: 'nowrap',
71 |     textAlign: 'right', // is not handled by isRTL
72 |     marginRight: 6, // is not handled by isRTL
73 |   },
74 | 
75 |   KeyboardShortcutRow_keyContainer__block: {
76 |     textAlign: 'left', // is not handled by isRTL
77 |     display: 'inline',
78 |   },
79 | 
80 |   KeyboardShortcutRow_key: {
81 |     fontFamily: 'monospace',
82 |     fontSize: 12,
83 |     textTransform: 'uppercase',
84 |     background: color.core.grayLightest,
85 |     padding: '2px 6px',
86 |   },
87 | 
88 |   KeyboardShortcutRow_action: {
89 |     display: 'inline',
90 |     wordBreak: 'break-word',
91 |     marginLeft: 8, // is not handled by isRTL
92 |   },
93 | }), { pureComponent: typeof React.PureComponent !== 'undefined' })(KeyboardShortcutRow);
94 | 


--------------------------------------------------------------------------------
/src/components/LeftArrow.jsx:
--------------------------------------------------------------------------------
1 | import LeftArrow from '../svg/arrow-left.svg';
2 | 
3 | export default LeftArrow;
4 | 


--------------------------------------------------------------------------------
/src/components/RightArrow.jsx:
--------------------------------------------------------------------------------
1 | import RightArrow from '../svg/arrow-right.svg';
2 | 
3 | export default RightArrow;
4 | 


--------------------------------------------------------------------------------
/src/constants.js:
--------------------------------------------------------------------------------
 1 | export const DISPLAY_FORMAT = 'L';
 2 | export const ISO_FORMAT = 'YYYY-MM-DD';
 3 | export const ISO_MONTH_FORMAT = 'YYYY-MM'; // TODO delete this line of dead code on next breaking change
 4 | 
 5 | export const START_DATE = 'startDate';
 6 | export const END_DATE = 'endDate';
 7 | 
 8 | export const HORIZONTAL_ORIENTATION = 'horizontal';
 9 | export const VERTICAL_ORIENTATION = 'vertical';
10 | export const VERTICAL_SCROLLABLE = 'verticalScrollable';
11 | 
12 | export const NAV_POSITION_BOTTOM = 'navPositionBottom';
13 | export const NAV_POSITION_TOP = 'navPositionTop';
14 | 
15 | export const ICON_BEFORE_POSITION = 'before';
16 | export const ICON_AFTER_POSITION = 'after';
17 | 
18 | export const INFO_POSITION_TOP = 'top';
19 | export const INFO_POSITION_BOTTOM = 'bottom';
20 | export const INFO_POSITION_BEFORE = 'before';
21 | export const INFO_POSITION_AFTER = 'after';
22 | 
23 | export const ANCHOR_LEFT = 'left';
24 | export const ANCHOR_RIGHT = 'right';
25 | 
26 | export const OPEN_DOWN = 'down';
27 | export const OPEN_UP = 'up';
28 | 
29 | export const DAY_SIZE = 39;
30 | export const BLOCKED_MODIFIER = 'blocked';
31 | export const WEEKDAYS = [0, 1, 2, 3, 4, 5, 6];
32 | 
33 | export const FANG_WIDTH_PX = 20;
34 | export const FANG_HEIGHT_PX = 10;
35 | export const DEFAULT_VERTICAL_SPACING = 22;
36 | 
37 | export const MODIFIER_KEY_NAMES = new Set(['Shift', 'Control', 'Alt', 'Meta']);
38 | 


--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
 1 | export { default as CalendarDay } from './components/CalendarDay';
 2 | export { default as CalendarMonth } from './components/CalendarMonth';
 3 | export { default as CalendarMonthGrid } from './components/CalendarMonthGrid';
 4 | export { default as DateRangePicker } from './components/DateRangePicker';
 5 | export { default as DateRangePickerInput } from './components/DateRangePickerInput';
 6 | export { default as DateRangePickerInputController } from './components/DateRangePickerInputController';
 7 | export { default as DateRangePickerShape } from './shapes/DateRangePickerShape';
 8 | export { default as DayPicker } from './components/DayPicker';
 9 | export { default as DayPickerRangeController } from './components/DayPickerRangeController';
10 | export { default as DayPickerSingleDateController } from './components/DayPickerSingleDateController';
11 | export { default as SingleDatePicker } from './components/SingleDatePicker';
12 | export { default as SingleDatePickerInput } from './components/SingleDatePickerInput';
13 | export { default as SingleDatePickerShape } from './shapes/SingleDatePickerShape';
14 | export { default as isInclusivelyAfterDay } from './utils/isInclusivelyAfterDay';
15 | export { default as isInclusivelyBeforeDay } from './utils/isInclusivelyBeforeDay';
16 | export { default as isNextDay } from './utils/isNextDay';
17 | export { default as isSameDay } from './utils/isSameDay';
18 | export { default as toISODateString } from './utils/toISODateString';
19 | export { default as toLocalizedDateString } from './utils/toLocalizedDateString';
20 | export { default as toMomentObject } from './utils/toMomentObject';
21 | 


--------------------------------------------------------------------------------
/src/initialize.js:
--------------------------------------------------------------------------------
1 | import registerCSSInterfaceWithDefaultTheme from './utils/registerCSSInterfaceWithDefaultTheme';
2 | 
3 | registerCSSInterfaceWithDefaultTheme();
4 | 


--------------------------------------------------------------------------------
/src/shapes/AnchorDirectionShape.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | 
3 | import {
4 |   ANCHOR_LEFT,
5 |   ANCHOR_RIGHT,
6 | } from '../constants';
7 | 
8 | export default PropTypes.oneOf([ANCHOR_LEFT, ANCHOR_RIGHT]);
9 | 


--------------------------------------------------------------------------------
/src/shapes/CalendarInfoPositionShape.js:
--------------------------------------------------------------------------------
 1 | import PropTypes from 'prop-types';
 2 | 
 3 | import {
 4 |   INFO_POSITION_TOP,
 5 |   INFO_POSITION_BOTTOM,
 6 |   INFO_POSITION_BEFORE,
 7 |   INFO_POSITION_AFTER,
 8 | } from '../constants';
 9 | 
10 | export default PropTypes.oneOf([
11 |   INFO_POSITION_TOP,
12 |   INFO_POSITION_BOTTOM,
13 |   INFO_POSITION_BEFORE,
14 |   INFO_POSITION_AFTER,
15 | ]);
16 | 


--------------------------------------------------------------------------------
/src/shapes/DateRangePickerShape.js:
--------------------------------------------------------------------------------
  1 | import PropTypes from 'prop-types';
  2 | import momentPropTypes from 'react-moment-proptypes';
  3 | import { mutuallyExclusiveProps, nonNegativeInteger } from 'airbnb-prop-types';
  4 | 
  5 | import { DateRangePickerPhrases } from '../defaultPhrases';
  6 | import getPhrasePropTypes from '../utils/getPhrasePropTypes';
  7 | 
  8 | import FocusedInputShape from './FocusedInputShape';
  9 | import IconPositionShape from './IconPositionShape';
 10 | import OrientationShape from './OrientationShape';
 11 | import DisabledShape from './DisabledShape';
 12 | import anchorDirectionShape from './AnchorDirectionShape';
 13 | import openDirectionShape from './OpenDirectionShape';
 14 | import DayOfWeekShape from './DayOfWeekShape';
 15 | import CalendarInfoPositionShape from './CalendarInfoPositionShape';
 16 | import NavPositionShape from './NavPositionShape';
 17 | 
 18 | export default {
 19 |   // required props for a functional interactive DateRangePicker
 20 |   startDate: momentPropTypes.momentObj,
 21 |   endDate: momentPropTypes.momentObj,
 22 |   onDatesChange: PropTypes.func.isRequired,
 23 | 
 24 |   focusedInput: FocusedInputShape,
 25 |   onFocusChange: PropTypes.func.isRequired,
 26 | 
 27 |   onClose: PropTypes.func,
 28 | 
 29 |   // input related props
 30 |   startDateId: PropTypes.string.isRequired,
 31 |   startDatePlaceholderText: PropTypes.string,
 32 |   startDateOffset: PropTypes.func,
 33 |   endDateOffset: PropTypes.func,
 34 |   endDateId: PropTypes.string.isRequired,
 35 |   endDatePlaceholderText: PropTypes.string,
 36 |   startDateAriaLabel: PropTypes.string,
 37 |   endDateAriaLabel: PropTypes.string,
 38 |   startDateTitleText: PropTypes.string,
 39 |   endDateTitleText: PropTypes.string,
 40 |   disabled: DisabledShape,
 41 |   required: PropTypes.bool,
 42 |   readOnly: PropTypes.bool,
 43 |   screenReaderInputMessage: PropTypes.string,
 44 |   showClearDates: PropTypes.bool,
 45 |   showDefaultInputIcon: PropTypes.bool,
 46 |   inputIconPosition: IconPositionShape,
 47 |   customInputIcon: PropTypes.node,
 48 |   customArrowIcon: PropTypes.node,
 49 |   customCloseIcon: PropTypes.node,
 50 |   noBorder: PropTypes.bool,
 51 |   block: PropTypes.bool,
 52 |   small: PropTypes.bool,
 53 |   regular: PropTypes.bool,
 54 |   keepFocusOnInput: PropTypes.bool,
 55 |   autoComplete: PropTypes.string,
 56 | 
 57 |   // calendar presentation and interaction related props
 58 |   renderMonthText: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'),
 59 |   renderMonthElement: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'),
 60 |   renderWeekHeaderElement: PropTypes.func,
 61 |   orientation: OrientationShape,
 62 |   anchorDirection: anchorDirectionShape,
 63 |   openDirection: openDirectionShape,
 64 |   horizontalMargin: PropTypes.number,
 65 |   withPortal: PropTypes.bool,
 66 |   withFullScreenPortal: PropTypes.bool,
 67 |   appendToBody: PropTypes.bool,
 68 |   disableScroll: PropTypes.bool,
 69 |   daySize: nonNegativeInteger,
 70 |   isRTL: PropTypes.bool,
 71 |   firstDayOfWeek: DayOfWeekShape,
 72 |   initialVisibleMonth: PropTypes.func,
 73 |   numberOfMonths: PropTypes.number,
 74 |   keepOpenOnDateSelect: PropTypes.bool,
 75 |   reopenPickerOnClearDates: PropTypes.bool,
 76 |   renderCalendarInfo: PropTypes.func,
 77 |   calendarInfoPosition: CalendarInfoPositionShape,
 78 |   hideKeyboardShortcutsPanel: PropTypes.bool,
 79 |   verticalHeight: nonNegativeInteger,
 80 |   transitionDuration: nonNegativeInteger,
 81 |   verticalSpacing: nonNegativeInteger,
 82 |   horizontalMonthPadding: nonNegativeInteger,
 83 | 
 84 |   // navigation related props
 85 |   dayPickerNavigationInlineStyles: PropTypes.object,
 86 |   navPosition: NavPositionShape,
 87 |   navPrev: PropTypes.node,
 88 |   navNext: PropTypes.node,
 89 |   renderNavPrevButton: PropTypes.func,
 90 |   renderNavNextButton: PropTypes.func,
 91 |   onPrevMonthClick: PropTypes.func,
 92 |   onNextMonthClick: PropTypes.func,
 93 | 
 94 |   // day presentation and interaction related props
 95 |   renderCalendarDay: PropTypes.func,
 96 |   renderDayContents: PropTypes.func,
 97 |   minimumNights: PropTypes.number,
 98 |   minDate: momentPropTypes.momentObj,
 99 |   maxDate: momentPropTypes.momentObj,
100 |   enableOutsideDays: PropTypes.bool,
101 |   isDayBlocked: PropTypes.func,
102 |   isOutsideRange: PropTypes.func,
103 |   isDayHighlighted: PropTypes.func,
104 | 
105 |   // internationalization props
106 |   displayFormat: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
107 |   monthFormat: PropTypes.string,
108 |   weekDayFormat: PropTypes.string,
109 |   phrases: PropTypes.shape(getPhrasePropTypes(DateRangePickerPhrases)),
110 |   dayAriaLabelFormat: PropTypes.string,
111 | };
112 | 


--------------------------------------------------------------------------------
/src/shapes/DayOfWeekShape.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | 
3 | import { WEEKDAYS } from '../constants';
4 | 
5 | export default PropTypes.oneOf(WEEKDAYS);
6 | 


--------------------------------------------------------------------------------
/src/shapes/DisabledShape.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | 
3 | import { START_DATE, END_DATE } from '../constants';
4 | 
5 | export default PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf([START_DATE, END_DATE])]);
6 | 


--------------------------------------------------------------------------------
/src/shapes/FocusedInputShape.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | 
3 | import {
4 |   START_DATE,
5 |   END_DATE,
6 | } from '../constants';
7 | 
8 | export default PropTypes.oneOf([START_DATE, END_DATE]);
9 | 


--------------------------------------------------------------------------------
/src/shapes/IconPositionShape.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | 
3 | import {
4 |   ICON_BEFORE_POSITION,
5 |   ICON_AFTER_POSITION,
6 | } from '../constants';
7 | 
8 | export default PropTypes.oneOf([ICON_BEFORE_POSITION, ICON_AFTER_POSITION]);
9 | 


--------------------------------------------------------------------------------
/src/shapes/ModifiersShape.js:
--------------------------------------------------------------------------------
 1 | import PropTypes from 'prop-types';
 2 | import { and } from 'airbnb-prop-types';
 3 | 
 4 | export default and([
 5 |   PropTypes.instanceOf(Set),
 6 |   function modifiers(props, propName, ...rest) {
 7 |     const { [propName]: propValue } = props;
 8 |     let firstError;
 9 |     [...propValue].some((v, i) => {
10 |       const fakePropName = `${propName}: index ${i}`;
11 |       firstError = PropTypes.string.isRequired(
12 |         { [fakePropName]: v },
13 |         fakePropName,
14 |         ...rest,
15 |       );
16 |       return firstError != null;
17 |     });
18 |     return firstError == null ? null : firstError;
19 |   },
20 | ], 'Modifiers (Set of Strings)');
21 | 


--------------------------------------------------------------------------------
/src/shapes/NavPositionShape.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | 
3 | import {
4 |   NAV_POSITION_BOTTOM,
5 |   NAV_POSITION_TOP,
6 | } from '../constants';
7 | 
8 | export default PropTypes.oneOf([NAV_POSITION_BOTTOM, NAV_POSITION_TOP]);
9 | 


--------------------------------------------------------------------------------
/src/shapes/OpenDirectionShape.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | 
3 | import {
4 |   OPEN_DOWN,
5 |   OPEN_UP,
6 | } from '../constants';
7 | 
8 | export default PropTypes.oneOf([OPEN_DOWN, OPEN_UP]);
9 | 


--------------------------------------------------------------------------------
/src/shapes/OrientationShape.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | 
3 | import {
4 |   HORIZONTAL_ORIENTATION,
5 |   VERTICAL_ORIENTATION,
6 | } from '../constants';
7 | 
8 | export default PropTypes.oneOf([HORIZONTAL_ORIENTATION, VERTICAL_ORIENTATION]);
9 | 


--------------------------------------------------------------------------------
/src/shapes/ScrollableOrientationShape.js:
--------------------------------------------------------------------------------
 1 | import PropTypes from 'prop-types';
 2 | 
 3 | import {
 4 |   HORIZONTAL_ORIENTATION,
 5 |   VERTICAL_ORIENTATION,
 6 |   VERTICAL_SCROLLABLE,
 7 | } from '../constants';
 8 | 
 9 | export default PropTypes.oneOf([
10 |   HORIZONTAL_ORIENTATION,
11 |   VERTICAL_ORIENTATION,
12 |   VERTICAL_SCROLLABLE,
13 | ]);
14 | 


--------------------------------------------------------------------------------
/src/shapes/SingleDatePickerShape.js:
--------------------------------------------------------------------------------
  1 | import PropTypes from 'prop-types';
  2 | import momentPropTypes from 'react-moment-proptypes';
  3 | import { mutuallyExclusiveProps, nonNegativeInteger } from 'airbnb-prop-types';
  4 | 
  5 | import { SingleDatePickerPhrases } from '../defaultPhrases';
  6 | import getPhrasePropTypes from '../utils/getPhrasePropTypes';
  7 | 
  8 | import IconPositionShape from './IconPositionShape';
  9 | import OrientationShape from './OrientationShape';
 10 | import anchorDirectionShape from './AnchorDirectionShape';
 11 | import openDirectionShape from './OpenDirectionShape';
 12 | import DayOfWeekShape from './DayOfWeekShape';
 13 | import CalendarInfoPositionShape from './CalendarInfoPositionShape';
 14 | import NavPositionShape from './NavPositionShape';
 15 | 
 16 | export default {
 17 |   // required props for a functional interactive SingleDatePicker
 18 |   date: momentPropTypes.momentObj,
 19 |   onDateChange: PropTypes.func.isRequired,
 20 | 
 21 |   focused: PropTypes.bool,
 22 |   onFocusChange: PropTypes.func.isRequired,
 23 | 
 24 |   // input related props
 25 |   id: PropTypes.string.isRequired,
 26 |   placeholder: PropTypes.string,
 27 |   ariaLabel: PropTypes.string,
 28 |   titleText: PropTypes.string,
 29 |   disabled: PropTypes.bool,
 30 |   required: PropTypes.bool,
 31 |   readOnly: PropTypes.bool,
 32 |   screenReaderInputMessage: PropTypes.string,
 33 |   showClearDate: PropTypes.bool,
 34 |   customCloseIcon: PropTypes.node,
 35 |   showDefaultInputIcon: PropTypes.bool,
 36 |   inputIconPosition: IconPositionShape,
 37 |   customInputIcon: PropTypes.node,
 38 |   noBorder: PropTypes.bool,
 39 |   block: PropTypes.bool,
 40 |   small: PropTypes.bool,
 41 |   regular: PropTypes.bool,
 42 |   verticalSpacing: nonNegativeInteger,
 43 |   keepFocusOnInput: PropTypes.bool,
 44 |   autoComplete: PropTypes.string,
 45 | 
 46 |   // calendar presentation and interaction related props
 47 |   renderMonthText: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'),
 48 |   renderMonthElement: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'),
 49 |   renderWeekHeaderElement: PropTypes.func,
 50 |   orientation: OrientationShape,
 51 |   anchorDirection: anchorDirectionShape,
 52 |   openDirection: openDirectionShape,
 53 |   horizontalMargin: PropTypes.number,
 54 |   withPortal: PropTypes.bool,
 55 |   withFullScreenPortal: PropTypes.bool,
 56 |   appendToBody: PropTypes.bool,
 57 |   disableScroll: PropTypes.bool,
 58 |   initialVisibleMonth: PropTypes.func,
 59 |   firstDayOfWeek: DayOfWeekShape,
 60 |   numberOfMonths: PropTypes.number,
 61 |   keepOpenOnDateSelect: PropTypes.bool,
 62 |   reopenPickerOnClearDate: PropTypes.bool,
 63 |   renderCalendarInfo: PropTypes.func,
 64 |   calendarInfoPosition: CalendarInfoPositionShape,
 65 |   hideKeyboardShortcutsPanel: PropTypes.bool,
 66 |   daySize: nonNegativeInteger,
 67 |   isRTL: PropTypes.bool,
 68 |   verticalHeight: nonNegativeInteger,
 69 |   transitionDuration: nonNegativeInteger,
 70 |   horizontalMonthPadding: nonNegativeInteger,
 71 | 
 72 |   // navigation related props
 73 |   dayPickerNavigationInlineStyles: PropTypes.object,
 74 |   navPosition: NavPositionShape,
 75 |   navPrev: PropTypes.node,
 76 |   navNext: PropTypes.node,
 77 |   renderNavPrevButton: PropTypes.func,
 78 |   renderNavNextButton: PropTypes.func,
 79 | 
 80 |   onPrevMonthClick: PropTypes.func,
 81 |   onNextMonthClick: PropTypes.func,
 82 |   onClose: PropTypes.func,
 83 | 
 84 |   // day presentation and interaction related props
 85 |   renderCalendarDay: PropTypes.func,
 86 |   renderDayContents: PropTypes.func,
 87 |   enableOutsideDays: PropTypes.bool,
 88 |   isDayBlocked: PropTypes.func,
 89 |   isOutsideRange: PropTypes.func,
 90 |   isDayHighlighted: PropTypes.func,
 91 |   minDate: momentPropTypes.momentObj,
 92 |   maxDate: momentPropTypes.momentObj,
 93 | 
 94 |   // internationalization props
 95 |   displayFormat: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
 96 |   monthFormat: PropTypes.string,
 97 |   weekDayFormat: PropTypes.string,
 98 |   phrases: PropTypes.shape(getPhrasePropTypes(SingleDatePickerPhrases)),
 99 |   dayAriaLabelFormat: PropTypes.string,
100 | };
101 | 


--------------------------------------------------------------------------------
/src/svg/arrow-left.svg:
--------------------------------------------------------------------------------
1 | <svg focusable="false" viewBox="0 0 1000 1000"><path d="M336 275L126 485h806c13 0 23 10 23 23s-10 23-23 23H126l210 210c11 11 11 21 0 32-5 5-10 7-16 7s-11-2-16-7L55 524c-11-11-11-21 0-32l249-249c21-22 53 10 32 32z"/></svg>
2 | 


--------------------------------------------------------------------------------
/src/svg/arrow-right.svg:
--------------------------------------------------------------------------------
1 | <svg focusable="false" viewBox="0 0 1000 1000"><path d="M694 242l249 250c12 11 12 21 1 32L694 773c-5 5-10 7-16 7s-11-2-16-7c-11-11-11-21 0-32l210-210H68c-13 0-23-10-23-23s10-23 23-23h806L662 275c-21-22 11-54 32-33z"/></svg>
2 | 


--------------------------------------------------------------------------------
/src/svg/calendar.svg:
--------------------------------------------------------------------------------
1 | <svg focusable="false" viewBox="0 0 1393.1 1500"><path d="m107 1393h241v-241h-241zm295 0h268v-241h-268zm-295-295h241v-268h-241zm295 0h268v-268h-268zm-295-321h241v-241h-241zm616 616h268v-241h-268zm-321-616h268v-241h-268zm643 616h241v-241h-241zm-322-295h268v-268h-268zm-294-723v-241c0-7-3-14-8-19-6-5-12-8-19-8h-54c-7 0-13 3-19 8-5 5-8 12-8 19v241c0 7 3 14 8 19 6 5 12 8 19 8h54c7 0 13-3 19-8 5-5 8-12 8-19zm616 723h241v-268h-241zm-322-321h268v-241h-268zm322 0h241v-241h-241zm27-402v-241c0-7-3-14-8-19-6-5-12-8-19-8h-54c-7 0-13 3-19 8-5 5-8 12-8 19v241c0 7 3 14 8 19 6 5 12 8 19 8h54c7 0 13-3 19-8 5-5 8-12 8-19zm321-54v1072c0 29-11 54-32 75s-46 32-75 32h-1179c-29 0-54-11-75-32s-32-46-32-75v-1072c0-29 11-54 32-75s46-32 75-32h107v-80c0-37 13-68 40-95s57-39 94-39h54c37 0 68 13 95 39 26 26 39 58 39 95v80h321v-80c0-37 13-69 40-95 26-26 57-39 94-39h54c37 0 68 13 94 39s40 58 40 95v80h107c29 0 54 11 75 32s32 46 32 75z"/></svg>
2 | 


--------------------------------------------------------------------------------
/src/svg/chevron-down.svg:
--------------------------------------------------------------------------------
1 | <svg focusable="false" viewBox="0 0 1000 1000"><path d="M968 289L514 741c-11 11-21 11-32 0L29 289c-4-5-6-11-6-16 0-13 10-23 23-23 6 0 11 2 15 7l437 436 438-436c4-5 9-7 16-7 6 0 11 2 16 7 9 10 9 21 0 32z"/></svg>
2 | 


--------------------------------------------------------------------------------
/src/svg/chevron-up.svg:
--------------------------------------------------------------------------------
1 | <svg focusable="false" viewBox="0 0 1000 1000"><path d="M32 713l453-453c11-11 21-11 32 0l453 453c5 5 7 10 7 16 0 13-10 23-22 23-7 0-12-2-16-7L501 309 64 745c-4 5-9 7-15 7-7 0-12-2-17-7-9-11-9-21 0-32z"/></svg>
2 | 


--------------------------------------------------------------------------------
/src/svg/close.svg:
--------------------------------------------------------------------------------
1 | <svg focusable="false" viewBox="0 0 12 12"><path fill-rule="evenodd" d="M11.53.47a.75.75 0 0 0-1.061 0l-4.47 4.47L1.529.47A.75.75 0 1 0 .468 1.531l4.47 4.47-4.47 4.47a.75.75 0 1 0 1.061 1.061l4.47-4.47 4.47 4.47a.75.75 0 1 0 1.061-1.061l-4.47-4.47 4.47-4.47a.75.75 0 0 0 0-1.061z"/></svg>
2 | 


--------------------------------------------------------------------------------
/src/theme/DefaultTheme.js:
--------------------------------------------------------------------------------
  1 | const core = {
  2 |   white: '#fff',
  3 |   gray: '#484848',
  4 |   grayLight: '#82888a',
  5 |   grayLighter: '#cacccd',
  6 |   grayLightest: '#f2f2f2',
  7 | 
  8 |   borderMedium: '#c4c4c4',
  9 |   border: '#dbdbdb',
 10 |   borderLight: '#e4e7e7',
 11 |   borderLighter: '#eceeee',
 12 |   borderBright: '#f4f5f5',
 13 | 
 14 |   primary: '#00a699',
 15 |   primaryShade_1: '#33dacd',
 16 |   primaryShade_2: '#66e2da',
 17 |   primaryShade_3: '#80e8e0',
 18 |   primaryShade_4: '#b2f1ec',
 19 |   primary_dark: '#008489',
 20 | 
 21 |   secondary: '#007a87',
 22 | 
 23 |   yellow: '#ffe8bc',
 24 |   yellow_dark: '#ffce71',
 25 | };
 26 | 
 27 | export default {
 28 |   reactDates: {
 29 |     zIndex: 0,
 30 |     border: {
 31 |       input: {
 32 |         border: 0,
 33 |         borderTop: 0,
 34 |         borderRight: 0,
 35 |         borderBottom: '2px solid transparent',
 36 |         borderLeft: 0,
 37 |         outlineFocused: 0,
 38 |         borderFocused: 0,
 39 |         borderTopFocused: 0,
 40 |         borderLeftFocused: 0,
 41 |         borderBottomFocused: `2px solid ${core.primary_dark}`,
 42 |         borderRightFocused: 0,
 43 |         borderRadius: 0,
 44 |       },
 45 |       pickerInput: {
 46 |         borderWidth: 1,
 47 |         borderStyle: 'solid',
 48 |         borderRadius: 2,
 49 |       },
 50 |     },
 51 | 
 52 |     color: {
 53 |       core,
 54 | 
 55 |       disabled: core.grayLightest,
 56 | 
 57 |       background: core.white,
 58 |       backgroundDark: '#f2f2f2',
 59 |       backgroundFocused: core.white,
 60 |       border: 'rgb(219, 219, 219)',
 61 |       text: core.gray,
 62 |       textDisabled: core.border,
 63 |       textFocused: '#007a87',
 64 |       placeholderText: '#757575',
 65 | 
 66 |       outside: {
 67 |         backgroundColor: core.white,
 68 |         backgroundColor_active: core.white,
 69 |         backgroundColor_hover: core.white,
 70 |         color: core.gray,
 71 |         color_active: core.gray,
 72 |         color_hover: core.gray,
 73 |       },
 74 | 
 75 |       highlighted: {
 76 |         backgroundColor: core.yellow,
 77 |         backgroundColor_active: core.yellow_dark,
 78 |         backgroundColor_hover: core.yellow_dark,
 79 |         color: core.gray,
 80 |         color_active: core.gray,
 81 |         color_hover: core.gray,
 82 |       },
 83 | 
 84 |       minimumNights: {
 85 |         backgroundColor: core.white,
 86 |         backgroundColor_active: core.white,
 87 |         backgroundColor_hover: core.white,
 88 |         borderColor: core.borderLighter,
 89 |         color: core.grayLighter,
 90 |         color_active: core.grayLighter,
 91 |         color_hover: core.grayLighter,
 92 |       },
 93 | 
 94 |       hoveredSpan: {
 95 |         backgroundColor: core.primaryShade_4,
 96 |         backgroundColor_active: core.primaryShade_3,
 97 |         backgroundColor_hover: core.primaryShade_4,
 98 |         borderColor: core.primaryShade_3,
 99 |         borderColor_active: core.primaryShade_3,
100 |         borderColor_hover: core.primaryShade_3,
101 |         color: core.secondary,
102 |         color_active: core.secondary,
103 |         color_hover: core.secondary,
104 |       },
105 | 
106 |       selectedSpan: {
107 |         backgroundColor: core.primaryShade_2,
108 |         backgroundColor_active: core.primaryShade_1,
109 |         backgroundColor_hover: core.primaryShade_1,
110 |         borderColor: core.primaryShade_1,
111 |         borderColor_active: core.primary,
112 |         borderColor_hover: core.primary,
113 |         color: core.white,
114 |         color_active: core.white,
115 |         color_hover: core.white,
116 |       },
117 | 
118 |       selected: {
119 |         backgroundColor: core.primary,
120 |         backgroundColor_active: core.primary,
121 |         backgroundColor_hover: core.primary,
122 |         borderColor: core.primary,
123 |         borderColor_active: core.primary,
124 |         borderColor_hover: core.primary,
125 |         color: core.white,
126 |         color_active: core.white,
127 |         color_hover: core.white,
128 |       },
129 | 
130 |       blocked_calendar: {
131 |         backgroundColor: core.grayLighter,
132 |         backgroundColor_active: core.grayLighter,
133 |         backgroundColor_hover: core.grayLighter,
134 |         borderColor: core.grayLighter,
135 |         borderColor_active: core.grayLighter,
136 |         borderColor_hover: core.grayLighter,
137 |         color: core.grayLight,
138 |         color_active: core.grayLight,
139 |         color_hover: core.grayLight,
140 |       },
141 | 
142 |       blocked_out_of_range: {
143 |         backgroundColor: core.white,
144 |         backgroundColor_active: core.white,
145 |         backgroundColor_hover: core.white,
146 |         borderColor: core.borderLight,
147 |         borderColor_active: core.borderLight,
148 |         borderColor_hover: core.borderLight,
149 |         color: core.grayLighter,
150 |         color_active: core.grayLighter,
151 |         color_hover: core.grayLighter,
152 |       },
153 |     },
154 | 
155 |     spacing: {
156 |       dayPickerHorizontalPadding: 9,
157 |       captionPaddingTop: 22,
158 |       captionPaddingBottom: 37,
159 |       inputPadding: 0,
160 |       displayTextPaddingVertical: undefined,
161 |       displayTextPaddingTop: 11,
162 |       displayTextPaddingBottom: 9,
163 |       displayTextPaddingHorizontal: undefined,
164 |       displayTextPaddingLeft: 11,
165 |       displayTextPaddingRight: 11,
166 |       displayTextPaddingVertical_small: undefined,
167 |       displayTextPaddingTop_small: 7,
168 |       displayTextPaddingBottom_small: 5,
169 |       displayTextPaddingHorizontal_small: undefined,
170 |       displayTextPaddingLeft_small: 7,
171 |       displayTextPaddingRight_small: 7,
172 |     },
173 | 
174 |     sizing: {
175 |       inputWidth: 130,
176 |       inputWidth_small: 97,
177 |       arrowWidth: 24,
178 |     },
179 | 
180 |     noScrollBarOnVerticalScrollable: false,
181 | 
182 |     font: {
183 |       size: 14,
184 |       captionSize: 18,
185 |       input: {
186 |         size: 19,
187 |         weight: 200,
188 |         lineHeight: '24px',
189 |         size_small: 15,
190 |         lineHeight_small: '18px',
191 |         letterSpacing_small: '0.2px',
192 |         styleDisabled: 'italic',
193 |       },
194 |     },
195 |   },
196 | };
197 | 


--------------------------------------------------------------------------------
/src/utils/calculateDimension.js:
--------------------------------------------------------------------------------
 1 | export default function calculateDimension(el, axis, borderBox = false, withMargin = false) {
 2 |   if (!el) {
 3 |     return 0;
 4 |   }
 5 | 
 6 |   const axisStart = axis === 'width' ? 'Left' : 'Top';
 7 |   const axisEnd = axis === 'width' ? 'Right' : 'Bottom';
 8 | 
 9 |   // Only read styles if we need to
10 |   const style = (!borderBox || withMargin) ? window.getComputedStyle(el) : null;
11 | 
12 |   // Offset includes border and padding
13 |   const { offsetWidth, offsetHeight } = el;
14 |   let size = axis === 'width' ? offsetWidth : offsetHeight;
15 | 
16 |   // Get the inner size
17 |   if (!borderBox) {
18 |     size -= (
19 |       parseFloat(style[`padding${axisStart}`])
20 |       + parseFloat(style[`padding${axisEnd}`])
21 |       + parseFloat(style[`border${axisStart}Width`])
22 |       + parseFloat(style[`border${axisEnd}Width`])
23 |     );
24 |   }
25 | 
26 |   // Apply margin
27 |   if (withMargin) {
28 |     size += (parseFloat(style[`margin${axisStart}`]) + parseFloat(style[`margin${axisEnd}`]));
29 |   }
30 | 
31 |   return size;
32 | }
33 | 


--------------------------------------------------------------------------------
/src/utils/disableScroll.js:
--------------------------------------------------------------------------------
 1 | const getScrollingRoot = () => document.scrollingElement || document.documentElement;
 2 | 
 3 | /**
 4 |  * Recursively finds the scroll parent of a node. The scroll parrent of a node
 5 |  * is the closest node that is scrollable. A node is scrollable if:
 6 |  *  - it is allowed to scroll via CSS ('overflow-y' not visible or hidden);
 7 |  *  - and its children/content are "bigger" than the node's box height.
 8 |  *
 9 |  * The root of the document always scrolls by default.
10 |  *
11 |  * @param {HTMLElement} node Any DOM element.
12 |  * @return {HTMLElement} The scroll parent element.
13 |  */
14 | export function getScrollParent(node) {
15 |   const parent = node.parentElement;
16 | 
17 |   if (parent == null) return getScrollingRoot();
18 | 
19 |   const { overflowY } = window.getComputedStyle(parent);
20 |   const canScroll = overflowY !== 'visible' && overflowY !== 'hidden';
21 | 
22 |   if (canScroll && parent.scrollHeight > parent.clientHeight) {
23 |     return parent;
24 |   }
25 | 
26 |   return getScrollParent(parent);
27 | }
28 | 
29 | /**
30 |  * Recursively traverses the tree upwards from the given node, capturing all
31 |  * ancestor nodes that scroll along with their current 'overflow-y' CSS
32 |  * property.
33 |  *
34 |  * @param {HTMLElement} node Any DOM element.
35 |  * @param {Map<HTMLElement,string>} [acc] Accumulator map.
36 |  * @return {Map<HTMLElement,string>} Map of ancestors with their 'overflow-y' value.
37 |  */
38 | export function getScrollAncestorsOverflowY(node, acc = new Map()) {
39 |   const scrollingRoot = getScrollingRoot();
40 |   const scrollParent = getScrollParent(node);
41 |   acc.set(scrollParent, scrollParent.style.overflowY);
42 | 
43 |   if (scrollParent === scrollingRoot) return acc;
44 |   return getScrollAncestorsOverflowY(scrollParent, acc);
45 | }
46 | 
47 | /**
48 |  * Disabling the scroll on a node involves finding all the scrollable ancestors
49 |  * and set their 'overflow-y' CSS property to 'hidden'. When all ancestors have
50 |  * 'overflow-y: hidden' (up to the document element) there is no scroll
51 |  * container, thus all the scroll outside of the node is disabled. In order to
52 |  * enable scroll again, we store the previous value of the 'overflow-y' for
53 |  * every ancestor in a closure and reset it back.
54 |  *
55 |  * @param {HTMLElement} node Any DOM element.
56 |  */
57 | export default function disableScroll(node) {
58 |   const scrollAncestorsOverflowY = getScrollAncestorsOverflowY(node);
59 |   const toggle = (on) => scrollAncestorsOverflowY.forEach((overflowY, ancestor) => {
60 |     ancestor.style.setProperty('overflow-y', on ? 'hidden' : overflowY);
61 |   });
62 | 
63 |   toggle(true);
64 |   return () => toggle(false);
65 | }
66 | 


--------------------------------------------------------------------------------
/src/utils/getActiveElement.js:
--------------------------------------------------------------------------------
1 | export default function getActiveElement() {
2 |   return typeof document !== 'undefined' && document.activeElement;
3 | }
4 | 


--------------------------------------------------------------------------------
/src/utils/getCalendarDaySettings.js:
--------------------------------------------------------------------------------
 1 | import getPhrase from './getPhrase';
 2 | import { BLOCKED_MODIFIER } from '../constants';
 3 | 
 4 | function isSelected(modifiers) {
 5 |   return modifiers.has('selected')
 6 |   || modifiers.has('selected-span')
 7 |   || modifiers.has('selected-start')
 8 |   || modifiers.has('selected-end');
 9 | }
10 | 
11 | function shouldUseDefaultCursor(modifiers) {
12 |   return modifiers.has('blocked-minimum-nights')
13 |   || modifiers.has('blocked-calendar')
14 |   || modifiers.has('blocked-out-of-range');
15 | }
16 | 
17 | function isHoveredSpan(modifiers) {
18 |   if (isSelected(modifiers)) return false;
19 |   return modifiers.has('hovered-span') || modifiers.has('after-hovered-start') || modifiers.has('before-hovered-end');
20 | }
21 | 
22 | function getAriaLabel(phrases, modifiers, day, ariaLabelFormat) {
23 |   const {
24 |     chooseAvailableDate,
25 |     dateIsUnavailable,
26 |     dateIsSelected,
27 |     dateIsSelectedAsStartDate,
28 |     dateIsSelectedAsEndDate,
29 |   } = phrases;
30 | 
31 |   const formattedDate = {
32 |     date: day.format(ariaLabelFormat),
33 |   };
34 | 
35 |   if (modifiers.has('selected-start') && dateIsSelectedAsStartDate) {
36 |     return getPhrase(dateIsSelectedAsStartDate, formattedDate);
37 |   } if (modifiers.has('selected-end') && dateIsSelectedAsEndDate) {
38 |     return getPhrase(dateIsSelectedAsEndDate, formattedDate);
39 |   } if (isSelected(modifiers) && dateIsSelected) {
40 |     return getPhrase(dateIsSelected, formattedDate);
41 |   } if (modifiers.has(BLOCKED_MODIFIER)) {
42 |     return getPhrase(dateIsUnavailable, formattedDate);
43 |   }
44 | 
45 |   return getPhrase(chooseAvailableDate, formattedDate);
46 | }
47 | 
48 | export default function getCalendarDaySettings(day, ariaLabelFormat, daySize, modifiers, phrases) {
49 |   return {
50 |     ariaLabel: getAriaLabel(phrases, modifiers, day, ariaLabelFormat),
51 |     hoveredSpan: isHoveredSpan(modifiers),
52 |     isOutsideRange: modifiers.has('blocked-out-of-range'),
53 |     selected: isSelected(modifiers),
54 |     useDefaultCursor: shouldUseDefaultCursor(modifiers),
55 | 
56 |     daySizeStyles: {
57 |       width: daySize,
58 |       height: daySize - 1,
59 |     },
60 |   };
61 | }
62 | 


--------------------------------------------------------------------------------
/src/utils/getCalendarMonthWeeks.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | 
 3 | import { WEEKDAYS } from '../constants';
 4 | 
 5 | export default function getCalendarMonthWeeks(
 6 |   month,
 7 |   enableOutsideDays,
 8 |   firstDayOfWeek = moment.localeData().firstDayOfWeek(),
 9 | ) {
10 |   if (!moment.isMoment(month) || !month.isValid()) {
11 |     throw new TypeError('`month` must be a valid moment object');
12 |   }
13 |   if (WEEKDAYS.indexOf(firstDayOfWeek) === -1) {
14 |     throw new TypeError('`firstDayOfWeek` must be an integer between 0 and 6');
15 |   }
16 | 
17 |   // set utc offset to get correct dates in future (when timezone changes)
18 |   const firstOfMonth = month.clone().startOf('month').hour(12);
19 |   const lastOfMonth = month.clone().endOf('month').hour(12);
20 | 
21 |   // calculate the exact first and last days to fill the entire matrix
22 |   // (considering days outside month)
23 |   const prevDays = ((firstOfMonth.day() + 7 - firstDayOfWeek) % 7);
24 |   const nextDays = ((firstDayOfWeek + 6 - lastOfMonth.day()) % 7);
25 |   const firstDay = firstOfMonth.clone().subtract(prevDays, 'day');
26 |   const lastDay = lastOfMonth.clone().add(nextDays, 'day');
27 | 
28 |   const totalDays = lastDay.diff(firstDay, 'days') + 1;
29 | 
30 |   const currentDay = firstDay.clone();
31 |   const weeksInMonth = [];
32 | 
33 |   for (let i = 0; i < totalDays; i += 1) {
34 |     if (i % 7 === 0) {
35 |       weeksInMonth.push([]);
36 |     }
37 | 
38 |     let day = null;
39 |     if ((i >= prevDays && i < (totalDays - nextDays)) || enableOutsideDays) {
40 |       day = currentDay.clone();
41 |     }
42 | 
43 |     weeksInMonth[weeksInMonth.length - 1].push(day);
44 | 
45 |     currentDay.add(1, 'day');
46 |   }
47 | 
48 |   return weeksInMonth;
49 | }
50 | 


--------------------------------------------------------------------------------
/src/utils/getCalendarMonthWidth.js:
--------------------------------------------------------------------------------
1 | export default function getCalendarMonthWidth(daySize, calendarMonthPadding = 0) {
2 |   return (7 * daySize) + (2 * calendarMonthPadding) + 1;
3 | }
4 | 


--------------------------------------------------------------------------------
/src/utils/getDetachedContainerStyles.js:
--------------------------------------------------------------------------------
 1 | import { OPEN_UP, ANCHOR_RIGHT } from '../constants';
 2 | 
 3 | /**
 4 |  * Calculate and return a CSS transform style to position a detached element
 5 |  * next to a reference element. The open and anchor direction indicate wether
 6 |  * it should be positioned above/below and/or to the left/right of the
 7 |  * reference element.
 8 |  *
 9 |  * Assuming r(0,0), r(1,1), d(0,0), d(1,1) for the bottom-left and top-right
10 |  * corners of the reference and detached elements, respectively:
11 |  *  - openDirection = DOWN, anchorDirection = LEFT => d(0,1) == r(0,1)
12 |  *  - openDirection = UP, anchorDirection = LEFT => d(0,0) == r(0,0)
13 |  *  - openDirection = DOWN, anchorDirection = RIGHT => d(1,1) == r(1,1)
14 |  *  - openDirection = UP, anchorDirection = RIGHT => d(1,0) == r(1,0)
15 |  *
16 |  * By using a CSS transform, we allow to further position it using
17 |  * top/bottom CSS properties for the anchor gutter.
18 |  *
19 |  * @param {string} openDirection The vertical positioning of the popup
20 |  * @param {string} anchorDirection The horizontal position of the popup
21 |  * @param {HTMLElement} referenceEl The reference element
22 |  */
23 | export default function getDetachedContainerStyles(openDirection, anchorDirection, referenceEl) {
24 |   const referenceRect = referenceEl.getBoundingClientRect();
25 |   let offsetX = referenceRect.left;
26 |   let offsetY = referenceRect.top;
27 | 
28 |   if (openDirection === OPEN_UP) {
29 |     offsetY = -(window.innerHeight - referenceRect.bottom);
30 |   }
31 | 
32 |   if (anchorDirection === ANCHOR_RIGHT) {
33 |     offsetX = -(window.innerWidth - referenceRect.right);
34 |   }
35 | 
36 |   return {
37 |     transform: `translate3d(${Math.round(offsetX)}px, ${Math.round(offsetY)}px, 0)`,
38 |   };
39 | }
40 | 


--------------------------------------------------------------------------------
/src/utils/getInputHeight.js:
--------------------------------------------------------------------------------
 1 | /* eslint-disable camelcase */
 2 | 
 3 | function getPadding(vertical, top, bottom) {
 4 |   const isTopDefined = typeof top === 'number';
 5 |   const isBottomDefined = typeof bottom === 'number';
 6 |   const isVerticalDefined = typeof vertical === 'number';
 7 | 
 8 |   if (isTopDefined && isBottomDefined) {
 9 |     return top + bottom;
10 |   }
11 | 
12 |   if (isTopDefined && isVerticalDefined) {
13 |     return top + vertical;
14 |   }
15 | 
16 |   if (isTopDefined) {
17 |     return top;
18 |   }
19 | 
20 |   if (isBottomDefined && isVerticalDefined) {
21 |     return bottom + vertical;
22 |   }
23 | 
24 |   if (isBottomDefined) {
25 |     return bottom;
26 |   }
27 | 
28 |   if (isVerticalDefined) {
29 |     return 2 * vertical;
30 |   }
31 | 
32 |   return 0;
33 | }
34 | 
35 | export default function getInputHeight({
36 |   font: {
37 |     input: {
38 |       lineHeight,
39 |       lineHeight_small,
40 |     },
41 |   },
42 |   spacing: {
43 |     inputPadding,
44 |     displayTextPaddingVertical,
45 |     displayTextPaddingTop,
46 |     displayTextPaddingBottom,
47 |     displayTextPaddingVertical_small,
48 |     displayTextPaddingTop_small,
49 |     displayTextPaddingBottom_small,
50 |   },
51 | }, small) {
52 |   const calcLineHeight = small ? lineHeight_small : lineHeight;
53 | 
54 |   const padding = small
55 |     ? getPadding(
56 |       displayTextPaddingVertical_small,
57 |       displayTextPaddingTop_small,
58 |       displayTextPaddingBottom_small,
59 |     )
60 |     : getPadding(
61 |       displayTextPaddingVertical,
62 |       displayTextPaddingTop,
63 |       displayTextPaddingBottom,
64 |     );
65 | 
66 |   return parseInt(calcLineHeight, 10) + (2 * inputPadding) + padding;
67 | }
68 | 


--------------------------------------------------------------------------------
/src/utils/getNumberOfCalendarMonthWeeks.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | 
 3 | function getBlankDaysBeforeFirstDay(firstDayOfMonth, firstDayOfWeek) {
 4 |   const weekDayDiff = firstDayOfMonth.day() - firstDayOfWeek;
 5 |   return (weekDayDiff + 7) % 7;
 6 | }
 7 | 
 8 | export default function getNumberOfCalendarMonthWeeks(
 9 |   month,
10 |   firstDayOfWeek = moment.localeData().firstDayOfWeek(),
11 | ) {
12 |   const firstDayOfMonth = month.clone().startOf('month').hour(12);
13 |   const numBlankDays = getBlankDaysBeforeFirstDay(firstDayOfMonth, firstDayOfWeek);
14 |   return Math.ceil((numBlankDays + month.daysInMonth()) / 7);
15 | }
16 | 


--------------------------------------------------------------------------------
/src/utils/getPhrase.jsx:
--------------------------------------------------------------------------------
 1 | export default function getPhrase(phrase, args) {
 2 |   if (typeof phrase === 'string') return phrase;
 3 | 
 4 |   if (typeof phrase === 'function') {
 5 |     return phrase(args);
 6 |   }
 7 | 
 8 |   return '';
 9 | }
10 | 


--------------------------------------------------------------------------------
/src/utils/getPhrasePropTypes.js:
--------------------------------------------------------------------------------
 1 | import PropTypes from 'prop-types';
 2 | 
 3 | export default function getPhrasePropTypes(defaultPhrases) {
 4 |   return Object.keys(defaultPhrases)
 5 |     .reduce((phrases, key) => ({
 6 |       ...phrases,
 7 |       [key]: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.node]),
 8 |     }), {});
 9 | }
10 | 


--------------------------------------------------------------------------------
/src/utils/getPooledMoment.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | 
 3 | const momentPool = new Map();
 4 | export default function getPooledMoment(dayString) {
 5 |   if (!momentPool.has(dayString)) {
 6 |     momentPool.set(dayString, moment(dayString));
 7 |   }
 8 | 
 9 |   return momentPool.get(dayString);
10 | }
11 | 


--------------------------------------------------------------------------------
/src/utils/getPreviousMonthMemoLast.js:
--------------------------------------------------------------------------------
 1 | let getPreviousMonthMemoKey;
 2 | let getPreviousMonthMemoValue;
 3 | 
 4 | export default function getPreviousMonthMemoLast(month) {
 5 |   if (month !== getPreviousMonthMemoKey) {
 6 |     getPreviousMonthMemoKey = month;
 7 |     getPreviousMonthMemoValue = month.clone().subtract(1, 'month');
 8 |   }
 9 | 
10 |   return getPreviousMonthMemoValue;
11 | }
12 | 


--------------------------------------------------------------------------------
/src/utils/getResponsiveContainerStyles.js:
--------------------------------------------------------------------------------
 1 | import { ANCHOR_LEFT } from '../constants';
 2 | 
 3 | export default function getResponsiveContainerStyles(
 4 |   anchorDirection,
 5 |   currentOffset,
 6 |   containerEdge,
 7 |   margin,
 8 | ) {
 9 |   const windowWidth = typeof window !== 'undefined' ? window.innerWidth : 0;
10 |   const calculatedOffset = anchorDirection === ANCHOR_LEFT
11 |     ? windowWidth - containerEdge
12 |     : containerEdge;
13 |   const calculatedMargin = margin || 0;
14 | 
15 |   return {
16 |     [anchorDirection]: Math.min(currentOffset + calculatedOffset - calculatedMargin, 0),
17 |   };
18 | }
19 | 


--------------------------------------------------------------------------------
/src/utils/getSelectedDateOffset.js:
--------------------------------------------------------------------------------
1 | const defaultModifier = (day) => day;
2 | 
3 | export default function getSelectedDateOffset(fn, day, modifier = defaultModifier) {
4 |   if (!fn) return day;
5 |   return modifier(fn(day.clone()));
6 | }
7 | 


--------------------------------------------------------------------------------
/src/utils/getTransformStyles.js:
--------------------------------------------------------------------------------
1 | export default function getTransformStyles(transformValue) {
2 |   return {
3 |     transform: transformValue,
4 |     msTransform: transformValue,
5 |     MozTransform: transformValue,
6 |     WebkitTransform: transformValue,
7 |   };
8 | }
9 | 


--------------------------------------------------------------------------------
/src/utils/getVisibleDays.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | import toISOMonthString from './toISOMonthString';
 3 | 
 4 | export default function getVisibleDays(
 5 |   month,
 6 |   numberOfMonths,
 7 |   enableOutsideDays,
 8 |   withoutTransitionMonths,
 9 | ) {
10 |   if (!moment.isMoment(month)) return {};
11 | 
12 |   const visibleDaysByMonth = {};
13 |   let currentMonth = withoutTransitionMonths ? month.clone() : month.clone().subtract(1, 'month');
14 |   for (let i = 0; i < (withoutTransitionMonths ? numberOfMonths : numberOfMonths + 2); i += 1) {
15 |     const visibleDays = [];
16 | 
17 |     // set utc offset to get correct dates in future (when timezone changes)
18 |     const baseDate = currentMonth.clone();
19 |     const firstOfMonth = baseDate.clone().startOf('month').hour(12);
20 |     const lastOfMonth = baseDate.clone().endOf('month').hour(12);
21 | 
22 |     const currentDay = firstOfMonth.clone();
23 | 
24 |     // days belonging to the previous month
25 |     if (enableOutsideDays) {
26 |       for (let j = 0; j < currentDay.weekday(); j += 1) {
27 |         const prevDay = currentDay.clone().subtract(j + 1, 'day');
28 |         visibleDays.unshift(prevDay);
29 |       }
30 |     }
31 | 
32 |     while (currentDay < lastOfMonth) {
33 |       visibleDays.push(currentDay.clone());
34 |       currentDay.add(1, 'day');
35 |     }
36 | 
37 |     if (enableOutsideDays) {
38 |       // weekday() returns the index of the day of the week according to the locale
39 |       // this means if the week starts on Monday, weekday() will return 0 for a Monday date, not 1
40 |       if (currentDay.weekday() !== 0) {
41 |         // days belonging to the next month
42 |         for (let k = currentDay.weekday(), count = 0; k < 7; k += 1, count += 1) {
43 |           const nextDay = currentDay.clone().add(count, 'day');
44 |           visibleDays.push(nextDay);
45 |         }
46 |       }
47 |     }
48 | 
49 |     visibleDaysByMonth[toISOMonthString(currentMonth)] = visibleDays;
50 |     currentMonth = currentMonth.clone().add(1, 'month');
51 |   }
52 | 
53 |   return visibleDaysByMonth;
54 | }
55 | 


--------------------------------------------------------------------------------
/src/utils/isAfterDay.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | 
 3 | import isBeforeDay from './isBeforeDay';
 4 | import isSameDay from './isSameDay';
 5 | 
 6 | export default function isAfterDay(a, b) {
 7 |   if (!moment.isMoment(a) || !moment.isMoment(b)) return false;
 8 |   return !isBeforeDay(a, b) && !isSameDay(a, b);
 9 | }
10 | 


--------------------------------------------------------------------------------
/src/utils/isBeforeDay.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | 
 3 | export default function isBeforeDay(a, b) {
 4 |   if (!moment.isMoment(a) || !moment.isMoment(b)) return false;
 5 | 
 6 |   const aYear = a.year();
 7 |   const aMonth = a.month();
 8 | 
 9 |   const bYear = b.year();
10 |   const bMonth = b.month();
11 | 
12 |   const isSameYear = aYear === bYear;
13 |   const isSameMonth = aMonth === bMonth;
14 | 
15 |   if (isSameYear && isSameMonth) return a.date() < b.date();
16 |   if (isSameYear) return aMonth < bMonth;
17 |   return aYear < bYear;
18 | }
19 | 


--------------------------------------------------------------------------------
/src/utils/isDayVisible.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | 
 3 | import isBeforeDay from './isBeforeDay';
 4 | import isAfterDay from './isAfterDay';
 5 | import toISOMonthString from './toISOMonthString';
 6 | 
 7 | const startCacheOutsideDays = new Map();
 8 | const endCacheOutsideDays = new Map();
 9 | 
10 | const startCacheInsideDays = new Map();
11 | const endCacheInsideDays = new Map();
12 | 
13 | export default function isDayVisible(day, month, numberOfMonths, enableOutsideDays) {
14 |   if (!moment.isMoment(day)) return false;
15 | 
16 |   // Cloning is a little expensive, so we want to do it as little as possible.
17 | 
18 |   const startKey = toISOMonthString(month);
19 |   // eslint-disable-next-line prefer-template
20 |   const endKey = startKey + '+' + numberOfMonths;
21 | 
22 |   if (enableOutsideDays) {
23 |     if (!startCacheOutsideDays.has(startKey)) {
24 |       startCacheOutsideDays.set(startKey, month.clone().startOf('month').startOf('week').hour(12));
25 |     }
26 | 
27 |     if (isBeforeDay(day, startCacheOutsideDays.get(startKey))) return false;
28 | 
29 |     if (!endCacheOutsideDays.has(endKey)) {
30 |       endCacheOutsideDays.set(
31 |         endKey,
32 |         month.clone().endOf('week').add(numberOfMonths - 1, 'months').endOf('month')
33 |           .endOf('week')
34 |           .hour(12),
35 |       );
36 |     }
37 | 
38 |     return !isAfterDay(day, endCacheOutsideDays.get(endKey));
39 |   }
40 | 
41 |   // !enableOutsideDays
42 | 
43 |   if (!startCacheInsideDays.has(startKey)) {
44 |     startCacheInsideDays.set(startKey, month.clone().startOf('month').hour(12));
45 |   }
46 | 
47 |   if (isBeforeDay(day, startCacheInsideDays.get(startKey))) return false;
48 | 
49 |   if (!endCacheInsideDays.has(endKey)) {
50 |     endCacheInsideDays.set(
51 |       endKey,
52 |       month.clone().add(numberOfMonths - 1, 'months').endOf('month').hour(12),
53 |     );
54 |   }
55 | 
56 |   return !isAfterDay(day, endCacheInsideDays.get(endKey));
57 | }
58 | 


--------------------------------------------------------------------------------
/src/utils/isInclusivelyAfterDay.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 | 
3 | import isBeforeDay from './isBeforeDay';
4 | 
5 | export default function isInclusivelyAfterDay(a, b) {
6 |   if (!moment.isMoment(a) || !moment.isMoment(b)) return false;
7 |   return !isBeforeDay(a, b);
8 | }
9 | 


--------------------------------------------------------------------------------
/src/utils/isInclusivelyBeforeDay.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 | 
3 | import isAfterDay from './isAfterDay';
4 | 
5 | export default function isInclusivelyBeforeDay(a, b) {
6 |   if (!moment.isMoment(a) || !moment.isMoment(b)) return false;
7 |   return !isAfterDay(a, b);
8 | }
9 | 


--------------------------------------------------------------------------------
/src/utils/isNextDay.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | 
 3 | import isSameDay from './isSameDay';
 4 | 
 5 | export default function isNextDay(a, b) {
 6 |   if (!moment.isMoment(a) || !moment.isMoment(b)) return false;
 7 |   const nextDay = moment(a).add(1, 'day');
 8 |   return isSameDay(nextDay, b);
 9 | }
10 | 


--------------------------------------------------------------------------------
/src/utils/isNextMonth.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 | 
3 | import isSameMonth from './isSameMonth';
4 | 
5 | export default function isNextMonth(a, b) {
6 |   if (!moment.isMoment(a) || !moment.isMoment(b)) return false;
7 |   return isSameMonth(a.clone().add(1, 'month'), b);
8 | }
9 | 


--------------------------------------------------------------------------------
/src/utils/isPrevMonth.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 | 
3 | import isSameMonth from './isSameMonth';
4 | 
5 | export default function isPrevMonth(a, b) {
6 |   if (!moment.isMoment(a) || !moment.isMoment(b)) return false;
7 |   return isSameMonth(a.clone().subtract(1, 'month'), b);
8 | }
9 | 


--------------------------------------------------------------------------------
/src/utils/isPreviousDay.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | 
 3 | import isSameDay from './isSameDay';
 4 | 
 5 | export default function isPreviousDay(a, b) {
 6 |   if (!moment.isMoment(a) || !moment.isMoment(b)) return false;
 7 |   const dayBefore = moment(a).subtract(1, 'day');
 8 |   return isSameDay(dayBefore, b);
 9 | }
10 | 


--------------------------------------------------------------------------------
/src/utils/isSameDay.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | 
 3 | export default function isSameDay(a, b) {
 4 |   if (!moment.isMoment(a) || !moment.isMoment(b)) return false;
 5 |   // Compare least significant, most likely to change units first
 6 |   // Moment's isSame clones moment inputs and is a tad slow
 7 |   return a.date() === b.date()
 8 |     && a.month() === b.month()
 9 |     && a.year() === b.year();
10 | }
11 | 


--------------------------------------------------------------------------------
/src/utils/isSameMonth.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | 
 3 | export default function isSameMonth(a, b) {
 4 |   if (!moment.isMoment(a) || !moment.isMoment(b)) return false;
 5 |   // Compare least significant, most likely to change units first
 6 |   // Moment's isSame clones moment inputs and is a tad slow
 7 |   return a.month() === b.month()
 8 |     && a.year() === b.year();
 9 | }
10 | 


--------------------------------------------------------------------------------
/src/utils/isTransitionEndSupported.js:
--------------------------------------------------------------------------------
1 | export default function isTransitionEndSupported() {
2 |   return !!(typeof window !== 'undefined' && 'TransitionEvent' in window);
3 | }
4 | 


--------------------------------------------------------------------------------
/src/utils/modifiers.js:
--------------------------------------------------------------------------------
  1 | import isDayVisible from './isDayVisible';
  2 | import toISODateString from './toISODateString';
  3 | import toISOMonthString from './toISOMonthString';
  4 | import getPreviousMonthMemoLast from './getPreviousMonthMemoLast';
  5 | 
  6 | import { VERTICAL_SCROLLABLE } from '../constants';
  7 | 
  8 | export function addModifier(updatedDays, day, modifier, props, state) {
  9 |   const { numberOfMonths: numberOfVisibleMonths, enableOutsideDays, orientation } = props;
 10 |   const { currentMonth: firstVisibleMonth, visibleDays } = state;
 11 | 
 12 |   let currentMonth = firstVisibleMonth;
 13 |   let numberOfMonths = numberOfVisibleMonths;
 14 |   if (orientation === VERTICAL_SCROLLABLE) {
 15 |     numberOfMonths = Object.keys(visibleDays).length;
 16 |   } else {
 17 |     currentMonth = getPreviousMonthMemoLast(currentMonth);
 18 |     numberOfMonths += 2;
 19 |   }
 20 |   if (!day || !isDayVisible(day, currentMonth, numberOfMonths, enableOutsideDays)) {
 21 |     return updatedDays;
 22 |   }
 23 | 
 24 |   const iso = toISODateString(day);
 25 | 
 26 |   let updatedDaysAfterAddition = { ...updatedDays };
 27 |   if (enableOutsideDays) {
 28 |     const monthsToUpdate = Object.keys(visibleDays).filter((monthKey) => (
 29 |       Object.keys(visibleDays[monthKey]).indexOf(iso) > -1
 30 |     ));
 31 | 
 32 |     updatedDaysAfterAddition = monthsToUpdate.reduce((acc, monthIso) => {
 33 |       const month = updatedDays[monthIso] || visibleDays[monthIso];
 34 | 
 35 |       if (!month[iso] || !month[iso].has(modifier)) {
 36 |         const modifiers = new Set(month[iso]);
 37 |         modifiers.add(modifier);
 38 |         acc[monthIso] = {
 39 |           ...month,
 40 |           [iso]: modifiers,
 41 |         };
 42 |       }
 43 | 
 44 |       return acc;
 45 |     }, updatedDaysAfterAddition);
 46 |   } else {
 47 |     const monthIso = toISOMonthString(day);
 48 |     const month = updatedDays[monthIso] || visibleDays[monthIso] || {};
 49 | 
 50 |     if (!month[iso] || !month[iso].has(modifier)) {
 51 |       const modifiers = new Set(month[iso]);
 52 |       modifiers.add(modifier);
 53 |       updatedDaysAfterAddition[monthIso] = {
 54 |         ...month,
 55 |         [iso]: modifiers,
 56 |       };
 57 |     }
 58 |   }
 59 | 
 60 |   return updatedDaysAfterAddition;
 61 | }
 62 | 
 63 | export function deleteModifier(updatedDays, day, modifier, props, state) {
 64 |   const { numberOfMonths: numberOfVisibleMonths, enableOutsideDays, orientation } = props;
 65 |   const { currentMonth: firstVisibleMonth, visibleDays } = state;
 66 | 
 67 |   let currentMonth = firstVisibleMonth;
 68 |   let numberOfMonths = numberOfVisibleMonths;
 69 |   if (orientation === VERTICAL_SCROLLABLE) {
 70 |     numberOfMonths = Object.keys(visibleDays).length;
 71 |   } else {
 72 |     currentMonth = getPreviousMonthMemoLast(currentMonth);
 73 |     numberOfMonths += 2;
 74 |   }
 75 |   if (!day || !isDayVisible(day, currentMonth, numberOfMonths, enableOutsideDays)) {
 76 |     return updatedDays;
 77 |   }
 78 | 
 79 |   const iso = toISODateString(day);
 80 | 
 81 |   let updatedDaysAfterDeletion = { ...updatedDays };
 82 |   if (enableOutsideDays) {
 83 |     const monthsToUpdate = Object.keys(visibleDays).filter((monthKey) => (
 84 |       Object.keys(visibleDays[monthKey]).indexOf(iso) > -1
 85 |     ));
 86 | 
 87 |     updatedDaysAfterDeletion = monthsToUpdate.reduce((acc, monthIso) => {
 88 |       const month = updatedDays[monthIso] || visibleDays[monthIso];
 89 | 
 90 |       if (month[iso] && month[iso].has(modifier)) {
 91 |         const modifiers = new Set(month[iso]);
 92 |         modifiers.delete(modifier);
 93 |         acc[monthIso] = {
 94 |           ...month,
 95 |           [iso]: modifiers,
 96 |         };
 97 |       }
 98 | 
 99 |       return acc;
100 |     }, updatedDaysAfterDeletion);
101 |   } else {
102 |     const monthIso = toISOMonthString(day);
103 |     const month = updatedDays[monthIso] || visibleDays[monthIso] || {};
104 | 
105 |     if (month[iso] && month[iso].has(modifier)) {
106 |       const modifiers = new Set(month[iso]);
107 |       modifiers.delete(modifier);
108 |       updatedDaysAfterDeletion[monthIso] = {
109 |         ...month,
110 |         [iso]: modifiers,
111 |       };
112 |     }
113 |   }
114 | 
115 |   return updatedDaysAfterDeletion;
116 | }
117 | 


--------------------------------------------------------------------------------
/src/utils/noflip.js:
--------------------------------------------------------------------------------
 1 | const NOFLIP = '/* @noflip */';
 2 | 
 3 | // Appends a noflip comment to a style rule in order to prevent it from being automatically
 4 | // flipped in RTL contexts. This should be used only in situations where the style must remain
 5 | // unflipped regardless of direction context. See: https://github.com/kentcdodds/rtl-css-js#usage
 6 | export default function noflip(value) {
 7 |   if (typeof value === 'number') return `${value}px ${NOFLIP}`;
 8 |   if (typeof value === 'string') return `${value} ${NOFLIP}`;
 9 | 
10 |   throw new TypeError('noflip expects a string or a number');
11 | }
12 | 


--------------------------------------------------------------------------------
/src/utils/registerCSSInterfaceWithDefaultTheme.js:
--------------------------------------------------------------------------------
1 | import CSSInterface from 'react-with-styles-interface-css';
2 | 
3 | import registerInterfaceWithDefaultTheme from './registerInterfaceWithDefaultTheme';
4 | 
5 | export default function registerCSSInterfaceWithDefaultTheme() {
6 |   registerInterfaceWithDefaultTheme(CSSInterface);
7 | }
8 | 


--------------------------------------------------------------------------------
/src/utils/registerInterfaceWithDefaultTheme.js:
--------------------------------------------------------------------------------
1 | import ThemedStyleSheet from 'react-with-styles/lib/ThemedStyleSheet';
2 | import DefaultTheme from '../theme/DefaultTheme';
3 | 
4 | export default function registerInterfaceWithDefaultTheme(reactWithStylesInterface) {
5 |   ThemedStyleSheet.registerInterface(reactWithStylesInterface);
6 |   ThemedStyleSheet.registerTheme(DefaultTheme);
7 | }
8 | 


--------------------------------------------------------------------------------
/src/utils/toISODateString.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | 
 3 | import toMomentObject from './toMomentObject';
 4 | 
 5 | export default function toISODateString(date, currentFormat) {
 6 |   const dateObj = moment.isMoment(date) ? date : toMomentObject(date, currentFormat);
 7 |   if (!dateObj) return null;
 8 | 
 9 |   // Template strings compiled in strict mode uses concat, which is slow. Since
10 |   // this code is in a hot path and we want it to be as fast as possible, we
11 |   // want to use old-fashioned +.
12 |   // eslint-disable-next-line prefer-template
13 |   return dateObj.year() + '-' + String(dateObj.month() + 1).padStart(2, '0') + '-' + String(dateObj.date()).padStart(2, '0');
14 | }
15 | 


--------------------------------------------------------------------------------
/src/utils/toISOMonthString.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | 
 3 | import toMomentObject from './toMomentObject';
 4 | 
 5 | export default function toISOMonthString(date, currentFormat) {
 6 |   const dateObj = moment.isMoment(date) ? date : toMomentObject(date, currentFormat);
 7 |   if (!dateObj) return null;
 8 | 
 9 |   // Template strings compiled in strict mode uses concat, which is slow. Since
10 |   // this code is in a hot path and we want it to be as fast as possible, we
11 |   // want to use old-fashioned +.
12 |   // eslint-disable-next-line prefer-template
13 |   return dateObj.year() + '-' + String(dateObj.month() + 1).padStart(2, '0');
14 | }
15 | 


--------------------------------------------------------------------------------
/src/utils/toLocalizedDateString.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | 
 3 | import toMomentObject from './toMomentObject';
 4 | 
 5 | import { DISPLAY_FORMAT } from '../constants';
 6 | 
 7 | export default function toLocalizedDateString(date, currentFormat) {
 8 |   const dateObj = moment.isMoment(date) ? date : toMomentObject(date, currentFormat);
 9 |   if (!dateObj) return null;
10 | 
11 |   return dateObj.format(DISPLAY_FORMAT);
12 | }
13 | 


--------------------------------------------------------------------------------
/src/utils/toMomentObject.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | 
 3 | import { DISPLAY_FORMAT, ISO_FORMAT } from '../constants';
 4 | 
 5 | export default function toMomentObject(dateString, customFormat) {
 6 |   const dateFormats = customFormat
 7 |     ? [customFormat, DISPLAY_FORMAT, ISO_FORMAT]
 8 |     : [DISPLAY_FORMAT, ISO_FORMAT];
 9 | 
10 |   const date = moment(dateString, dateFormats, true);
11 |   return date.isValid() ? date.hour(12) : null;
12 | }
13 | 


--------------------------------------------------------------------------------
/stories/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 |   "rules": {
3 |     "import/no-extraneous-dependencies": [2, {
4 |       "devDependencies": true
5 |     }],
6 |   }
7 | }
8 | 


--------------------------------------------------------------------------------
/stories/DateRangePicker.js:
--------------------------------------------------------------------------------
  1 | import React from 'react';
  2 | import moment from 'moment';
  3 | import momentJalaali from 'moment-jalaali';
  4 | import { storiesOf } from '@storybook/react';
  5 | import { withInfo } from '@storybook/addon-info';
  6 | import DirectionProvider, { DIRECTIONS } from 'react-with-direction/dist/DirectionProvider';
  7 | 
  8 | import {
  9 |   VERTICAL_ORIENTATION,
 10 |   ANCHOR_RIGHT,
 11 | } from '../src/constants';
 12 | 
 13 | import DateRangePickerWrapper from '../examples/DateRangePickerWrapper';
 14 | 
 15 | const TestInput = props => (
 16 |   <div style={{ marginTop: 16 }}>
 17 |     <input
 18 |       {...props}
 19 |       type="text"
 20 |       style={{
 21 |         height: 48,
 22 |         width: 284,
 23 |         fontSize: 18,
 24 |         fontWeight: 200,
 25 |         padding: '12px 16px',
 26 |       }}
 27 |     />
 28 |   </div>
 29 | );
 30 | 
 31 | class TestWrapper extends React.Component {
 32 |   constructor(props) {
 33 |     super(props);
 34 |     this.state = {
 35 |       showDatePicker: false,
 36 |     };
 37 |   }
 38 | 
 39 |   render() {
 40 |     const { showDatePicker } = this.state;
 41 |     const display = showDatePicker ? 'block' : 'none';
 42 |     return (
 43 |       <div>
 44 |         <button
 45 |           type="button"
 46 |           onClick={() => this.setState({ showDatePicker: !showDatePicker })}
 47 |         >
 48 |           Show me!
 49 |         </button>
 50 | 
 51 |         <div style={{ display }}>
 52 |           <DateRangePickerWrapper />
 53 |         </div>
 54 |       </div>
 55 |     );
 56 |   }
 57 | }
 58 | 
 59 | storiesOf('DateRangePicker (DRP)', module)
 60 |   .add('default', withInfo()(() => (
 61 |     <DateRangePickerWrapper />
 62 |   )))
 63 |   .add('hidden with display: none', withInfo()(() => (
 64 |     <TestWrapper />
 65 |   )))
 66 |   .add('as part of a form', withInfo()(() => (
 67 |     <div>
 68 |       <DateRangePickerWrapper />
 69 |       <TestInput placeholder="Input 1" />
 70 |       <TestInput placeholder="Input 2" />
 71 |       <TestInput placeholder="Input 3" />
 72 |     </div>
 73 |   )))
 74 |   .add('non-english locale', withInfo()(() => {
 75 |     moment.locale('zh-cn');
 76 |     return (
 77 |       <DateRangePickerWrapper
 78 |         showClearDates
 79 |         startDatePlaceholderText="入住日期"
 80 |         endDatePlaceholderText="退房日期"
 81 |         monthFormat="YYYY[年]MMMM"
 82 |         phrases={{
 83 |           closeDatePicker: '关闭',
 84 |           clearDates: '清除日期',
 85 |         }}
 86 |       />
 87 |     );
 88 |   }))
 89 |   .add('non-english locale (Persian)', withInfo()(() => {
 90 |     moment.locale('fa');
 91 |     momentJalaali.loadPersian({ dialect: 'persian-modern', usePersianDigits: true });
 92 |     return (
 93 |       <DateRangePickerWrapper
 94 |         isRTL
 95 |         stateDateWrapper={momentJalaali}
 96 |         startDatePlaceholderText="تاریخ شروع"
 97 |         endDatePlaceholderText="تاریخ پایان"
 98 |         renderMonthText={month => momentJalaali(month).format('jMMMM jYYYY')}
 99 |         renderDayContents={day => momentJalaali(day).format('jD')}
100 |       />
101 |     );
102 |   }))
103 |   .add('with DirectionProvider', withInfo()(() => (
104 |     <DirectionProvider direction={DIRECTIONS.RTL}>
105 |       <DateRangePickerWrapper
106 |         startDatePlaceholderText="تاریخ شروع"
107 |         endDatePlaceholderText="تاریخ پایان"
108 |         anchorDirection={ANCHOR_RIGHT}
109 |         showDefaultInputIcon
110 |         showClearDates
111 |         isRTL
112 |       />
113 |     </DirectionProvider>
114 |   )))
115 |   .add('vertical with custom height', withInfo()(() => (
116 |     <DateRangePickerWrapper
117 |       orientation={VERTICAL_ORIENTATION}
118 |       verticalHeight={568}
119 |     />
120 |   )))
121 |   .add('with navigation blocked (minDate and maxDate)', withInfo()(() => (
122 |     <DateRangePickerWrapper
123 |       minDate={moment().subtract(2, 'months').startOf('month')}
124 |       maxDate={moment().add(2, 'months').endOf('month')}
125 |       numberOfMonths={2}
126 |     />
127 |   )))
128 |   .add('with custom autoComplete', withInfo()(() => (
129 |     <DateRangePickerWrapper autoComplete="datePicker" />
130 |   )));
131 | 


--------------------------------------------------------------------------------
/stories/DateRangePicker_day.js:
--------------------------------------------------------------------------------
  1 | import React from 'react';
  2 | import moment from 'moment';
  3 | import { storiesOf } from '@storybook/react';
  4 | import { withInfo } from '@storybook/addon-info';
  5 | 
  6 | import isSameDay from '../src/utils/isSameDay';
  7 | import isInclusivelyAfterDay from '../src/utils/isInclusivelyAfterDay';
  8 | 
  9 | import CustomizableCalendarDay from '../src/components/CustomizableCalendarDay';
 10 | 
 11 | import DateRangePickerWrapper from '../examples/DateRangePickerWrapper';
 12 | 
 13 | const datesList = [
 14 |   moment(),
 15 |   moment().add(1, 'days'),
 16 |   moment().add(3, 'days'),
 17 |   moment().add(9, 'days'),
 18 |   moment().add(10, 'days'),
 19 |   moment().add(11, 'days'),
 20 |   moment().add(12, 'days'),
 21 |   moment().add(13, 'days'),
 22 | ];
 23 | 
 24 | const selectedStyles = {
 25 |   background: '#590098',
 26 |   border: '1px solid #590098',
 27 |   color: '#fff',
 28 | 
 29 |   hover: {
 30 |     background: '#7A32AC',
 31 |     border: '1px solid #7A32AC',
 32 |     color: '#fff',
 33 |   },
 34 | };
 35 | 
 36 | const hoveredStyles = {
 37 |   background: '#cd99d0',
 38 |   border: '1px solid #cd99d0',
 39 |   color: '#fff',
 40 | };
 41 | 
 42 | const blockedStyles = {
 43 |   background: '#fff',
 44 |   border: '1px double #e4e7e7',
 45 |   color: '#dce0e0',
 46 | 
 47 |   hover: {
 48 |     background: '#fff',
 49 |     border: '1px double #e4e7e7',
 50 |     color: '#dce0e0',
 51 |   },
 52 | };
 53 | 
 54 | const customDayStyles = {
 55 |   selectedStartStyles: selectedStyles,
 56 |   selectedEndStyles: selectedStyles,
 57 |   hoveredSpanStyles: hoveredStyles,
 58 |   afterHoveredStartStyles: hoveredStyles,
 59 |   blockedMinNightsStyles: blockedStyles,
 60 |   blockedCalendarStyles: blockedStyles,
 61 |   blockedOutOfRangeStyles: blockedStyles,
 62 | 
 63 |   selectedSpanStyles: {
 64 |     background: '#9b32a2',
 65 |     border: '1px solid #9b32a2',
 66 |     color: '#fff',
 67 | 
 68 |     hover: {
 69 |       background: '#83008b',
 70 |       border: '1px solid #83008b',
 71 |       color: '#fff',
 72 |     },
 73 |   },
 74 | };
 75 | 
 76 | storiesOf('DRP - Day Props', module)
 77 |   .add('default', withInfo()(() => (
 78 |     <DateRangePickerWrapper autoFocus />
 79 |   )))
 80 |   .add('with minimum nights set', withInfo()(() => (
 81 |     <DateRangePickerWrapper
 82 |       minimumNights={3}
 83 |       initialStartDate={moment().add(3, 'days')}
 84 |       autoFocusEndDate
 85 |     />
 86 |   )))
 87 |   .add('allows single day range', withInfo()(() => (
 88 |     <DateRangePickerWrapper
 89 |       minimumNights={0}
 90 |       initialStartDate={moment().add(3, 'days')}
 91 |       autoFocusEndDate
 92 |     />
 93 |   )))
 94 |   .add('allows all days, including past days', withInfo()(() => (
 95 |     <DateRangePickerWrapper
 96 |       isOutsideRange={() => false}
 97 |       autoFocus
 98 |     />
 99 |   )))
100 |   .add('allows next two weeks only', withInfo()(() => (
101 |     <DateRangePickerWrapper
102 |       isOutsideRange={day =>
103 |         !isInclusivelyAfterDay(day, moment()) ||
104 |         isInclusivelyAfterDay(day, moment().add(2, 'weeks'))
105 |       }
106 |       autoFocus
107 |     />
108 |   )))
109 |   .add('with some blocked dates', withInfo()(() => (
110 |     <DateRangePickerWrapper
111 |       isDayBlocked={day1 => datesList.some(day2 => isSameDay(day1, day2))}
112 |       autoFocus
113 |     />
114 |   )))
115 |   .add('with some highlighted dates', withInfo()(() => (
116 |     <DateRangePickerWrapper
117 |       isDayHighlighted={day1 => datesList.some(day2 => isSameDay(day1, day2))}
118 |       autoFocus
119 |     />
120 |   )))
121 |   .add('blocks fridays', withInfo()(() => (
122 |     <DateRangePickerWrapper
123 |       isDayBlocked={day => moment.weekdays(day.weekday()) === 'Friday'}
124 |       autoFocus
125 |     />
126 |   )))
127 |   .add('with custom daily details', withInfo()(() => (
128 |     <DateRangePickerWrapper
129 |       renderDayContents={day => <div className="foo-bar">{day.format('ddd')}</div>}
130 |       autoFocus
131 |     />
132 |   )))
133 |   .add('one-off custom styling', withInfo()(() => (
134 |     <DateRangePickerWrapper
135 |       minimumNights={3}
136 |       renderCalendarDay={props => <CustomizableCalendarDay {...props} {...customDayStyles} />}
137 |       autoFocus
138 |     />
139 |   )));
140 | 


--------------------------------------------------------------------------------
/stories/DayPicker.js:
--------------------------------------------------------------------------------
  1 | import React from 'react';
  2 | import { storiesOf } from '@storybook/react';
  3 | import { withInfo } from '@storybook/addon-info';
  4 | import DirectionProvider, { DIRECTIONS } from 'react-with-direction/dist/DirectionProvider';
  5 | import DayPicker from '../src/components/DayPicker';
  6 | 
  7 | import {
  8 |   VERTICAL_ORIENTATION,
  9 |   VERTICAL_SCROLLABLE,
 10 | } from '../src/constants';
 11 | 
 12 | const TestPrevIcon = () => (
 13 |   <div
 14 |     style={{
 15 |       border: '1px solid #dce0e0',
 16 |       backgroundColor: '#fff',
 17 |       color: '#484848',
 18 |       left: '22px',
 19 |       padding: '3px',
 20 |       position: 'absolute',
 21 |       top: '20px',
 22 |       width: '40px',
 23 |     }}
 24 |     tabIndex="0"
 25 |   >
 26 |     Prev
 27 |   </div>
 28 | );
 29 | 
 30 | const TestNextIcon = () => (
 31 |   <div
 32 |     style={{
 33 |       border: '1px solid #dce0e0',
 34 |       backgroundColor: '#fff',
 35 |       color: '#484848',
 36 |       padding: '3px',
 37 |       position: 'absolute',
 38 |       right: '22px',
 39 |       top: '20px',
 40 |       width: '40px',
 41 |     }}
 42 |     tabIndex="0"
 43 |   >
 44 |     Next
 45 |   </div>
 46 | );
 47 | 
 48 | const TestCustomInfoPanel = () => (
 49 |   <div
 50 |     style={{
 51 |       padding: '10px 21px',
 52 |       borderTop: '1px solid #dce0e0',
 53 |       color: '#484848',
 54 |     }}
 55 |   >
 56 |     &#x2755; Some useful info here
 57 |   </div>
 58 | );
 59 | 
 60 | function renderNavPrevButton(buttonProps) {
 61 |   const {
 62 |     ariaLabel,
 63 |     disabled,
 64 |     onClick,
 65 |     onKeyUp,
 66 |     onMouseUp,
 67 |   } = buttonProps;
 68 | 
 69 |   return (
 70 |     <button
 71 |       aria-label={ariaLabel}
 72 |       disabled={disabled}
 73 |       onClick={onClick}
 74 |       onKeyUp={onKeyUp}
 75 |       onMouseUp={onMouseUp}
 76 |       style={{ position: 'absolute', top: 23, left: 22 }}
 77 |       type="button"
 78 |     >
 79 |     &lsaquo; Prev
 80 |     </button>
 81 |   );
 82 | }
 83 | 
 84 | function renderNavNextButton(buttonProps) {
 85 |   const {
 86 |     ariaLabel,
 87 |     disabled,
 88 |     onClick,
 89 |     onKeyUp,
 90 |     onMouseUp,
 91 |   } = buttonProps;
 92 | 
 93 |   return (
 94 |     <button
 95 |       aria-label={ariaLabel}
 96 |       disabled={disabled}
 97 |       onClick={onClick}
 98 |       onKeyUp={onKeyUp}
 99 |       onMouseUp={onMouseUp}
100 |       style={{ position: 'absolute', top: 23, right: 22 }}
101 |       type="button"
102 |     >
103 |           Next &rsaquo;
104 |     </button>
105 |   );
106 | }
107 | 
108 | storiesOf('DayPicker', module)
109 |   .add('default', withInfo()(() => (
110 |     <DayPicker />
111 |   )))
112 |   .add('with custom day size', withInfo()(() => (
113 |     <DayPicker daySize={50} />
114 |   )))
115 |   .add('single month', withInfo()(() => (
116 |     <DayPicker numberOfMonths={1} />
117 |   )))
118 |   .add('3 months', withInfo()(() => (
119 |     <DayPicker numberOfMonths={3} />
120 |   )))
121 |   .add('vertical', withInfo()(() => (
122 |     <DayPicker
123 |       numberOfMonths={2}
124 |       orientation={VERTICAL_ORIENTATION}
125 |     />
126 |   )))
127 |   .add('vertically scrollable with 12 months', withInfo()(() => (
128 |     <div
129 |       style={{
130 |         height: 568,
131 |         width: 320,
132 |       }}
133 |     >
134 |       <DayPicker
135 |         numberOfMonths={12}
136 |         orientation={VERTICAL_SCROLLABLE}
137 |       />
138 |     </div>
139 |   )))
140 |   .add('vertical with custom day size', withInfo()(() => (
141 |     <DayPicker
142 |       numberOfMonths={2}
143 |       orientation={VERTICAL_ORIENTATION}
144 |       daySize={50}
145 |     />
146 |   )))
147 |   .add('vertical with custom height', withInfo()(() => (
148 |     <DayPicker
149 |       numberOfMonths={2}
150 |       orientation={VERTICAL_ORIENTATION}
151 |       verticalHeight={568}
152 |     />
153 |   )))
154 |   .add('vertical with DirectionProvider', withInfo()(() => (
155 |     <DirectionProvider direction={DIRECTIONS.RTL}>
156 |       <DayPicker
157 |         numberOfMonths={2}
158 |         orientation={VERTICAL_ORIENTATION}
159 |         isRTL
160 |       />
161 |     </DirectionProvider>
162 |   )))
163 |   .add('vertically scrollable with DirectionProvider', withInfo()(() => (
164 |     <DirectionProvider direction={DIRECTIONS.RTL}>
165 |       <div
166 |         style={{
167 |           height: 568,
168 |           width: 320,
169 |         }}
170 |       >
171 |         <DayPicker
172 |           numberOfMonths={12}
173 |           orientation={VERTICAL_SCROLLABLE}
174 |         />
175 |       </div>
176 |     </DirectionProvider>
177 |   )))
178 |   .add('with custom arrows', withInfo()(() => (
179 |     <DayPicker
180 |       navPrev={<TestPrevIcon />}
181 |       navNext={<TestNextIcon />}
182 |     />
183 |   )))
184 |   .add('with custom navigation buttons', withInfo()(() => (
185 |     <DayPicker
186 |       renderNavPrevButton={renderNavPrevButton}
187 |       renderNavNextButton={renderNavNextButton}
188 |     />
189 |   )))
190 |   .add('with custom details', withInfo()(() => (
191 |     <DayPicker
192 |       renderDayContents={(day) => (day.day() % 6 === 5 ? '😻' : day.format('D'))}
193 |     />
194 |   )))
195 |   .add('vertical with fixed-width container', withInfo()(() => (
196 |     <div style={{ width: '400px' }}>
197 |       <DayPicker
198 |         numberOfMonths={2}
199 |         orientation={VERTICAL_ORIENTATION}
200 |       />
201 |     </div>
202 |   )))
203 |   .add('with info panel', withInfo()(() => (
204 |     <DayPicker
205 |       renderCalendarInfo={() => (
206 |         <TestCustomInfoPanel />
207 |       )}
208 |     />
209 |   )))
210 |   .add('with custom week header text', withInfo()(() => (
211 |     <DayPicker
212 |       renderWeekHeaderElement={(day) => (
213 |         <strong style={{ color: '#FE01E5' }}><small>{day.toUpperCase()}</small></strong>
214 |       )}
215 |     />
216 |   )))
217 |   .add('with custom week day format', withInfo()(() => (
218 |     <DayPicker
219 |       weekDayFormat="ddd"
220 |     />
221 |   )))
222 |   .add('with no animation', withInfo()(() => (
223 |     <DayPicker
224 |       transitionDuration={0}
225 |     />
226 |   )))
227 |   .add('noBorder', withInfo()(() => (
228 |     <DayPicker noBorder />
229 |   )));
230 | 


--------------------------------------------------------------------------------
/stories/InfoPanelDecorator.js:
--------------------------------------------------------------------------------
 1 | import React from 'react';
 2 | import PropTypes from 'prop-types';
 3 | 
 4 | export function monospace(text) {
 5 |   return `<span style="font-family:monospace;background:#f7f7f7">${text}</span>`;
 6 | }
 7 | 
 8 | function InfoPanel({ text }) {
 9 |   return (
10 |     <div
11 |       style={{
12 |         backgroundColor: '#fff',
13 |         fontColor: '#3c3f40',
14 |         fontSize: 14,
15 |         margin: '8px 0',
16 |         padding: 16,
17 |       }}
18 |     >
19 |       <span dangerouslySetInnerHTML={{ __html: text }} />
20 |     </div>
21 |   );
22 | }
23 | 
24 | InfoPanel.propTypes = {
25 |   text: PropTypes.string.isRequired,
26 | };
27 | 
28 | export default function InfoPanelDecorator(text) {
29 |   return story => (
30 |     <div>
31 |       <InfoPanel text={text} />
32 |       {story()}
33 |     </div>
34 |   );
35 | }
36 | 


--------------------------------------------------------------------------------
/stories/PresetDateRangePicker.js:
--------------------------------------------------------------------------------
 1 | import React from 'react';
 2 | import { storiesOf } from '@storybook/react';
 3 | import { withInfo } from '@storybook/addon-info';
 4 | import moment from 'moment';
 5 | 
 6 | import PresetDateRangePicker from '../examples/PresetDateRangePicker';
 7 | 
 8 | import InfoPanelDecorator, { monospace } from './InfoPanelDecorator';
 9 | 
10 | const presetDateRangePickerControllerInfo = `The ${monospace('PresetDateRangePicker')} component is not
11 |   exported by ${monospace('react-dates')}. It is instead an example of how you might use the
12 |   ${monospace('DateRangePicker')} along with the ${monospace('renderCalendarInfo')} prop in
13 |   order to add preset range buttons for easy range selection. You can see the example code
14 |   <a href="https://github.com/react-dates/react-dates/blob/HEAD/examples/PresetDateRangePicker.jsx">
15 |   here</a> and
16 |   <a href="https://github.com/react-dates/react-dates/blob/HEAD/stories/PresetDateRangePicker.js">
17 |   here</a>.`;
18 | 
19 | const today = moment();
20 | const tomorrow = moment().add(1, 'day');
21 | const presets = [{
22 |   text: 'Today',
23 |   start: today,
24 |   end: today,
25 | },
26 | {
27 |   text: 'Tomorrow',
28 |   start: tomorrow,
29 |   end: tomorrow,
30 | },
31 | {
32 |   text: 'Next Week',
33 |   start: today,
34 |   end: moment().add(1, 'week'),
35 | },
36 | {
37 |   text: 'Next Month',
38 |   start: today,
39 |   end: moment().add(1, 'month'),
40 | }];
41 | 
42 | storiesOf('PresetDateRangePicker', module)
43 |   .addDecorator(InfoPanelDecorator(presetDateRangePickerControllerInfo))
44 |   .add('default', withInfo()(() => (
45 |     <PresetDateRangePicker
46 |       presets={presets}
47 |       autoFocus
48 |     />
49 |   )));
50 | 


--------------------------------------------------------------------------------
/stories/SingleDatePicker.js:
--------------------------------------------------------------------------------
  1 | import React from 'react';
  2 | import moment from 'moment';
  3 | import momentJalaali from 'moment-jalaali';
  4 | import { storiesOf } from '@storybook/react';
  5 | import { withInfo } from '@storybook/addon-info';
  6 | import DirectionProvider, { DIRECTIONS } from 'react-with-direction/dist/DirectionProvider';
  7 | import isInclusivelyBeforeDay from '../src/utils/isInclusivelyBeforeDay';
  8 | import isInclusivelyAfterDay from '../src/utils/isInclusivelyAfterDay';
  9 | 
 10 | import {
 11 |   VERTICAL_ORIENTATION,
 12 |   ANCHOR_RIGHT,
 13 | } from '../src/constants';
 14 | 
 15 | import SingleDatePickerWrapper from '../examples/SingleDatePickerWrapper';
 16 | 
 17 | const TestInput = props => (
 18 |   <div style={{ marginTop: 16 }} >
 19 |     <input
 20 |       {...props}
 21 |       type="text"
 22 |       style={{
 23 |         height: 48,
 24 |         width: 284,
 25 |         fontSize: 18,
 26 |         fontWeight: 200,
 27 |         padding: '12px 16px',
 28 |       }}
 29 |     />
 30 |   </div>
 31 | );
 32 | 
 33 | storiesOf('SingleDatePicker (SDP)', module)
 34 |   .add('default', withInfo()(() => (
 35 |     <SingleDatePickerWrapper />
 36 |   )))
 37 |   .add('as part of a form', withInfo()(() => (
 38 |     <div>
 39 |       <SingleDatePickerWrapper />
 40 |       <TestInput placeholder="Input 1" />
 41 |       <TestInput placeholder="Input 2" />
 42 |       <TestInput placeholder="Input 3" />
 43 |     </div>
 44 |    )))
 45 |   .add('non-english locale (Chinese)', withInfo()(() => {
 46 |     moment.locale('zh-cn');
 47 |     return (
 48 |       <SingleDatePickerWrapper
 49 |         placeholder="入住日期"
 50 |         monthFormat="YYYY[年]MMMM"
 51 |         phrases={{
 52 |           closeDatePicker: '关闭',
 53 |           clearDate: '清除日期',
 54 |         }}
 55 |       />
 56 |     );
 57 |   }))
 58 |   .add('non-english locale (Persian)', withInfo()(() => {
 59 |     moment.locale('fa');
 60 |     return (
 61 |       <SingleDatePickerWrapper
 62 |         placeholder="تقویم فارسی"
 63 |         renderMonthText={month => momentJalaali(month).format('jMMMM jYYYY')}
 64 |         renderDayContents={day => momentJalaali(day).format('jD')}
 65 |       />
 66 |     );
 67 |   }))
 68 |   .add('with DirectionProvider', withInfo()(() => (
 69 |     <DirectionProvider direction={DIRECTIONS.RTL}>
 70 |       <SingleDatePickerWrapper
 71 |         placeholder="تاریخ شروع"
 72 |         anchorDirection={ANCHOR_RIGHT}
 73 |         showDefaultInputIcon
 74 |         showClearDate
 75 |         isRTL
 76 |       />
 77 |     </DirectionProvider>
 78 |   )))
 79 |   .add('with custom month navigation and blocked navigation (minDate and maxDate)', withInfo()(() => (
 80 |     <SingleDatePickerWrapper
 81 |       minDate={moment().subtract(2, 'months').startOf('month')}
 82 |       maxDate={moment().add(2, 'months').endOf('month')}
 83 |     />
 84 |   )))
 85 |   .add('with custom isOutsideRange and month navigation and blocked navigation (minDate and maxDate)', withInfo()(() => {
 86 |     const minDate = moment().subtract(2, 'months').startOf('month')
 87 |     const maxDate = moment().add(2, 'months').endOf('month')
 88 |     const isOutsideRange = day => isInclusivelyBeforeDay(day, minDate) || isInclusivelyAfterDay(day, maxDate)
 89 |     return (
 90 |     <SingleDatePickerWrapper
 91 |       minDate={minDate}
 92 |       maxDate={maxDate}
 93 |       isOutsideRange={isOutsideRange}
 94 |     />
 95 |   )}))
 96 |   .add('vertical with custom height', withInfo()(() => (
 97 |     <SingleDatePickerWrapper
 98 |       orientation={VERTICAL_ORIENTATION}
 99 |       verticalHeight={568}
100 |     />
101 |   )))
102 |   .add('with custom autoComplete attribute', withInfo()(() => (
103 |     <SingleDatePickerWrapper
104 |     autoComplete="datePicker"
105 |     />
106 |   )));
107 | 


--------------------------------------------------------------------------------
/stories/SingleDatePicker_day.js:
--------------------------------------------------------------------------------
 1 | import React from 'react';
 2 | import moment from 'moment';
 3 | import { storiesOf } from '@storybook/react';
 4 | import { withInfo } from '@storybook/addon-info';
 5 | 
 6 | import isInclusivelyAfterDay from '../src/utils/isInclusivelyAfterDay';
 7 | import isSameDay from '../src/utils/isSameDay';
 8 | 
 9 | import SingleDatePickerWrapper from '../examples/SingleDatePickerWrapper';
10 | 
11 | const datesList = [
12 |   moment(),
13 |   moment().add(1, 'days'),
14 |   moment().add(3, 'days'),
15 |   moment().add(9, 'days'),
16 |   moment().add(10, 'days'),
17 |   moment().add(11, 'days'),
18 |   moment().add(12, 'days'),
19 |   moment().add(13, 'days'),
20 | ];
21 | 
22 | storiesOf('SDP - Day Props', module)
23 |   .add('default', withInfo()(() => (
24 |     <SingleDatePickerWrapper autoFocus />
25 |   )))
26 |   .add('allows all days, including past days', withInfo()(() => (
27 |     <SingleDatePickerWrapper
28 |       isOutsideRange={() => false}
29 |       autoFocus
30 |     />
31 |   )))
32 |   .add('allows next two weeks only', withInfo()(() => (
33 |     <SingleDatePickerWrapper
34 |       isOutsideRange={day =>
35 |         !isInclusivelyAfterDay(day, moment()) ||
36 |         isInclusivelyAfterDay(day, moment().add(2, 'weeks'))
37 |       }
38 |       autoFocus
39 |     />
40 |   )))
41 |   .add('with some blocked dates', withInfo()(() => (
42 |     <SingleDatePickerWrapper
43 |       isDayBlocked={day1 => datesList.some(day2 => isSameDay(day1, day2))}
44 |       autoFocus
45 |     />
46 |   )))
47 |   .add('with some highlighted dates', withInfo()(() => (
48 |     <SingleDatePickerWrapper
49 |       isDayHighlighted={day1 => datesList.some(day2 => isSameDay(day1, day2))}
50 |       autoFocus
51 |     />
52 |   )))
53 |   .add('blocks fridays', withInfo()(() => (
54 |     <SingleDatePickerWrapper
55 |       isDayBlocked={day => moment.weekdays(day.weekday()) === 'Friday'}
56 |       autoFocus
57 |     />
58 |   )))
59 |   .add('with custom daily details', withInfo()(() => (
60 |     <SingleDatePickerWrapper
61 |       numberOfMonths={1}
62 |       renderDayContents={day => day.format('ddd')}
63 |       autoFocus
64 |     />
65 |   )));
66 | 


--------------------------------------------------------------------------------
/stories/SingleDatePicker_input.js:
--------------------------------------------------------------------------------
  1 | import React from 'react';
  2 | import moment from 'moment';
  3 | import { storiesOf } from '@storybook/react';
  4 | import { withInfo } from '@storybook/addon-info';
  5 | 
  6 | import SingleDatePickerWrapper from '../examples/SingleDatePickerWrapper';
  7 | 
  8 | const TestCustomInputIcon = () => (
  9 |   <span
 10 |     style={{
 11 |       border: '1px solid #dce0e0',
 12 |       backgroundColor: '#fff',
 13 |       color: '#484848',
 14 |       padding: '3px',
 15 |     }}
 16 |   >
 17 |     C
 18 |   </span>
 19 | );
 20 | 
 21 | storiesOf('SDP - Input Props', module)
 22 |   .add('default', withInfo()(() => (
 23 |     <SingleDatePickerWrapper
 24 |       initialDate={moment().add(3, 'days')}
 25 |     />
 26 |   )))
 27 |   .add('disabled', withInfo()(() => (
 28 |     <SingleDatePickerWrapper
 29 |       initialDate={moment().add(3, 'days')}
 30 |       disabled
 31 |     />
 32 |   )))
 33 |   .add('readOnly', withInfo()(() => (
 34 |     <SingleDatePickerWrapper
 35 |       initialDate={moment().add(3, 'days')}
 36 |       readOnly
 37 |     />
 38 |   )))
 39 |   .add('with clear dates button', withInfo()(() => (
 40 |     <SingleDatePickerWrapper
 41 |       initialDate={moment().add(3, 'days')}
 42 |       showClearDate
 43 |     />
 44 |   )))
 45 |   .add('reopens DayPicker on clear dates', withInfo()(() => (
 46 |     <SingleDatePickerWrapper
 47 |       initialDate={moment().add(3, 'days')}
 48 |       showClearDate
 49 |       reopenPickerOnClearDate
 50 |     />
 51 |   )))
 52 |   .add('with custom display format', withInfo()(() => (
 53 |     <SingleDatePickerWrapper
 54 |       initialDate={moment().add(3, 'days')}
 55 |       displayFormat="MMM D"
 56 |     />
 57 |   )))
 58 |   .add('with show calendar icon', withInfo()(() => (
 59 |     <SingleDatePickerWrapper
 60 |       initialDate={moment().add(3, 'days')}
 61 |       showDefaultInputIcon
 62 |     />
 63 |   )))
 64 |   .add('with custom show calendar icon', withInfo()(() => (
 65 |     <SingleDatePickerWrapper
 66 |       initialDate={moment().add(3, 'days')}
 67 |       customInputIcon={<TestCustomInputIcon />}
 68 |     />
 69 |   )))
 70 |   .add('with show calendar icon after input', withInfo()(() => (
 71 |     <SingleDatePickerWrapper
 72 |       initialDate={moment().add(3, 'days')}
 73 |       showDefaultInputIcon
 74 |       inputIconPosition="after"
 75 |     />
 76 |   )))
 77 |   .add('with screen reader message', withInfo()(() => (
 78 |     <SingleDatePickerWrapper
 79 |       initialDate={moment().add(3, 'days')}
 80 |       screenReaderInputMessage="Here you could inform screen reader users of the date format, minimum nights, blocked out dates, etc"
 81 |     />
 82 |   )))
 83 |   .add('with custom title attribute', withInfo()(() => (
 84 |     <SingleDatePickerWrapper
 85 |       initialDate={moment().add(3, 'days')}
 86 |       titleText="Here you can set the title attribute of the input, which shows in the tooltip on :hover over the field"
 87 |     />
 88 |   )))
 89 |   .add('noBorder', withInfo()(() => (
 90 |     <SingleDatePickerWrapper
 91 |       initialDate={moment().add(3, 'days')}
 92 |       noBorder
 93 |     />
 94 |   )))
 95 |   .add('block styling', withInfo()(() => (
 96 |     <SingleDatePickerWrapper
 97 |       initialDate={moment().add(3, 'days')}
 98 |       showClearDate
 99 |       block
100 |     />
101 |   )))
102 |   .add('small styling', withInfo()(() => (
103 |     <SingleDatePickerWrapper
104 |       initialDate={moment().add(3, 'days')}
105 |       showClearDate
106 |       small
107 |     />
108 |   )))
109 |   .add('regular styling', withInfo()(() => (
110 |     <SingleDatePickerWrapper
111 |       initialDate={moment().add(3, 'days')}
112 |       showClearDate
113 |       regular
114 |     />
115 |   )));
116 | 


--------------------------------------------------------------------------------
/test/_helpers/describeIfWindow.js:
--------------------------------------------------------------------------------
1 | export default typeof document === 'undefined' ? describe.skip : describe;
2 | 


--------------------------------------------------------------------------------
/test/_helpers/enzymeSetup.js:
--------------------------------------------------------------------------------
1 | import configure from 'enzyme-adapter-react-helper';
2 | 
3 | configure({ disableLifecycleMethods: true });
4 | 


--------------------------------------------------------------------------------
/test/_helpers/registerReactWithStylesInterface.js:
--------------------------------------------------------------------------------
 1 | import ThemedStyleSheet from 'react-with-styles/lib/ThemedStyleSheet';
 2 | import aphroditeInterface from 'react-with-styles-interface-aphrodite';
 3 | import { StyleSheetTestUtils } from 'aphrodite';
 4 | 
 5 | import DefaultTheme from '../../src/theme/DefaultTheme';
 6 | 
 7 | ThemedStyleSheet.registerTheme(DefaultTheme);
 8 | ThemedStyleSheet.registerInterface(aphroditeInterface);
 9 | 
10 | beforeEach(() => {
11 |   StyleSheetTestUtils.suppressStyleInjection();
12 | });
13 | 
14 | afterEach(() => {
15 |   StyleSheetTestUtils.clearBufferAndResumeStyleInjection();
16 | });
17 | 


--------------------------------------------------------------------------------
/test/_helpers/restoreSinonStubs.js:
--------------------------------------------------------------------------------
1 | import sinon from 'sinon-sandbox';
2 | 
3 | afterEach(() => {
4 |   sinon.restore();
5 | });
6 | 


--------------------------------------------------------------------------------
/test/_helpers/withTouchSupport.js:
--------------------------------------------------------------------------------
 1 | const wrap = require('mocha-wrap');
 2 | const withGlobal = require('mocha-wrap/withGlobal');
 3 | const withOverride = require('mocha-wrap/withOverride');
 4 | 
 5 | function withTouchSupport() {
 6 |   return this
 7 |     .use(withGlobal, 'window', () => (typeof window !== 'undefined' ? window : {}))
 8 |     .use(withOverride, () => window, 'ontouchstart', () => window.ontouchstart || (() => {}))
 9 |     .use(withGlobal, 'navigator', () => (typeof navigator !== 'undefined' ? navigator : {}))
10 |     .use(withOverride, () => navigator, 'maxTouchPoints', () => navigator.maxTouchPoints || true);
11 | }
12 | 
13 | wrap.register(withTouchSupport);
14 | 


--------------------------------------------------------------------------------
/test/browser-main.js:
--------------------------------------------------------------------------------
1 | const requireAll = (requireContext) => requireContext.keys().forEach(requireContext);
2 | 
3 | if (typeof window !== 'undefined') {
4 |   requireAll(require.context('./_helpers', true, /.jsx?$/));
5 |   requireAll(require.context('.', true, /.jsx?$/));
6 | }
7 | 


--------------------------------------------------------------------------------
/test/components/CalendarMonthGrid_spec.jsx:
--------------------------------------------------------------------------------
  1 | import React from 'react';
  2 | import { expect } from 'chai';
  3 | import { shallow } from 'enzyme';
  4 | import moment from 'moment';
  5 | import sinon from 'sinon-sandbox';
  6 | 
  7 | import CalendarMonth from '../../src/components/CalendarMonth';
  8 | import CalendarMonthGrid from '../../src/components/CalendarMonthGrid';
  9 | 
 10 | import getTransformStyles from '../../src/utils/getTransformStyles';
 11 | 
 12 | describe('CalendarMonthGrid', () => {
 13 |   it('the number of CalendarMonths rendered matches props.numberOfMonths + 2', () => {
 14 |     const NUM_OF_MONTHS = 5;
 15 |     const wrapper = shallow(<CalendarMonthGrid numberOfMonths={NUM_OF_MONTHS} />).dive();
 16 |     expect(wrapper.find(CalendarMonth)).to.have.lengthOf(NUM_OF_MONTHS + 2);
 17 |   });
 18 | 
 19 |   it('has style equal to getTransformStyles(foo)', () => {
 20 |     const translationValue = 100;
 21 |     const transformStyles = getTransformStyles(`translateX(${translationValue}px)`);
 22 |     const wrapper = shallow(<CalendarMonthGrid translationValue={translationValue} />).dive();
 23 |     Object.keys(transformStyles).forEach((key) => {
 24 |       expect(wrapper.prop('style')[key]).to.equal(transformStyles[key]);
 25 |     });
 26 |   });
 27 | 
 28 |   it('does not generate duplicate months', () => {
 29 |     const initialMonth = moment();
 30 |     const wrapper = shallow((
 31 |       <CalendarMonthGrid numberOfMonths={12} initialMonth={initialMonth} />
 32 |     )).dive();
 33 | 
 34 |     wrapper.instance().componentWillReceiveProps({
 35 |       initialMonth,
 36 |       numberOfMonths: 24,
 37 |     });
 38 | 
 39 |     const { months } = wrapper.state();
 40 | 
 41 |     const collisions = months
 42 |       .map((m) => m.format('YYYY-MM'))
 43 |       .reduce((acc, m) => ({ ...acc, [m]: true }), {});
 44 | 
 45 |     expect(Object.keys(collisions).length).to.equal(months.length);
 46 |   });
 47 | 
 48 |   it('does not setState if hasMonthChanged and hasNumberOfMonthsChanged are falsy', () => {
 49 |     const setState = sinon.stub(CalendarMonthGrid.prototype, 'setState');
 50 |     const initialMonth = moment();
 51 |     const wrapper = shallow((
 52 |       <CalendarMonthGrid numberOfMonths={12} initialMonth={initialMonth} />
 53 |     )).dive();
 54 | 
 55 |     wrapper.instance().componentWillReceiveProps({
 56 |       initialMonth,
 57 |       numberOfMonths: 12,
 58 |     });
 59 | 
 60 |     expect(setState.callCount).to.eq(0);
 61 |   });
 62 | 
 63 |   it('works with the same number of months', () => {
 64 |     const initialMonth = moment();
 65 |     const wrapper = shallow((
 66 |       <CalendarMonthGrid numberOfMonths={12} initialMonth={initialMonth} />
 67 |     )).dive();
 68 | 
 69 |     wrapper.instance().componentWillReceiveProps({
 70 |       initialMonth,
 71 |       numberOfMonths: 12,
 72 |       firstVisibleMonthIndex: 0,
 73 |     });
 74 | 
 75 |     const { months } = wrapper.state();
 76 | 
 77 |     const collisions = months
 78 |       .map((m) => m.format('YYYY-MM'))
 79 |       .reduce((acc, m) => ({ ...acc, [m]: true }), {});
 80 | 
 81 |     expect(Object.keys(collisions).length).to.equal(months.length);
 82 |   });
 83 | 
 84 |   describe('#onMonthSelect', () => {
 85 |     it('calls onMonthChange', () => {
 86 |       const onMonthChangeSpy = sinon.spy();
 87 |       const wrapper = shallow(<CalendarMonthGrid onMonthChange={onMonthChangeSpy} />).dive();
 88 |       const currentMonth = moment();
 89 |       const newMonthVal = (currentMonth.month() + 5) % 12;
 90 |       wrapper.instance().onMonthSelect(currentMonth, newMonthVal);
 91 |       expect(onMonthChangeSpy.callCount).to.equal(1);
 92 |     });
 93 |   });
 94 | 
 95 |   describe('#onYearSelect', () => {
 96 |     it('calls onYearChange', () => {
 97 |       const onYearChangeSpy = sinon.spy();
 98 |       const wrapper = shallow(<CalendarMonthGrid onYearChange={onYearChangeSpy} />).dive();
 99 |       const currentMonth = moment();
100 |       const newMonthVal = (currentMonth.month() + 5) % 12;
101 |       wrapper.instance().onYearSelect(currentMonth, newMonthVal);
102 |       expect(onYearChangeSpy.callCount).to.equal(1);
103 |     });
104 |   });
105 | });
106 | 


--------------------------------------------------------------------------------
/test/components/CalendarMonth_spec.jsx:
--------------------------------------------------------------------------------
 1 | import React from 'react';
 2 | import { expect } from 'chai';
 3 | import { shallow, mount } from 'enzyme';
 4 | import sinon from 'sinon-sandbox';
 5 | import moment from 'moment';
 6 | import describeIfWindow from '../_helpers/describeIfWindow';
 7 | 
 8 | import CalendarMonth from '../../src/components/CalendarMonth';
 9 | 
10 | describe('CalendarMonth', () => {
11 |   describe('#render', () => {
12 |     describe('data-visible attribute', () => {
13 |       it('data-visible attribute is truthy if props.isVisible', () => {
14 |         const wrapper = shallow(<CalendarMonth isVisible />).dive();
15 |         expect(wrapper.prop('data-visible')).to.equal(true);
16 |       });
17 | 
18 |       it('data-visible attribute is falsy if !props.isVisible', () => {
19 |         const wrapper = shallow(<CalendarMonth isVisible={false} />).dive();
20 |         expect(wrapper.prop('data-visible')).to.equal(false);
21 |       });
22 |     });
23 | 
24 |     describe('caption', () => {
25 |       it('text is the correctly formatted month title', () => {
26 |         const MONTH = moment();
27 |         const captionWrapper = shallow(<CalendarMonth month={MONTH} />).dive().find('strong');
28 |         expect(captionWrapper.text()).to.equal(MONTH.format('MMMM YYYY'));
29 |       });
30 |     });
31 | 
32 |     it('renderMonthElement renders month element when month changes', () => {
33 |       const renderMonthElementStub = sinon.stub().returns(<div id="month-element" />);
34 |       const wrapper = shallow(<CalendarMonth renderMonthElement={renderMonthElementStub} />).dive();
35 |       wrapper.setProps({ month: moment().subtract(1, 'months') });
36 | 
37 |       const [{
38 |         month,
39 |         onMonthSelect,
40 |         onYearSelect,
41 |         isVisible,
42 |       }] = renderMonthElementStub.getCall(0).args;
43 | 
44 |       expect(moment.isMoment(month)).to.equal(true);
45 |       expect(typeof onMonthSelect).to.equal('function');
46 |       expect(typeof onYearSelect).to.equal('function');
47 |       expect(typeof isVisible).to.equal('boolean');
48 |       expect(wrapper.find('#month-element').exists()).to.equal(true);
49 |     });
50 | 
51 |     describeIfWindow('setMonthTitleHeight', () => {
52 |       beforeEach(() => {
53 |         sinon.stub(window, 'setTimeout').callsFake((handler) => handler());
54 |       });
55 | 
56 |       it('sets the title height after mount', () => {
57 |         const setMonthTitleHeightStub = sinon.stub();
58 |         mount(
59 |           <CalendarMonth
60 |             isVisible
61 |             setMonthTitleHeight={setMonthTitleHeightStub}
62 |           />,
63 |         );
64 | 
65 |         expect(setMonthTitleHeightStub).to.have.property('callCount', 1);
66 |       });
67 | 
68 |       describe('if the callbacks gets set again', () => {
69 |         it('updates the title height', () => {
70 |           const setMonthTitleHeightStub = sinon.stub();
71 |           const wrapper = mount(
72 |             <CalendarMonth
73 |               isVisible
74 |               setMonthTitleHeight={setMonthTitleHeightStub}
75 |             />,
76 |           );
77 | 
78 |           expect(setMonthTitleHeightStub).to.have.property('callCount', 1);
79 | 
80 |           wrapper.setProps({ setMonthTitleHeight: null });
81 | 
82 |           wrapper.setProps({ setMonthTitleHeight: setMonthTitleHeightStub });
83 |           expect(setMonthTitleHeightStub).to.have.property('callCount', 2);
84 |         });
85 |       });
86 |     });
87 |   });
88 | });
89 | 


--------------------------------------------------------------------------------
/test/components/CalendarWeek_spec.jsx:
--------------------------------------------------------------------------------
 1 | import React from 'react';
 2 | import { expect } from 'chai';
 3 | import { shallow } from 'enzyme';
 4 | 
 5 | import CalendarWeek from '../../src/components/CalendarWeek';
 6 | 
 7 | import CalendarDay from '../../src/components/CalendarDay';
 8 | 
 9 | describe('CalendarWeek', () => {
10 |   it('renders a tr', () => {
11 |     const wrapper = shallow((
12 |       <CalendarWeek>
13 |         <CalendarDay />
14 |       </CalendarWeek>
15 |     ));
16 |     expect(wrapper.is('tr')).to.equal(true);
17 |   });
18 | });
19 | 


--------------------------------------------------------------------------------
/test/components/KeyboardShortcutRow_spec.jsx:
--------------------------------------------------------------------------------
 1 | import React from 'react';
 2 | import { expect } from 'chai';
 3 | import { shallow } from 'enzyme';
 4 | 
 5 | import KeyboardShortcutRow from '../../src/components/KeyboardShortcutRow';
 6 | 
 7 | describe('KeyboardShortcutRow', () => {
 8 |   it('is an li', () => {
 9 |     const wrapper = shallow((
10 |       <KeyboardShortcutRow
11 |         unicode="foo"
12 |         label="bar"
13 |         action="baz"
14 |       />
15 |     )).dive();
16 |     expect(wrapper.is('li')).to.equal(true);
17 |   });
18 | });
19 | 


--------------------------------------------------------------------------------
/test/components/SingleDatePickerInput_spec.jsx:
--------------------------------------------------------------------------------
  1 | import React from 'react';
  2 | import { expect } from 'chai';
  3 | import { shallow } from 'enzyme';
  4 | import sinon from 'sinon-sandbox';
  5 | 
  6 | import SingleDatePickerInput from '../../src/components/SingleDatePickerInput';
  7 | import DateInput from '../../src/components/DateInput';
  8 | import { SingleDatePickerInputPhrases } from '../../src/defaultPhrases';
  9 | 
 10 | describe('SingleDatePickerInput', () => {
 11 |   describe('render', () => {
 12 |     it('should render any children provided', () => {
 13 |       const Child = () => <div>CHILD</div>;
 14 | 
 15 |       const wrapper = shallow((
 16 |         <SingleDatePickerInput id="date">
 17 |           <Child />
 18 |         </SingleDatePickerInput>
 19 |       )).dive();
 20 |       expect(wrapper.find(Child)).to.have.lengthOf(1);
 21 |     });
 22 |   });
 23 | 
 24 |   describe('clear date', () => {
 25 |     describe('props.showClearDate is falsy', () => {
 26 |       it('does not render a clear date button', () => {
 27 |         const wrapper = shallow(<SingleDatePickerInput id="date" showClearDate={false} />).dive();
 28 |         expect(wrapper.find('button')).to.have.lengthOf(0);
 29 |       });
 30 |     });
 31 | 
 32 |     describe('props.showClearDate is truthy', () => {
 33 |       it('has a clear date button', () => {
 34 |         const wrapper = shallow(<SingleDatePickerInput id="date" showClearDate />).dive();
 35 |         expect(wrapper.find('button')).to.have.lengthOf(1);
 36 |       });
 37 |     });
 38 | 
 39 |     describe('props.customCloseIcon is a React Element', () => {
 40 |       it('has custom icon', () => {
 41 |         const wrapper = shallow((
 42 |           <SingleDatePickerInput
 43 |             id="date"
 44 |             showClearDate
 45 |             customCloseIcon={<span className="custom-close-icon" />}
 46 |           />
 47 |         )).dive();
 48 |         expect(wrapper.find('.custom-close-icon')).to.have.lengthOf(1);
 49 |       });
 50 |     });
 51 |   });
 52 | 
 53 |   describe('show calendar icon', () => {
 54 |     describe('props.showInputIcon is falsy', () => {
 55 |       it('does not have a calendar button', () => {
 56 |         const wrapper = shallow((
 57 |           <SingleDatePickerInput id="date" showDefaultInputIcon={false} />
 58 |         )).dive();
 59 |         expect(wrapper.find('button')).to.have.lengthOf(0);
 60 |       });
 61 |     });
 62 | 
 63 |     describe('props.showInputIcon is truthy', () => {
 64 |       it('has button', () => {
 65 |         const wrapper = shallow(<SingleDatePickerInput id="date" showDefaultInputIcon />).dive();
 66 |         expect(wrapper.find('button')).to.have.lengthOf(1);
 67 |       });
 68 |     });
 69 | 
 70 |     describe('props.customInputIcon is a React Element', () => {
 71 |       it('has custom icon', () => {
 72 |         const wrapper = shallow((
 73 |           <SingleDatePickerInput
 74 |             id="date"
 75 |             customInputIcon={<span className="custom-icon" />}
 76 |           />
 77 |         )).dive();
 78 |         expect(wrapper.find('.custom-icon')).to.have.lengthOf(1);
 79 |       });
 80 |     });
 81 |   });
 82 | 
 83 |   describe('clear date interactions', () => {
 84 |     describe('onClick', () => {
 85 |       it('props.onClearDate gets triggered', () => {
 86 |         const onClearDateSpy = sinon.spy();
 87 |         const wrapper = shallow((
 88 |           <SingleDatePickerInput
 89 |             id="date"
 90 |             onClearDate={onClearDateSpy}
 91 |             showClearDate
 92 |           />
 93 |         )).dive();
 94 |         const clearDateWrapper = wrapper.find('button');
 95 |         clearDateWrapper.simulate('click');
 96 |         expect(onClearDateSpy).to.have.property('called', true);
 97 |       });
 98 |     });
 99 |   });
100 | 
101 |   describe('screen reader message', () => {
102 |     describe('props.screenReaderMessage is falsy', () => {
103 |       it('default value is passed to DateInput', () => {
104 |         const wrapper = shallow(<SingleDatePickerInput id="date" />).dive();
105 |         const dateInput = wrapper.find(DateInput);
106 |         expect(dateInput).to.have.lengthOf(1);
107 |         expect(dateInput.props()).to.have.property(
108 |           'screenReaderMessage',
109 |           SingleDatePickerInputPhrases.keyboardForwardNavigationInstructions,
110 |         );
111 |       });
112 |     });
113 | 
114 |     describe('props.screenReaderMessage is truthy', () => {
115 |       it('prop value is passed to DateInput', () => {
116 |         const message = 'test message';
117 |         const wrapper = shallow((
118 |           <SingleDatePickerInput
119 |             id="date"
120 |             screenReaderMessage={message}
121 |           />
122 |         )).dive();
123 |         const dateInput = wrapper.find(DateInput);
124 |         expect(dateInput).to.have.lengthOf(1);
125 |         expect(dateInput.props()).to.have.property('screenReaderMessage', message);
126 |       });
127 |     });
128 |   });
129 | });
130 | 


--------------------------------------------------------------------------------
/test/mocha.opts:
--------------------------------------------------------------------------------
1 | --require airbnb-js-shims
2 | test-build/**/*.js
3 | 


--------------------------------------------------------------------------------
/test/utils/calculateDimension_spec.js:
--------------------------------------------------------------------------------
 1 | import { expect } from 'chai';
 2 | 
 3 | import calculateDimension from '../../src/utils/calculateDimension';
 4 | 
 5 | describe('#calculateDimension', () => {
 6 |   it('returns 0 for an empty element', () => {
 7 |     expect(calculateDimension(null, 'width')).to.equal(0);
 8 |     expect(calculateDimension(null, 'width', false)).to.equal(0);
 9 |     expect(calculateDimension(null, 'width', true)).to.equal(0);
10 |   });
11 | 
12 |   describe('borderBox true', () => {
13 |     const el = {
14 |       offsetWidth: 17,
15 |       offsetHeight: 42,
16 |     };
17 | 
18 |     it('returns el.offsetWidth for "width"', () => {
19 |       expect(calculateDimension(el, 'width', true)).to.equal(el.offsetWidth);
20 |     });
21 | 
22 |     it('returns el.offsetHeight for "height"', () => {
23 |       expect(calculateDimension(el, 'height', true)).to.equal(el.offsetHeight);
24 |     });
25 |   });
26 | 
27 |   /* Requires a DOM */
28 |   describe.skip('withMargin false and borderBox true', () => {
29 |     let testElement = null;
30 | 
31 |     beforeEach(() => {
32 |       testElement = document.createElement('div');
33 | 
34 |       testElement.style.width = '100px';
35 |       testElement.style.height = '250px';
36 |       testElement.style.padding = '15px 10px';
37 |       testElement.style.border = '1px solid red';
38 |       testElement.style.margin = '3px 6px 5px 2px';
39 |       testElement.boxSizing = 'border-box';
40 |     });
41 | 
42 |     it('calculates border-box height', () => {
43 |       expect(calculateDimension(testElement, 'height', true)).to.equal(282);
44 |     });
45 | 
46 |     it('calculates border-box height with margin', () => {
47 |       expect(calculateDimension(testElement, 'height', true, true)).to.equal(290);
48 |     });
49 | 
50 |     it('calculates border-box width', () => {
51 |       expect(calculateDimension(testElement, 'width', true)).to.equal(122);
52 |     });
53 | 
54 |     it('calculates border-box width with margin', () => {
55 |       expect(calculateDimension(testElement, 'width', true, true)).to.equal(130);
56 |     });
57 | 
58 |     it('calculates content-box height', () => {
59 |       expect(calculateDimension(testElement, 'height')).to.equal(250);
60 |     });
61 | 
62 |     it('calculates content-box height with margin', () => {
63 |       expect(calculateDimension(testElement, 'height', false, true)).to.equal(258);
64 |     });
65 | 
66 |     it('calculates content-box width', () => {
67 |       expect(calculateDimension(testElement, 'width')).to.equal(100);
68 |     });
69 | 
70 |     it('calculates content-box width with margin', () => {
71 |       expect(calculateDimension(testElement, 'width', false, true)).to.equal(108);
72 |     });
73 |   });
74 | });
75 | 


--------------------------------------------------------------------------------
/test/utils/disableScroll_spec.js:
--------------------------------------------------------------------------------
  1 | import { expect } from 'chai';
  2 | 
  3 | import disableScroll, {
  4 |   getScrollParent,
  5 |   getScrollAncestorsOverflowY,
  6 | } from '../../src/utils/disableScroll';
  7 | 
  8 | import describeIfWindow from '../_helpers/describeIfWindow';
  9 | 
 10 | const createScrollContainer = (level = 1) => {
 11 |   const el = document.createElement('div');
 12 |   el.style.width = '300px';
 13 |   el.style.height = `${level * 100}px`;
 14 |   el.style.overflow = 'auto';
 15 |   return el;
 16 | };
 17 | 
 18 | const createScrollContent = () => {
 19 |   const el = document.createElement('div');
 20 |   el.style.width = '100%';
 21 |   el.style.height = '99999px';
 22 |   return el;
 23 | };
 24 | 
 25 | const createTargetElement = () => {
 26 |   const el = document.createElement('div');
 27 |   return el;
 28 | };
 29 | 
 30 | describeIfWindow('#disableScroll', () => {
 31 |   let grandParentScrollContainer;
 32 |   let parentScrollContainer;
 33 |   let scrollContent;
 34 |   let targetElement;
 35 | 
 36 |   before(() => {
 37 |     grandParentScrollContainer = createScrollContainer(1);
 38 |     parentScrollContainer = createScrollContainer(2);
 39 |     scrollContent = createScrollContent();
 40 |     targetElement = createTargetElement();
 41 |     scrollContent.appendChild(targetElement);
 42 |   });
 43 | 
 44 |   describe('#getScrollParent', () => {
 45 |     describe('with no parent', () => {
 46 |       before(() => {
 47 |         document.body.appendChild(scrollContent);
 48 |       });
 49 | 
 50 |       after(() => {
 51 |         document.body.removeChild(scrollContent);
 52 |       });
 53 | 
 54 |       it('returns scrolling root if no scroll parent', () => {
 55 |         const scrollParent = getScrollParent(targetElement);
 56 |         expect(scrollParent).to.equal(document.documentElement);
 57 |       });
 58 |     });
 59 | 
 60 |     describe('with a single scroll container', () => {
 61 |       before(() => {
 62 |         parentScrollContainer.appendChild(scrollContent);
 63 |         document.body.appendChild(parentScrollContainer);
 64 |       });
 65 | 
 66 |       after(() => {
 67 |         parentScrollContainer.appendChild(scrollContent);
 68 |         document.body.removeChild(parentScrollContainer);
 69 |       });
 70 | 
 71 |       it('returns the scroll container', () => {
 72 |         const scrollParent = getScrollParent(targetElement);
 73 |         expect(scrollParent).to.equal(parentScrollContainer);
 74 |       });
 75 |     });
 76 | 
 77 |     describe('with multiple scroll containers', () => {
 78 |       before(() => {
 79 |         parentScrollContainer.appendChild(scrollContent);
 80 |         grandParentScrollContainer.appendChild(parentScrollContainer);
 81 |         document.body.appendChild(grandParentScrollContainer);
 82 |       });
 83 | 
 84 |       after(() => {
 85 |         parentScrollContainer.removeChild(scrollContent);
 86 |         grandParentScrollContainer.removeChild(parentScrollContainer);
 87 |         document.body.removeChild(grandParentScrollContainer);
 88 |       });
 89 | 
 90 |       it('returns the closest scroll container', () => {
 91 |         const scrollParent = getScrollParent(targetElement);
 92 |         expect(scrollParent).to.equal(parentScrollContainer);
 93 |       });
 94 |     });
 95 |   });
 96 | 
 97 |   describe('#getScrollAncestorsOverflowY', () => {
 98 |     before(() => {
 99 |       parentScrollContainer.appendChild(scrollContent);
100 |       grandParentScrollContainer.appendChild(parentScrollContainer);
101 |       document.body.appendChild(grandParentScrollContainer);
102 |     });
103 | 
104 |     after(() => {
105 |       parentScrollContainer.removeChild(scrollContent);
106 |       grandParentScrollContainer.removeChild(parentScrollContainer);
107 |       document.body.removeChild(grandParentScrollContainer);
108 |     });
109 | 
110 |     it('returns a map with the overflowY of all scrollable ancestors', () => {
111 |       const scrollAncestorsOverflowY = getScrollAncestorsOverflowY(targetElement);
112 |       expect(scrollAncestorsOverflowY.size).to.equal(3); // both scroll containers + root
113 | 
114 |       expect(scrollAncestorsOverflowY.has(parentScrollContainer)).to.equal(true);
115 |       expect(scrollAncestorsOverflowY.has(grandParentScrollContainer)).to.equal(true);
116 |       expect(scrollAncestorsOverflowY.has(document.documentElement)).to.equal(true);
117 | 
118 |       expect(scrollAncestorsOverflowY.get(parentScrollContainer)).to.equal('auto');
119 |       expect(scrollAncestorsOverflowY.get(grandParentScrollContainer)).to.equal('auto');
120 |       expect(scrollAncestorsOverflowY.get(document.documentElement)).to.be.a('string');
121 |     });
122 |   });
123 | 
124 |   describe('#disableScroll', () => {
125 |     before(() => {
126 |       parentScrollContainer.appendChild(scrollContent);
127 |       grandParentScrollContainer.appendChild(parentScrollContainer);
128 |       document.body.appendChild(grandParentScrollContainer);
129 |     });
130 | 
131 |     after(() => {
132 |       parentScrollContainer.removeChild(scrollContent);
133 |       grandParentScrollContainer.removeChild(parentScrollContainer);
134 |       document.body.removeChild(grandParentScrollContainer);
135 |     });
136 | 
137 |     describe('disable', () => {
138 |       it('should set all scroll ancestors overflow to hidden', () => {
139 |         const enableScroll = disableScroll(targetElement);
140 |         const scrollAncestorsOverflowY = getScrollAncestorsOverflowY(targetElement);
141 | 
142 |         // eslint-disable-next-line no-restricted-syntax
143 |         for (const [, overflowY] of scrollAncestorsOverflowY) {
144 |           expect(overflowY).to.equal('hidden');
145 |         }
146 | 
147 |         // reset to initial state
148 |         enableScroll();
149 |       });
150 |     });
151 | 
152 |     describe('enable', () => {
153 |       it('should set all scroll ancestors overflow to their previous value', () => {
154 |         const scrollAncestorsOverflowYBefore = getScrollAncestorsOverflowY(targetElement);
155 |         const enableScroll = disableScroll(targetElement);
156 |         enableScroll();
157 |         const scrollAncestorsOverflowY = getScrollAncestorsOverflowY(targetElement);
158 | 
159 |         // eslint-disable-next-line no-restricted-syntax
160 |         for (const [element, overflowY] of scrollAncestorsOverflowY) {
161 |           expect(scrollAncestorsOverflowYBefore.get(element)).to.equal(overflowY);
162 |         }
163 |       });
164 |     });
165 |   });
166 | });
167 | 


--------------------------------------------------------------------------------
/test/utils/getActiveElement_spec.js:
--------------------------------------------------------------------------------
 1 | import { expect } from 'chai';
 2 | import wrap from 'mocha-wrap';
 3 | 
 4 | import getActiveElement from '../../src/utils/getActiveElement';
 5 | 
 6 | const describeIfNoWindow = typeof document === 'undefined' ? describe : describe.skip;
 7 | const test = 'FOOBARBAZ';
 8 | 
 9 | describeIfNoWindow('getActiveElement', () => {
10 |   describe('without `document`', () => {
11 |     it('returns false', () => {
12 |       expect(typeof document).to.equal('undefined');
13 |       expect(getActiveElement()).to.equal(false);
14 |     });
15 |   });
16 | 
17 |   wrap()
18 |   .withGlobal('document', () => ({}))
19 |   .describe('with `document`', () => {
20 |     it('returns undefined without `document.activeElement`', () => {
21 |       expect(getActiveElement()).to.be.an('undefined');
22 |     });
23 | 
24 |     wrap()
25 |     .withOverride(() => document, 'activeElement', () => test)
26 |     .it('returns activeElement value with `document.activeElement', () => {
27 |       expect(getActiveElement()).to.equal(test);
28 |     });
29 |   });
30 | });
31 | 


--------------------------------------------------------------------------------
/test/utils/getCalendarMonthWidth_spec.js:
--------------------------------------------------------------------------------
 1 | import { expect } from 'chai';
 2 | 
 3 | import getCalendarMonthWidth from '../../src/utils/getCalendarMonthWidth';
 4 | 
 5 | describe('#getCalendarMonthWidth', () => {
 6 |   it('correctly calculates width for default day size of 39', () => {
 7 |     expect(getCalendarMonthWidth(39, 13)).to.equal(300);
 8 |   });
 9 | 
10 |   it('returns a number when padding is undefined', () => {
11 |     expect(Number.isNaN(getCalendarMonthWidth(39, undefined))).to.equal(false);
12 |   });
13 | 
14 |   it('returns a number when padding is null', () => {
15 |     expect(Number.isNaN(getCalendarMonthWidth(39, null))).to.equal(false);
16 |   });
17 | });
18 | 


--------------------------------------------------------------------------------
/test/utils/getDetachedContainerStyles_spec.js:
--------------------------------------------------------------------------------
 1 | import { expect } from 'chai';
 2 | import wrap from 'mocha-wrap';
 3 | 
 4 | import getDetachedContainerStyles from '../../src/utils/getDetachedContainerStyles';
 5 | 
 6 | import {
 7 |   OPEN_DOWN,
 8 |   OPEN_UP,
 9 |   ANCHOR_LEFT,
10 |   ANCHOR_RIGHT,
11 | } from '../../src/constants';
12 | 
13 | const describeIfNoWindow = typeof document === 'undefined' ? describe : describe.skip;
14 | 
15 | const windowWidth = 100;
16 | const windowHeight = 100;
17 | 
18 | // Fake 30x100px element on x,y = 10,10
19 | const referenceElRect = {
20 |   top: 10,
21 |   bottom: 40,
22 |   left: 10,
23 |   right: 110,
24 | };
25 | const referenceEl = {
26 |   getBoundingClientRect() {
27 |     return referenceElRect;
28 |   },
29 | };
30 | 
31 | describeIfNoWindow('#getDetachedContainerStyles', () => {
32 |   wrap()
33 |     .withGlobal('window', () => ({}))
34 |     .withOverride(() => window, 'innerWidth', () => windowWidth)
35 |     .withOverride(() => window, 'innerHeight', () => windowHeight)
36 |     .describe('with `window`', () => {
37 |       describe('on down-left positioning', () => {
38 |         it('returns translation from top-left of window to top-left of reference el', () => {
39 |           const styles = getDetachedContainerStyles(OPEN_DOWN, ANCHOR_LEFT, referenceEl);
40 |           expect(styles.transform).to.equal(`translate3d(${Math.round(referenceElRect.left)}px, ${Math.round(referenceElRect.top)}px, 0)`);
41 |         });
42 |       });
43 | 
44 |       describe('on up-left positioning', () => {
45 |         it('returns translation from bottom-left of window to bottom-left of reference el', () => {
46 |           const styles = getDetachedContainerStyles(OPEN_UP, ANCHOR_LEFT, referenceEl);
47 |           const offsetY = -(windowHeight - referenceElRect.bottom);
48 |           expect(styles.transform).to.equal(`translate3d(${Math.round(referenceElRect.left)}px, ${Math.round(offsetY)}px, 0)`);
49 |         });
50 |       });
51 | 
52 |       describe('on down-right positioning', () => {
53 |         it('returns translation from top-right of window to top-right of reference el', () => {
54 |           const styles = getDetachedContainerStyles(OPEN_DOWN, ANCHOR_RIGHT, referenceEl);
55 |           const offsetX = -(windowWidth - referenceElRect.right);
56 |           expect(styles.transform).to.equal(`translate3d(${Math.round(offsetX)}px, ${Math.round(referenceElRect.top)}px, 0)`);
57 |         });
58 |       });
59 | 
60 |       describe('on up-right positioning', () => {
61 |         it('returns translation from bottom-right of window to bottom-right of reference el', () => {
62 |           const styles = getDetachedContainerStyles(OPEN_UP, ANCHOR_RIGHT, referenceEl);
63 |           const offsetX = -(windowWidth - referenceElRect.right);
64 |           const offsetY = -(windowHeight - referenceElRect.bottom);
65 |           expect(styles.transform).to.equal(`translate3d(${Math.round(offsetX)}px, ${Math.round(offsetY)}px, 0)`);
66 |         });
67 |       });
68 |     });
69 | });
70 | 


--------------------------------------------------------------------------------
/test/utils/getInputHeight_spec.js:
--------------------------------------------------------------------------------
 1 | import { expect } from 'chai';
 2 | import getInputHeight from '../../src/utils/getInputHeight';
 3 | 
 4 | const theme = {
 5 |   font: {
 6 |     input: {
 7 |       lineHeight: 13,
 8 |       lineHeight_small: 7,
 9 |     },
10 |   },
11 |   spacing: {
12 |     inputPadding: 10,
13 |     displayTextPaddingVertical: 8,
14 |     displayTextPaddingTop: 10,
15 |     displayTextPaddingBottom: 12,
16 |     displayTextPaddingVertical_small: 2,
17 |     displayTextPaddingTop_small: 4,
18 |     displayTextPaddingBottom_small: 6,
19 |   },
20 | };
21 | 
22 | describe('#getInputHeight', () => {
23 |   it('returns the expected value with falsy second arg', () => {
24 |     const inputHeight = getInputHeight(theme);
25 |     expect(inputHeight).to.equal(55);
26 |   });
27 | 
28 |   it('returns the expected value with truthy second arg', () => {
29 |     const inputHeight = getInputHeight(theme, true);
30 |     expect(inputHeight).to.equal(37);
31 |   });
32 | });
33 | 


--------------------------------------------------------------------------------
/test/utils/getNumberOfCalendarMonthWeeks_spec.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | import { expect } from 'chai';
 3 | 
 4 | import getNumberOfCalendarMonthWeeks from '../../src/utils/getNumberOfCalendarMonthWeeks';
 5 | 
 6 | describe('getNumberOfCalendarMonthWeeks', () => {
 7 |   it('returns 4 weeks for a 4-week month', () => {
 8 |     const february2018 = moment('2018-02-01', 'YYYY-MM-DD');
 9 |     expect(getNumberOfCalendarMonthWeeks(february2018, 4)).to.equal(4);
10 |   });
11 | 
12 |   it('returns 5 weeks for a 5-week month', () => {
13 |     const july2018 = moment('2018-07-01', 'YYYY-MM-DD');
14 |     expect(getNumberOfCalendarMonthWeeks(july2018, 0)).to.equal(5);
15 |   });
16 | 
17 |   it('returns 6 weeks for a 6-week month', () => {
18 |     const september2018 = moment('2018-09-01', 'YYYY-MM-DD');
19 |     expect(getNumberOfCalendarMonthWeeks(september2018, 0)).to.equal(6);
20 |   });
21 | 
22 |   it('changing the first day of week changes the number of weeks', () => {
23 |     const september2018 = moment('2018-09-01', 'YYYY-MM-DD');
24 |     expect(getNumberOfCalendarMonthWeeks(september2018, 6)).to.equal(5);
25 |   });
26 | });
27 | 


--------------------------------------------------------------------------------
/test/utils/getPhrasePropTypes_spec.js:
--------------------------------------------------------------------------------
 1 | import { expect } from 'chai';
 2 | 
 3 | import getPhrasePropTypes from '../../src/utils/getPhrasePropTypes';
 4 | 
 5 | const PhraseObject = {
 6 |   foo: 'x',
 7 |   bar: 'y',
 8 |   baz: 'z',
 9 | };
10 | 
11 | describe('#getPhrasePropTypes', () => {
12 |   it('contains each key from the original object', () => {
13 |     const propTypes = getPhrasePropTypes(PhraseObject);
14 |     Object.keys(PhraseObject).forEach((key) => {
15 |       expect(Object.keys(propTypes).filter((type) => type === key).length).to.not.equal(0);
16 |     });
17 |   });
18 | });
19 | 


--------------------------------------------------------------------------------
/test/utils/getPhrase_spec.js:
--------------------------------------------------------------------------------
 1 | import { expect } from 'chai';
 2 | import sinon from 'sinon-sandbox';
 3 | import getPhrase from '../../src/utils/getPhrase';
 4 | 
 5 | describe('getPhrase', () => {
 6 |   it('returns empty string when arg is falsy', () => {
 7 |     expect(getPhrase()).to.equal('');
 8 |   });
 9 | 
10 |   it('returns arg if arg is a string', () => {
11 |     const test = 'foobarbaz';
12 |     expect(getPhrase(test)).to.equal(test);
13 |   });
14 | 
15 |   describe('arg is a function', () => {
16 |     it('returns invoked arg', () => {
17 |       const test = 'this is a new test string';
18 |       const phraseFunc = sinon.stub().returns(test);
19 |       const phrase = getPhrase(phraseFunc);
20 |       expect(phraseFunc.callCount).to.equal(1);
21 |       expect(phrase).to.equal(test);
22 |     });
23 | 
24 |     it('passes second arg into the invoked function', () => {
25 |       const testObj = { hello: 'world' };
26 |       const phraseFunc = sinon.stub();
27 |       getPhrase(phraseFunc, testObj);
28 |       expect(phraseFunc.getCall(0).args[0]).to.equal(testObj);
29 |     });
30 |   });
31 | });
32 | 


--------------------------------------------------------------------------------
/test/utils/getPooledMoment_spec.js:
--------------------------------------------------------------------------------
 1 | import { expect } from 'chai';
 2 | import moment from 'moment';
 3 | 
 4 | import getPooledMoment from '../../src/utils/getPooledMoment';
 5 | 
 6 | describe('getPooledMoment', () => {
 7 |   it('returns a moment given a day string', () => {
 8 |     const momentObj = getPooledMoment('2017-12-10');
 9 |     expect(moment.isMoment(momentObj)).to.equal(true);
10 |     expect(momentObj.format('YYYY MM DD')).to.equal('2017 12 10');
11 |   });
12 | 
13 |   it('returns the same moment given the same day string', () => {
14 |     const momentObj1 = getPooledMoment('2017-12-10');
15 |     const momentObj2 = getPooledMoment('2017-12-10');
16 |     expect(momentObj1).to.equal(momentObj2);
17 |   });
18 | 
19 |   it('returns a different moment given a different day string', () => {
20 |     const momentObj1 = getPooledMoment('2017-12-10');
21 |     const momentObj2 = getPooledMoment('2017-12-11');
22 |     expect(momentObj1).not.to.equal(momentObj2);
23 |   });
24 | });
25 | 


--------------------------------------------------------------------------------
/test/utils/getResponsiveContainerStyles_spec.js:
--------------------------------------------------------------------------------
 1 | import { expect } from 'chai';
 2 | import wrap from 'mocha-wrap';
 3 | 
 4 | import getResponsiveContainerStyles from '../../src/utils/getResponsiveContainerStyles';
 5 | 
 6 | import { ANCHOR_LEFT, ANCHOR_RIGHT } from '../../src/constants';
 7 | 
 8 | const describeUnlessWindow = typeof window === 'undefined' ? describe : describe.skip;
 9 | 
10 | describe('#getResponsiveContainerStyles', () => {
11 |   describeUnlessWindow('window.innerWidth', () => {
12 |     wrap()
13 |     .withGlobal('window', () => ({}))
14 |     .withOverride(() => window, 'innerWidth', () => -42)
15 |     .it('uses window.innerWidth', () => {
16 |       const styles = getResponsiveContainerStyles(ANCHOR_LEFT, 0, 0);
17 |       expect(styles[ANCHOR_LEFT]).to.equal(window.innerWidth);
18 |     });
19 |   });
20 | 
21 |   it('returns a numerical value when margin is not included', () => {
22 |     const styles = getResponsiveContainerStyles(ANCHOR_LEFT, 0, 0);
23 |     expect(styles[ANCHOR_LEFT]).to.be.a('number');
24 |   });
25 | 
26 |   it('returns left style for left anchored container', () => {
27 |     const styles = getResponsiveContainerStyles(ANCHOR_LEFT, 0, 0, 0);
28 |     expect(styles[ANCHOR_LEFT]).to.not.be.an('undefined');
29 |   });
30 | 
31 |   it('returns right style for right anchored container', () => {
32 |     const styles = getResponsiveContainerStyles(ANCHOR_RIGHT, 0, 0, 0);
33 |     expect(styles[ANCHOR_RIGHT]).to.not.be.an('undefined');
34 |   });
35 | });
36 | 


--------------------------------------------------------------------------------
/test/utils/getSelectedDateOffset_spec.js:
--------------------------------------------------------------------------------
 1 | import { expect } from 'chai';
 2 | import moment from 'moment';
 3 | 
 4 | import getSelectedDateOffset from '../../src/utils/getSelectedDateOffset';
 5 | 
 6 | const today = moment();
 7 | 
 8 | describe('#getSelectedDateOffset', () => {
 9 |   it('returns a function modified moment object', () => {
10 |     const fn = (day) => day.add(2, 'days');
11 |     const modifiedDay = getSelectedDateOffset(fn, today);
12 |     expect(modifiedDay.format()).to.equal(today.clone().add(2, 'days').format());
13 |   });
14 | 
15 |   it('returns the passed day when function is undefined', () => {
16 |     const modifiedDay = getSelectedDateOffset(undefined, today);
17 |     expect(modifiedDay.format()).to.equal(today.format());
18 |   });
19 | 
20 |   it('modifies the returned day using the modifier callback', () => {
21 |     const fn = (day) => day.add(2, 'days');
22 |     const modifier = (day) => day.subtract(2, 'days');
23 |     const modifiedDay = getSelectedDateOffset(fn, today, modifier);
24 |     expect(modifiedDay.format()).to.equal(today.clone().format());
25 |   });
26 | 
27 |   it('does not apply the modifier if function is undefined', () => {
28 |     const modifier = (day) => day.subtract(2, 'days');
29 |     const modifiedDay = getSelectedDateOffset(undefined, today, modifier);
30 |     expect(modifiedDay.format()).to.equal(today.format());
31 |   });
32 | });
33 | 


--------------------------------------------------------------------------------
/test/utils/getTransformStyles_spec.js:
--------------------------------------------------------------------------------
 1 | import { expect } from 'chai';
 2 | 
 3 | import getTransformStyles from '../../src/utils/getTransformStyles';
 4 | 
 5 | describe('#getTransformStyles', () => {
 6 |   it('returns non-prefixed transform style', () => {
 7 |     const TRANSFORM_VALUE = 'foo';
 8 |     const transformStyles = getTransformStyles(TRANSFORM_VALUE);
 9 |     expect(transformStyles.transform).to.equal(TRANSFORM_VALUE);
10 |   });
11 | 
12 |   it('returns ms-prefixed transform style', () => {
13 |     const TRANSFORM_VALUE = 'foo';
14 |     const transformStyles = getTransformStyles(TRANSFORM_VALUE);
15 |     expect(transformStyles.msTransform).to.equal(TRANSFORM_VALUE);
16 |   });
17 | 
18 |   it('returns moz-prefixed transform style', () => {
19 |     const TRANSFORM_VALUE = 'foo';
20 |     const transformStyles = getTransformStyles(TRANSFORM_VALUE);
21 |     expect(transformStyles.MozTransform).to.equal(TRANSFORM_VALUE);
22 |   });
23 | 
24 |   it('returns webkit-prefixed transform style', () => {
25 |     const TRANSFORM_VALUE = 'foo';
26 |     const transformStyles = getTransformStyles(TRANSFORM_VALUE);
27 |     expect(transformStyles.WebkitTransform).to.equal(TRANSFORM_VALUE);
28 |   });
29 | });
30 | 


--------------------------------------------------------------------------------
/test/utils/getVisibleDays_spec.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | import { expect } from 'chai';
 3 | 
 4 | import isSameDay from '../../src/utils/isSameDay';
 5 | import getVisibleDays from '../../src/utils/getVisibleDays';
 6 | 
 7 | const today = moment();
 8 | 
 9 | describe('getVisibleDays', () => {
10 |   it('has numberOfMonths entries', () => {
11 |     const numberOfMonths = 3;
12 |     const visibleDays = getVisibleDays(today, numberOfMonths, false);
13 |     expect(Object.keys(visibleDays).length).to.equal(numberOfMonths + 2);
14 |   });
15 | 
16 |   it('values are all arrays of moment objects', () => {
17 |     const visibleDays = getVisibleDays(today, 3, false);
18 |     Object.values(visibleDays).forEach((days) => {
19 |       expect(Array.isArray(days)).to.equal(true);
20 |       days.forEach((day) => {
21 |         expect(moment.isMoment(day)).to.equal(true);
22 |       });
23 |     });
24 |   });
25 | 
26 |   it('contains first arg day', () => {
27 |     const visibleDays = getVisibleDays(today, 3, false);
28 |     const containsToday = Object.values(visibleDays)
29 |       .filter((days) => days.filter((day) => isSameDay(day, today)).length > 0);
30 |     expect(containsToday.length > 0).to.equal(true);
31 |   });
32 | });
33 | 


--------------------------------------------------------------------------------
/test/utils/isAfterDay_spec.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | import { expect } from 'chai';
 3 | 
 4 | import isAfterDay from '../../src/utils/isAfterDay';
 5 | 
 6 | const today = moment();
 7 | const tomorrow = moment().add(1, 'days');
 8 | 
 9 | describe('isAfterDay', () => {
10 |   it('returns true if first arg is after the second but have same month and year', () => {
11 |     expect(isAfterDay(tomorrow, today)).to.equal(true);
12 |   });
13 | 
14 |   it('returns true if first arg is after the second but have same day and year', () => {
15 |     expect(isAfterDay(moment().clone().add(1, 'month'), today)).to.equal(true);
16 |   });
17 | 
18 |   it('returns true if first arg is after the second but have same day and month', () => {
19 |     expect(isAfterDay(moment().clone().add(1, 'year'), today)).to.equal(true);
20 |   });
21 | 
22 |   it('returns false if args are the same day', () => {
23 |     expect(isAfterDay(today, today)).to.equal(false);
24 |   });
25 | 
26 |   it('returns false if first arg is after the second', () => {
27 |     expect(isAfterDay(today, tomorrow)).to.equal(false);
28 |   });
29 | 
30 |   describe('non-moment object arguments', () => {
31 |     it('is false if first argument is not a moment object', () => {
32 |       expect(isAfterDay(null, today)).to.equal(false);
33 |     });
34 | 
35 |     it('is false if second argument is not a moment object', () => {
36 |       expect(isAfterDay(today, 'foo')).to.equal(false);
37 |     });
38 |   });
39 | });
40 | 


--------------------------------------------------------------------------------
/test/utils/isBeforeDay_spec.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | import { expect } from 'chai';
 3 | 
 4 | import isBeforeDay from '../../src/utils/isBeforeDay';
 5 | 
 6 | const today = moment();
 7 | const tomorrow = moment().add(1, 'days');
 8 | 
 9 | describe('isBeforeDay', () => {
10 |   it('returns true if first arg is before the second but have same month and year', () => {
11 |     expect(isBeforeDay(today, tomorrow)).to.equal(true);
12 |   });
13 | 
14 |   it('returns true if first arg is before the second but have same day and year', () => {
15 |     expect(isBeforeDay(today, moment().clone().add(1, 'month'))).to.equal(true);
16 |   });
17 | 
18 |   it('returns true if first arg is before the second but have same day and month', () => {
19 |     expect(isBeforeDay(today, moment().clone().add(1, 'year'))).to.equal(true);
20 |   });
21 | 
22 |   it('returns false if args are the same day', () => {
23 |     expect(isBeforeDay(today, today)).to.equal(false);
24 |   });
25 | 
26 |   it('returns false if first arg is after the second', () => {
27 |     expect(isBeforeDay(tomorrow, today)).to.equal(false);
28 |   });
29 | 
30 |   describe('non-moment object arguments', () => {
31 |     it('is false if first argument is not a moment object', () => {
32 |       expect(isBeforeDay(null, today)).to.equal(false);
33 |     });
34 | 
35 |     it('is false if second argument is not a moment object', () => {
36 |       expect(isBeforeDay(today, 'foo')).to.equal(false);
37 |     });
38 |   });
39 | });
40 | 


--------------------------------------------------------------------------------
/test/utils/isDayVisible_spec.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | import { expect } from 'chai';
 3 | 
 4 | import isDayVisible from '../../src/utils/isDayVisible';
 5 | 
 6 | describe('#isDayVisible', () => {
 7 |   it('returns true if arg is in visible months', () => {
 8 |     const test = moment().add(3, 'months');
 9 |     const currentMonth = moment().add(2, 'months');
10 |     expect(isDayVisible(test, currentMonth, 2)).to.equal(true);
11 |   });
12 | 
13 |   it('returns false if arg is before first month', () => {
14 |     const test = moment().add(1, 'months');
15 |     const currentMonth = moment().add(2, 'months');
16 |     expect(isDayVisible(test, currentMonth, 2)).to.equal(false);
17 |   });
18 | 
19 |   it('returns false if arg is after last month', () => {
20 |     const test = moment().add(4, 'months');
21 |     const currentMonth = moment().add(2, 'months');
22 |     expect(isDayVisible(test, currentMonth, 2)).to.equal(false);
23 |   });
24 | 
25 |   describe('enableOutsideDays', () => {
26 |     it('returns true if arg is in partial week before visible months', () => {
27 |       const test = moment('2019-04-30');
28 |       const currentMonth = moment('2019-05-01');
29 |       expect(isDayVisible(test, currentMonth, 1, false)).to.equal(false);
30 |       expect(isDayVisible(test, currentMonth, 1, true)).to.equal(true);
31 |     });
32 | 
33 |     it('returns true if arg is in partial week after visible months', () => {
34 |       const test = moment('2019-06-01');
35 |       const currentMonth = moment('2019-05-01');
36 |       expect(isDayVisible(test, currentMonth, 1, false)).to.equal(false);
37 |       expect(isDayVisible(test, currentMonth, 1, true)).to.equal(true);
38 |     });
39 | 
40 |     it('returns false if arg is before partial week before visible months', () => {
41 |       const test = moment('2019-04-27');
42 |       const currentMonth = moment('2019-05-01');
43 |       expect(isDayVisible(test, currentMonth, 1, true)).to.equal(false);
44 |     });
45 | 
46 |     it('returns false if arg is after partial week after visible months', () => {
47 |       const test = moment('2019-06-03');
48 |       const currentMonth = moment('2019-05-01');
49 |       expect(isDayVisible(test, currentMonth, 1, true)).to.equal(false);
50 |     });
51 |   });
52 | 
53 |   // this test fails when run with the whole suite,
54 |   // potentially due to cache pollution from other tests
55 |   it.skip('works when the first day of the week that starts the month does not have a midnight', () => {
56 |     const march29 = moment('2020-03-29').utcOffset(-1 /* 'Atlantic/Azores' */);
57 |     const april2020 = moment('2020-04-02').utcOffset(-1 /* 'Atlantic/Azores' */);
58 |     expect(isDayVisible(march29, april2020, 1, true)).to.equal(true);
59 |   });
60 | });
61 | 


--------------------------------------------------------------------------------
/test/utils/isInclusivelyAfterDay_spec.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | import { expect } from 'chai';
 3 | 
 4 | import isInclusivelyAfterDay from '../../src/utils/isInclusivelyAfterDay';
 5 | 
 6 | const today = moment();
 7 | const tomorrow = moment().add(1, 'days');
 8 | 
 9 | describe('isInclusivelyAfterDay', () => {
10 |   it('returns true if first argument is after the second', () => {
11 |     expect(isInclusivelyAfterDay(tomorrow, today)).to.equal(true);
12 |   });
13 | 
14 |   it('returns true for same day arguments', () => {
15 |     expect(isInclusivelyAfterDay(today, today)).to.equal(true);
16 |   });
17 | 
18 |   it('returns false if first argument is before the second', () => {
19 |     expect(isInclusivelyAfterDay(today, tomorrow)).to.equal(false);
20 |   });
21 | 
22 |   describe('non-moment object arguments', () => {
23 |     it('is false if first argument is not a moment object', () => {
24 |       expect(isInclusivelyAfterDay(null, today)).to.equal(false);
25 |     });
26 | 
27 |     it('is false if second argument is not a moment object', () => {
28 |       expect(isInclusivelyAfterDay(today, 'foo')).to.equal(false);
29 |     });
30 |   });
31 | });
32 | 


--------------------------------------------------------------------------------
/test/utils/isInclusivelyBeforeDay_spec.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | import { expect } from 'chai';
 3 | 
 4 | import isInclusivelyBeforeDay from '../../src/utils/isInclusivelyBeforeDay';
 5 | 
 6 | const today = moment();
 7 | const tomorrow = moment().add(1, 'days');
 8 | 
 9 | describe('isInclusivelyBeforeDay', () => {
10 |   it('returns true if first argument is before the second', () => {
11 |     expect(isInclusivelyBeforeDay(today, tomorrow)).to.equal(true);
12 |   });
13 | 
14 |   it('returns true for same day arguments', () => {
15 |     expect(isInclusivelyBeforeDay(today, today)).to.equal(true);
16 |   });
17 | 
18 |   it('returns false if first argument is after the second', () => {
19 |     expect(isInclusivelyBeforeDay(tomorrow, today)).to.equal(false);
20 |   });
21 | 
22 |   describe('non-moment object arguments', () => {
23 |     it('is false if first argument is not a moment object', () => {
24 |       expect(isInclusivelyBeforeDay(null, today)).to.equal(false);
25 |     });
26 | 
27 |     it('is false if second argument is not a moment object', () => {
28 |       expect(isInclusivelyBeforeDay(today, 'foo')).to.equal(false);
29 |     });
30 |   });
31 | });
32 | 


--------------------------------------------------------------------------------
/test/utils/isNextDay_spec.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | import { expect } from 'chai';
 3 | 
 4 | import isNextDay from '../../src/utils/isNextDay';
 5 | 
 6 | const today = moment();
 7 | const tomorrow = moment().add(1, 'days');
 8 | 
 9 | describe('isNextDay', () => {
10 |   it('returns true if second argument is the next day after the first', () => {
11 |     expect(isNextDay(today, tomorrow)).to.equal(true);
12 |   });
13 | 
14 |   it('returns false if the second arg is not the next day after the first', () => {
15 |     expect(isNextDay(tomorrow, today)).to.equal(false);
16 |   });
17 | 
18 |   describe('non-moment arguments', () => {
19 |     it('is false if first argument is not a moment object', () => {
20 |       expect(isNextDay(null, today)).to.equal(false);
21 |     });
22 | 
23 |     it('is false if second argument is not a moment object', () => {
24 |       expect(isNextDay(today, 'foo')).to.equal(false);
25 |     });
26 |   });
27 | });
28 | 


--------------------------------------------------------------------------------
/test/utils/isNextMonth_spec.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | import { expect } from 'chai';
 3 | 
 4 | import isNextMonth from '../../src/utils/isNextMonth';
 5 | 
 6 | const today = moment();
 7 | const nextMonth = moment().add(1, 'months');
 8 | const twoMonths = moment().add(2, 'months');
 9 | 
10 | describe('isNextMonth', () => {
11 |   it('returns true if second argument is the next month after the first', () => {
12 |     expect(isNextMonth(today, nextMonth)).to.equal(true);
13 |   });
14 | 
15 |   it('returns false if second argument is not the next month after the first', () => {
16 |     expect(isNextMonth(nextMonth, today)).to.equal(false);
17 |   });
18 | 
19 |   it('returns false if second argument is more than one month after the first', () => {
20 |     expect(isNextMonth(today, twoMonths)).to.equal(false);
21 |   });
22 | 
23 |   describe('non-moment arguments', () => {
24 |     it('is false if first argument is not a moment object', () => {
25 |       expect(isNextMonth(null, today)).to.equal(false);
26 |     });
27 | 
28 |     it('is false if second argument is not a moment object', () => {
29 |       expect(isNextMonth(today, 'foo')).to.equal(false);
30 |     });
31 |   });
32 | });
33 | 


--------------------------------------------------------------------------------
/test/utils/isPrevMonth_spec.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | import { expect } from 'chai';
 3 | 
 4 | import isPrevMonth from '../../src/utils/isPrevMonth';
 5 | 
 6 | const today = moment();
 7 | const lastMonth = moment().subtract(1, 'months');
 8 | const twoMonthsAgo = moment().subtract(2, 'months');
 9 | 
10 | describe('isPrevMonth', () => {
11 |   it('returns true if second argument is the month before the first', () => {
12 |     expect(isPrevMonth(today, lastMonth)).to.equal(true);
13 |   });
14 | 
15 |   it('returns false if second argument is not the month before the first', () => {
16 |     expect(isPrevMonth(lastMonth, today)).to.equal(false);
17 |   });
18 | 
19 |   it('returns false if second argument is more than one month before the first', () => {
20 |     expect(isPrevMonth(today, twoMonthsAgo)).to.equal(false);
21 |   });
22 | 
23 |   describe('non-moment arguments', () => {
24 |     it('is false if first argument is not a moment object', () => {
25 |       expect(isPrevMonth(null, today)).to.equal(false);
26 |     });
27 | 
28 |     it('is false if second argument is not a moment object', () => {
29 |       expect(isPrevMonth(today, 'foo')).to.equal(false);
30 |     });
31 |   });
32 | });
33 | 


--------------------------------------------------------------------------------
/test/utils/isPreviousDay_spec.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | import { expect } from 'chai';
 3 | 
 4 | import isPreviousDay from '../../src/utils/isPreviousDay';
 5 | 
 6 | const today = moment();
 7 | const yesterday = moment().subtract(1, 'days');
 8 | 
 9 | describe('isPreviousDay', () => {
10 |   it('returns true if second argument is the day immediately before the first', () => {
11 |     expect(isPreviousDay(today, yesterday)).to.equal(true);
12 |   });
13 | 
14 |   it('returns false if the second arg is not the day immediately before the first', () => {
15 |     expect(isPreviousDay(yesterday, today)).to.equal(false);
16 |   });
17 | 
18 |   describe('non-moment arguments', () => {
19 |     it('is false if first argument is not a moment object', () => {
20 |       expect(isPreviousDay(null, today)).to.equal(false);
21 |     });
22 | 
23 |     it('is false if second argument is not a moment object', () => {
24 |       expect(isPreviousDay(today, 'foo')).to.equal(false);
25 |     });
26 |   });
27 | });
28 | 


--------------------------------------------------------------------------------
/test/utils/isSameDay_spec.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | import { expect } from 'chai';
 3 | 
 4 | import isSameDay from '../../src/utils/isSameDay';
 5 | 
 6 | const today = moment();
 7 | const tomorrow = moment().add(1, 'days');
 8 | 
 9 | describe('isSameDay', () => {
10 |   it('returns true if args are the same day', () => {
11 |     expect(isSameDay(today, today)).to.equal(true);
12 |   });
13 | 
14 |   it('returns false if args are not the same day', () => {
15 |     expect(isSameDay(today, tomorrow)).to.equal(false);
16 |   });
17 | 
18 |   it('returns false for same days of week', () => {
19 |     // Flags accidentally use of moment's day() function, which returns index
20 |     // within the week.
21 |     expect(isSameDay(
22 |       moment('2000-01-01'),
23 |       moment('2000-01-08'),
24 |     )).to.equal(false);
25 |   });
26 | 
27 |   describe('non-moment object arguments', () => {
28 |     it('is false if first argument is not a moment object', () => {
29 |       expect(isSameDay(null, today)).to.equal(false);
30 |     });
31 | 
32 |     it('is false if second argument is not a moment object', () => {
33 |       expect(isSameDay(today, 'foo')).to.equal(false);
34 |     });
35 |   });
36 | });
37 | 


--------------------------------------------------------------------------------
/test/utils/isSameMonth_spec.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | import { expect } from 'chai';
 3 | 
 4 | import isSameMonth from '../../src/utils/isSameMonth';
 5 | 
 6 | const today = moment();
 7 | const nextMonth = moment().add(1, 'month');
 8 | 
 9 | describe('isSameMonth', () => {
10 |   it('returns true if args are the same month', () => {
11 |     expect(isSameMonth(today, today)).to.equal(true);
12 |   });
13 | 
14 |   it('returns false if args are not the same month', () => {
15 |     expect(isSameMonth(today, nextMonth)).to.equal(false);
16 |   });
17 | 
18 |   describe('non-moment object arguments', () => {
19 |     it('is false if first argument is not a moment object', () => {
20 |       expect(isSameMonth(null, today)).to.equal(false);
21 |     });
22 | 
23 |     it('is false if second argument is not a moment object', () => {
24 |       expect(isSameMonth(today, 'foo')).to.equal(false);
25 |     });
26 |   });
27 | });
28 | 


--------------------------------------------------------------------------------
/test/utils/isTransitionEndSupported_spec.js:
--------------------------------------------------------------------------------
 1 | import { expect } from 'chai';
 2 | import wrap from 'mocha-wrap';
 3 | 
 4 | import isTransitionEndSupported from '../../src/utils/isTransitionEndSupported';
 5 | 
 6 | const describeIfNoWindow = typeof window === 'undefined' ? describe : describe.skip;
 7 | 
 8 | describeIfNoWindow('isTransitionEndSupported', () => {
 9 |   describe('without `window`', () => {
10 |     it('returns false', () => {
11 |       expect(typeof window).to.equal('undefined');
12 |       expect(isTransitionEndSupported()).to.equal(false);
13 |     });
14 |   });
15 | 
16 |   wrap()
17 |   .withGlobal('window', () => ({}))
18 |   .describe('with `window`', () => {
19 |     it('returns false without `window.TransitionEvent`', () => {
20 |       expect(isTransitionEndSupported()).to.equal(false);
21 |     });
22 | 
23 |     wrap()
24 |     .withOverride(() => window, 'TransitionEvent', () => () => {})
25 |     .it('returns true with `window.ontouchstart', () => {
26 |       expect(isTransitionEndSupported()).to.equal(true);
27 |     });
28 |   });
29 | });
30 | 


--------------------------------------------------------------------------------
/test/utils/noflip_spec.js:
--------------------------------------------------------------------------------
 1 | import { expect } from 'chai';
 2 | 
 3 | import noflip from '../../src/utils/noflip';
 4 | 
 5 | describe('noflip', () => {
 6 |   it('appends a noflip comment to a number', () => {
 7 |     expect(noflip(42)).to.equal('42px /* @noflip */');
 8 |   });
 9 | 
10 |   it('appends a noflip comment to a string', () => {
11 |     expect(noflip('foo')).to.equal('foo /* @noflip */');
12 |   });
13 | 
14 |   it('throws when value is unexpected type', () => {
15 |     expect(() => {
16 |       noflip([]);
17 |     }).to.throw(TypeError);
18 |   });
19 | });
20 | 


--------------------------------------------------------------------------------
/test/utils/toISODateString_spec.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | import { expect } from 'chai';
 3 | 
 4 | import toISODateString from '../../src/utils/toISODateString';
 5 | import { ISO_FORMAT } from '../../src/constants';
 6 | 
 7 | describe('toISODateString', () => {
 8 |   it('returns null for falsy argument', () => {
 9 |     expect(toISODateString()).to.equal(null);
10 |   });
11 | 
12 |   it('converts moment object to localized date string', () => {
13 |     const testDate = moment('1991-07-13');
14 |     const dateString = toISODateString(testDate);
15 |     expect(dateString).to.equal('1991-07-13');
16 |   });
17 | 
18 |   it('matches moment format behavior', () => {
19 |     const testDate = moment('1991-07-13');
20 |     const dateString = toISODateString(testDate);
21 |     expect(dateString).to.equal(testDate.format(ISO_FORMAT));
22 |   });
23 | 
24 |   it('converts iso date string to ISO date string', () => {
25 |     const testDate = moment('1991-07-13');
26 |     const dateString = toISODateString(testDate.format(ISO_FORMAT));
27 |     expect(dateString).to.equal('1991-07-13');
28 |   });
29 | 
30 |   it('convers localized date strings to ISO date string', () => {
31 |     const testDate = moment('1991-07-13');
32 |     const dateString = toISODateString(testDate.format('L'));
33 |     expect(dateString).to.equal('1991-07-13');
34 |   });
35 | 
36 |   it('converts custom format date strings with format passed in', () => {
37 |     const testDate = moment('1991-07-13');
38 |     const dateString = toISODateString(testDate.format('YYYY---DD/MM'), 'YYYY---DD/MM');
39 |     expect(dateString).to.equal('1991-07-13');
40 |   });
41 | });
42 | 


--------------------------------------------------------------------------------
/test/utils/toISOMonthString_spec.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | import { expect } from 'chai';
 3 | 
 4 | import { ISO_FORMAT, ISO_MONTH_FORMAT } from '../../src/constants';
 5 | import toISOMonthString from '../../src/utils/toISOMonthString';
 6 | 
 7 | describe('#toISOMonthString', () => {
 8 |   describe('arg is a moment object', () => {
 9 |     it('returns month in ISO_MONTH_FORMAT format', () => {
10 |       const today = moment();
11 |       expect(toISOMonthString(today)).to.equal(today.format(ISO_MONTH_FORMAT));
12 |     });
13 |   });
14 | 
15 |   describe('arg is a string', () => {
16 |     describe('arg is in ISO format', () => {
17 |       it('returns month in ISO_MONTH_FORMAT format', () => {
18 |         const today = moment();
19 |         const todayISO = today.format(ISO_FORMAT);
20 |         expect(toISOMonthString(todayISO)).to.equal(today.format(ISO_MONTH_FORMAT));
21 |       });
22 |     });
23 | 
24 |     describe('arg matches the second arg date format provided', () => {
25 |       it('returns month in ISO_MONTH_FORMAT format', () => {
26 |         const today = moment();
27 |         const dateFormat = 'MM_DD_YYYY';
28 |         const formattedDate = today.format(dateFormat);
29 |         const monthString = toISOMonthString(formattedDate, dateFormat);
30 |         expect(monthString).to.equal(today.format(ISO_MONTH_FORMAT));
31 |       });
32 |     });
33 | 
34 |     describe('arg is neither in iso format or in the provided format', () => {
35 |       it('returns null', () => {
36 |         const today = moment();
37 |         const dateFormat = 'MM_DD_YYYY';
38 |         const formattedDate = today.format('MM-DD-YYYY');
39 |         expect(toISOMonthString(formattedDate, dateFormat)).to.equal(null);
40 |       });
41 |     });
42 | 
43 |     describe('arg is not a valid date string', () => {
44 |       it('returns null', () => {
45 |         expect(toISOMonthString('This is not a date')).to.equal(null);
46 |       });
47 |     });
48 |   });
49 | });
50 | 


--------------------------------------------------------------------------------
/test/utils/toLocalizedDateString_spec.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | import { expect } from 'chai';
 3 | 
 4 | import toLocalizedDateString from '../../src/utils/toLocalizedDateString';
 5 | import { ISO_FORMAT } from '../../src/constants';
 6 | 
 7 | describe('toLocalizedDateString', () => {
 8 |   it('returns null for falsy argument', () => {
 9 |     expect(toLocalizedDateString()).to.equal(null);
10 |   });
11 | 
12 |   it('converts moment object to localized date string', () => {
13 |     const testDate = moment('1991-07-13');
14 |     const dateString = toLocalizedDateString(testDate);
15 |     expect(dateString).to.equal(testDate.format('L'));
16 |   });
17 | 
18 |   it('converts iso date string to localized date string', () => {
19 |     const testDate = moment('1991-07-13');
20 |     const dateString = toLocalizedDateString(testDate.format(ISO_FORMAT));
21 |     expect(dateString).to.equal(testDate.format('L'));
22 |   });
23 | 
24 |   it('localized date strings stay the same', () => {
25 |     const testDate = moment('1991-07-13');
26 |     const dateString = toLocalizedDateString(testDate.format('L'));
27 |     expect(dateString).to.equal(testDate.format('L'));
28 |   });
29 | 
30 |   it('converts custom format date strings with format passed in', () => {
31 |     const testDate = moment('1991-07-13');
32 |     const dateString = toLocalizedDateString(testDate.format('YYYY---DD/MM'), 'YYYY---DD/MM');
33 |     expect(dateString).to.equal(testDate.format('L'));
34 |   });
35 | });
36 | 


--------------------------------------------------------------------------------
/test/utils/toMomentObject_spec.js:
--------------------------------------------------------------------------------
 1 | import moment from 'moment';
 2 | import { expect } from 'chai';
 3 | 
 4 | import isSameDay from '../../src/utils/isSameDay';
 5 | import toMomentObject from '../../src/utils/toMomentObject';
 6 | 
 7 | describe('toMomentObject', () => {
 8 |   it('returns null for null input', () => {
 9 |     expect(toMomentObject(null)).to.equal(null);
10 |   });
11 | 
12 |   it('returns null for undefined input', () => {
13 |     expect(toMomentObject(undefined)).to.equal(null);
14 |   });
15 | 
16 |   it('returns null for empty string', () => {
17 |     expect(toMomentObject('')).to.equal(null);
18 |   });
19 | 
20 |   it('returns null for no input', () => {
21 |     expect(toMomentObject()).to.equal(null);
22 |   });
23 | 
24 |   it('output has time of 12PM', () => {
25 |     expect(toMomentObject('1991-07-13').hour()).to.equal(12);
26 |   });
27 | 
28 |   it('parses custom format', () => {
29 |     const date = toMomentObject('1991---13/07', 'YYYY---DD/MM');
30 | 
31 |     expect(date).not.to.equal(null);
32 |     expect(date.month()).to.equal(6); // moment months are zero-indexed
33 |     expect(date.date()).to.equal(13);
34 |     expect(date.year()).to.equal(1991);
35 |   });
36 | 
37 |   it('parses localized format', () => {
38 |     const date = toMomentObject(moment('1991-07-13').format('L'));
39 | 
40 |     expect(date).not.to.equal(null);
41 |     expect(date.month()).to.equal(6); // moment months are zero-indexed
42 |     expect(date.date()).to.equal(13);
43 |     expect(date.year()).to.equal(1991);
44 |   });
45 | 
46 |   describe('Daylight Savings Time issues', () => {
47 |     it('last of February does not equal first of March', () => {
48 |       expect(isSameDay(toMomentObject('2017-02-28'), toMomentObject('2017-03-01'))).to.equal(false);
49 |     });
50 | 
51 |     it('last of March does not equal first of April', () => {
52 |       expect(isSameDay(toMomentObject('2017-03-31'), toMomentObject('2017-04-01'))).to.equal(false);
53 |     });
54 |   });
55 | });
56 | 


--------------------------------------------------------------------------------