From 55d6453642db3cc3d4a90a9dc32ee244bebbec15 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Mon, 13 Nov 2017 22:09:49 -0800 Subject: [PATCH 001/618] Fix reliance of stories on built constants --- stories/DateRangePicker.js | 2 +- stories/DateRangePicker_calendar.js | 2 +- stories/DayPicker.js | 2 +- stories/DayPickerRangeController.js | 2 +- stories/DayPickerSingleDateController.js | 2 +- stories/SingleDatePicker.js | 2 +- stories/SingleDatePicker_calendar.js | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/stories/DateRangePicker.js b/stories/DateRangePicker.js index 949670bea3..e7df729fd2 100644 --- a/stories/DateRangePicker.js +++ b/stories/DateRangePicker.js @@ -4,7 +4,7 @@ import momentJalaali from 'moment-jalaali'; import { storiesOf } from '@storybook/react'; import { VERTICAL_ORIENTATION, -} from '../constants'; +} from '../src/constants'; import DateRangePickerWrapper from '../examples/DateRangePickerWrapper'; diff --git a/stories/DateRangePicker_calendar.js b/stories/DateRangePicker_calendar.js index e67c31a22b..841d033756 100644 --- a/stories/DateRangePicker_calendar.js +++ b/stories/DateRangePicker_calendar.js @@ -2,7 +2,7 @@ import React from 'react'; import moment from 'moment'; import { storiesOf } from '@storybook/react'; -import { VERTICAL_ORIENTATION, ANCHOR_RIGHT, OPEN_UP } from '../constants'; +import { VERTICAL_ORIENTATION, ANCHOR_RIGHT, OPEN_UP } from '../src/constants'; import DateRangePickerWrapper from '../examples/DateRangePickerWrapper'; diff --git a/stories/DayPicker.js b/stories/DayPicker.js index eeba97cc26..cea8b8ae6d 100644 --- a/stories/DayPicker.js +++ b/stories/DayPicker.js @@ -5,7 +5,7 @@ import DayPicker from '../src/components/DayPicker'; import { VERTICAL_ORIENTATION, VERTICAL_SCROLLABLE, -} from '../constants'; +} from '../src/constants'; const TestPrevIcon = () => ( ( Date: Tue, 14 Nov 2017 15:13:35 +0900 Subject: [PATCH 002/618] Add dayAriaLabelFormat Prop into CalendarDay component --- src/components/CalendarDay.jsx | 5 ++++- src/components/CalendarMonth.jsx | 3 +++ src/components/CalendarMonthGrid.jsx | 3 +++ src/components/DayPicker.jsx | 3 +++ test/components/CalendarDay_spec.jsx | 12 ++++++++++++ 5 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index ef06976f3b..6928aeaa17 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -24,6 +24,7 @@ const propTypes = forbidExtraProps({ onDayMouseEnter: PropTypes.func, onDayMouseLeave: PropTypes.func, renderDay: PropTypes.func, + ariaLabelFormat: PropTypes.string, // internationalization phrases: PropTypes.shape(getPhrasePropTypes(CalendarDayPhrases)), @@ -40,6 +41,7 @@ const defaultProps = { onDayMouseEnter() {}, onDayMouseLeave() {}, renderDay: null, + ariaLabelFormat: 'dddd, LL', // internationalization phrases: CalendarDayPhrases, @@ -87,6 +89,7 @@ class CalendarDay extends React.Component { render() { const { day, + ariaLabelFormat, daySize, isOutsideDay, modifiers, @@ -101,7 +104,7 @@ class CalendarDay extends React.Component { if (!day) return ; - const formattedDate = { date: `${day.format('dddd')}, ${day.format('LL')}` }; + const formattedDate = { date: day.format(ariaLabelFormat) }; const ariaLabel = modifiers.has(BLOCKED_MODIFIER) ? getPhrase(dateIsUnavailable, formattedDate) diff --git a/src/components/CalendarMonth.jsx b/src/components/CalendarMonth.jsx index c2a1093f8d..0ec3ab6e7c 100644 --- a/src/components/CalendarMonth.jsx +++ b/src/components/CalendarMonth.jsx @@ -50,6 +50,7 @@ const propTypes = forbidExtraProps({ // i18n monthFormat: PropTypes.string, phrases: PropTypes.shape(getPhrasePropTypes(CalendarDayPhrases)), + dayAriaLabelFormat: PropTypes.string, }); const defaultProps = { @@ -154,6 +155,7 @@ class CalendarMonth extends React.Component { isFocused, styles, phrases, + dayAriaLabelFormat, } = this.props; const { weeks } = this.state; @@ -202,6 +204,7 @@ class CalendarMonth extends React.Component { renderDay={renderDay} phrases={phrases} modifiers={modifiers[toISODateString(day)]} + ariaLabelFormat={dayAriaLabelFormat} /> ))} diff --git a/src/components/CalendarMonthGrid.jsx b/src/components/CalendarMonthGrid.jsx index dfd43c1e7f..7a6b706a33 100644 --- a/src/components/CalendarMonthGrid.jsx +++ b/src/components/CalendarMonthGrid.jsx @@ -54,6 +54,7 @@ const propTypes = forbidExtraProps({ // i18n monthFormat: PropTypes.string, phrases: PropTypes.shape(getPhrasePropTypes(CalendarDayPhrases)), + dayAriaLabelFormat: PropTypes.string, }); const defaultProps = { @@ -230,6 +231,7 @@ class CalendarMonthGrid extends React.Component { isRTL, styles, phrases, + dayAriaLabelFormat, } = this.props; const { months } = this.state; @@ -306,6 +308,7 @@ class CalendarMonthGrid extends React.Component { isFocused={isFocused} phrases={phrases} setMonthHeight={(height) => { this.setMonthHeight(height, i); }} + dayAriaLabelFormat={dayAriaLabelFormat} /> ); diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index a3fb8ee42b..07cc79efcb 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -84,6 +84,7 @@ const propTypes = forbidExtraProps({ monthFormat: PropTypes.string, weekDayFormat: PropTypes.string, phrases: PropTypes.shape(getPhrasePropTypes(DayPickerPhrases)), + dayAriaLabelFormat: PropTypes.string, }); export const defaultProps = { @@ -686,6 +687,7 @@ class DayPicker extends React.Component { styles, phrases, verticalHeight, + dayAriaLabelFormat, } = this.props; const numOfWeekHeaders = this.isVertical() ? 1 : numberOfMonths; @@ -805,6 +807,7 @@ class DayPicker extends React.Component { focusedDate={focusedDate} phrases={phrases} isRTL={isRTL} + dayAriaLabelFormat={dayAriaLabelFormat} /> {verticalScrollable && this.renderNavigation()} diff --git a/test/components/CalendarDay_spec.jsx b/test/components/CalendarDay_spec.jsx index 9493199c9e..d9171c2198 100644 --- a/test/components/CalendarDay_spec.jsx +++ b/test/components/CalendarDay_spec.jsx @@ -87,6 +87,18 @@ describe('CalendarDay', () => { expect(phrases.dateIsUnavailable.calledWith(expectedFormattedDay)).to.equal(true); expect(wrapper.find('button').prop('aria-label')).to.equal('dateIsUnavailable text'); }); + + it('should set aria-label with a value pass through ariaLabelFormat prop if it exists', () => { + const modifiers = new Set(); + + const wrapper = shallow().dive(); + + expect(wrapper.find('button').prop('aria-label')).to.equal('October 10th 2017'); + }); }); }); }); From bd3d7f927069cb5db7d1a3f2d9ca7a36a86dc883 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Wed, 15 Nov 2017 01:17:36 -0800 Subject: [PATCH 003/618] Use export of enzyme-adapter-react-helper to simplify test setup --- package.json | 2 +- test/_helpers/enzymeSetup.js | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index d9810a2907..554c3a5cad 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "coveralls": "^2.13.3", "cross-env": "^5.1.0", "enzyme": "^3.1.0", - "enzyme-adapter-react-15": "^1.0.2", + "enzyme-adapter-react-helper": "^1.0.3", "eslint": "^4.9.0", "eslint-config-airbnb": "^16.1.0", "eslint-plugin-import": "^2.8.0", diff --git a/test/_helpers/enzymeSetup.js b/test/_helpers/enzymeSetup.js index dad5556e93..5491fa359c 100644 --- a/test/_helpers/enzymeSetup.js +++ b/test/_helpers/enzymeSetup.js @@ -1,9 +1,3 @@ -const configure = require('enzyme').configure; -let Adapter; -try { - Adapter = require('enzyme-adapter-react-15'); -} catch (e) { - Adapter = require('enzyme-adapter-react-14'); -} +import configure from 'enzyme-adapter-react-helper'; -configure({ adapter: new Adapter(), disableLifecycleMethods: true }); +configure({ disableLifecycleMethods: true }); From 38513f554a175601b8a497a81e36692d3f51de9d Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Wed, 15 Nov 2017 01:37:14 -0800 Subject: [PATCH 004/618] =?UTF-8?q?Use=20enzyme-adapter-react-helper?= =?UTF-8?q?=E2=80=99s=20binary=20to=20clean=20up=20testing.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .travis.yml | 2 +- install-relevant-react.sh | 13 ------------- package.json | 4 +--- 3 files changed, 2 insertions(+), 17 deletions(-) delete mode 100644 install-relevant-react.sh diff --git a/.travis.yml b/.travis.yml index d0c8bbd383..e774d95326 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ before_script: - 'if [ -n "${KARMA-}" ]; then export DISPLAY=:99.0; fi' - 'if [ -n "${KARMA-}" ]; then sh -e /etc/init.d/xvfb start; fi' - 'if [ -n "${KARMA-}" ]; then sleep 3; fi' - - 'if [ -n "${REACT-}" ] && [ "${TEST-}" = true ]; then sh install-relevant-react.sh && npm ls >/dev/null || echo "temporary bypass"; fi' + - 'if [ -n "${REACT-}" ] && [ "${TEST-}" = true ]; then npm run react && (npm ls >/dev/null || echo "temporary bypass"); fi' script: - 'if [ -n "${LINT-}" ]; then npm run lint ; fi' - 'if [ "${TEST-}" = true ]; then npm run tests-only ; fi' diff --git a/install-relevant-react.sh b/install-relevant-react.sh deleted file mode 100644 index f73da9e55f..0000000000 --- a/install-relevant-react.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -REACT=${REACT:-15} - -echo "installing React $REACT" - -if [ "$REACT" = "0.14" ]; then - npm run react:14 -fi - -if [ "$REACT" = "15" ]; then - npm run react:15 -fi diff --git a/package.json b/package.json index 554c3a5cad..0e2c90548c 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,7 @@ "lint": "eslint --ext .js,.jsx src test", "mocha": "mocha ./test/_helpers", "storybook:uninstall": "npm uninstall --no-save @storybook/react && rimraf node_modules/@storybook node_modules/react-modal node_modules/react-dom-factories", - "react:clean": "npm run storybook:uninstall && npm i --no-save ajv ajv-keywords && npm uninstall --no-save react react-dom react-addons-test-utils react-test-renderer && rimraf node_modules/react-test-renderer node_modules/react && npm prune", - "react:14": "rimraf node_modules/.bin/npm && npm run react:clean && npm i --no-save react@0.14 react-dom@0.14 react-addons-test-utils@0.14 enzyme-adapter-react-14 && npm run storybook:uninstall && npm prune && npm i --no-save enzyme-adapter-react-14", - "react:15": "rimraf node_modules/.bin/npm && npm run react:clean && npm i --no-save react@15 react-dom@15 react-addons-test-utils@15 react-test-renderer@15 && npm run storybook:uninstall && npm prune", + "react": "enzyme-adapter-react-install 15", "pretest": "npm run --silent lint", "tests-only": "npm run mocha --silent test", "tests-karma": "karma start", From cfce43e375fd8afb744e5cd5a0f0d89e21cff125 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 17 Nov 2017 10:42:39 -0600 Subject: [PATCH 005/618] [Tests] test on React 16 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index e774d95326..a5e14e7880 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,7 @@ env: matrix: - REACT=0.14 - REACT=15 + - REACT=16 sudo: false matrix: fast_finish: true From 5e8eb683cf819eba8ecfb6e31273116de154d0b7 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 17 Nov 2017 11:35:31 -0600 Subject: [PATCH 006/618] [Tests] fix coverage --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a5e14e7880..06d23f20f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ script: - 'if [ -n "${LINT-}" ]; then npm run lint ; fi' - 'if [ "${TEST-}" = true ]; then npm run tests-only ; fi' - 'if [ -n "${KARMA-}" ]; then npm run tests-karma ; fi' - - 'if [ -n "${COVERAGE-}" ] && [ "${TRAVIS_BRANCH-}" = "master" ]; then npm run cover; cat ./coverage/lcov.info | ./node_modules/.bin/coveralls ; fi' + - 'if [ -n "${COVERAGE-}" ] && [ "${TRAVIS_BRANCH-}" = "master" ]; then npm run react && npm run cover; cat ./coverage/lcov.info | ./node_modules/.bin/coveralls ; fi' env: global: - TEST=true From c2b861bf8e46e4e2c05d924f39f93bb5cdf4851c Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 17 Nov 2017 22:04:07 -0600 Subject: [PATCH 007/618] [Dev Deps] update `babel-plugin-inline-svg`, `cross-env`, `enzyme`, `eslint`, `moment`, `sinon` --- package.json | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0e2c90548c..2b02006259 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "babel-core": "^6.26.0", "babel-loader": "^6.4.1", "babel-plugin-inline-react-svg": "^0.5.1", + "babel-plugin-inline-svg": "^0.1.0", "babel-plugin-istanbul": "^4.1.5", "babel-plugin-syntax-jsx": "^6.18.0", "babel-plugin-transform-replace-object-assign": "^0.2.1", @@ -67,10 +68,10 @@ "chai": "^4.1.2", "clean-css": "^4.1.9", "coveralls": "^2.13.3", - "cross-env": "^5.1.0", - "enzyme": "^3.1.0", + "cross-env": "^5.1.1", + "enzyme": "^3.2.0", "enzyme-adapter-react-helper": "^1.0.3", - "eslint": "^4.9.0", + "eslint": "^4.11.0", "eslint-config-airbnb": "^16.1.0", "eslint-plugin-import": "^2.8.0", "eslint-plugin-jsx-a11y": "^6.0.2", @@ -88,7 +89,7 @@ "karma-webpack": "^2.0.5", "mocha": "^3.5.3", "mocha-wrap": "^2.1.1", - "moment": "^2.19.1", + "moment": "^2.19.2", "moment-jalaali": "^0.7.2", "node-sass": "^4.5.3", "nyc": "^11.2.1", @@ -103,7 +104,7 @@ "rimraf": "^2.6.2", "safe-publish-latest": "^1.1.1", "sass-loader": "^6.0.6", - "sinon": "^4.0.1", + "sinon": "^4.1.2", "sinon-sandbox": "^1.0.2", "style-loader": "^0.19.0", "webpack": "^2.6.1" From 96655cf75b7b34391c1110cfde204deb0b2a5e3a Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 17 Nov 2017 22:12:20 -0600 Subject: [PATCH 008/618] [Tests] fix scripts --- .travis.yml | 3 +-- package.json | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 06d23f20f4..d73f3cae0e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,12 +12,11 @@ before_script: - 'if [ -n "${KARMA-}" ]; then export DISPLAY=:99.0; fi' - 'if [ -n "${KARMA-}" ]; then sh -e /etc/init.d/xvfb start; fi' - 'if [ -n "${KARMA-}" ]; then sleep 3; fi' - - 'if [ -n "${REACT-}" ] && [ "${TEST-}" = true ]; then npm run react && (npm ls >/dev/null || echo "temporary bypass"); fi' script: - 'if [ -n "${LINT-}" ]; then npm run lint ; fi' - 'if [ "${TEST-}" = true ]; then npm run tests-only ; fi' - 'if [ -n "${KARMA-}" ]; then npm run tests-karma ; fi' - - 'if [ -n "${COVERAGE-}" ] && [ "${TRAVIS_BRANCH-}" = "master" ]; then npm run react && npm run cover; cat ./coverage/lcov.info | ./node_modules/.bin/coveralls ; fi' + - 'if [ -n "${COVERAGE-}" ] && [ "${TRAVIS_BRANCH-}" = "master" ]; then npm run cover; cat ./coverage/lcov.info | ./node_modules/.bin/coveralls ; fi' env: global: - TEST=true diff --git a/package.json b/package.json index 2b02006259..0729f9b379 100644 --- a/package.json +++ b/package.json @@ -10,14 +10,16 @@ "prebuild:css": "rimraf lib/css && mkdir -p lib/css", "build:css": "node scripts/buildCSS.js", "clean": "rimraf lib esm", - "precover": "rimraf coverage", + "precover": "rimraf coverage && npm run react", "cover": "cross-env NODE_ENV=test node --max-old-space-size=2048 $(which nyc) npm run mocha test", "lint": "eslint --ext .js,.jsx src test", "mocha": "mocha ./test/_helpers", "storybook:uninstall": "npm uninstall --no-save @storybook/react && rimraf node_modules/@storybook node_modules/react-modal node_modules/react-dom-factories", "react": "enzyme-adapter-react-install 15", "pretest": "npm run --silent lint", + "pretests-only": "npm run react", "tests-only": "npm run mocha --silent test", + "pretests-karma": "npm run react", "tests-karma": "karma start", "test": "npm run tests-only", "storybook": "start-storybook -p 6006", From d40de87b908a62cd064e3b0de6c143ede377758e Mon Sep 17 00:00:00 2001 From: IjzerenHein Date: Fri, 24 Nov 2017 15:04:02 +0100 Subject: [PATCH 009/618] Fixed calendar-day background color which was hardcoded to #fff --- src/components/CalendarDay.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index ef06976f3b..4e1081ea75 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -180,7 +180,7 @@ export default withStyles(({ reactDates: { color } }) => ({ padding: 0, boxSizing: 'border-box', color: color.text, - background: '#fff', + background: color.background, ':hover': { background: color.core.borderLight, From 8256b3eb7f001ca9b4c46fcf5ac8452e96b69e6c Mon Sep 17 00:00:00 2001 From: IjzerenHein Date: Fri, 24 Nov 2017 15:05:08 +0100 Subject: [PATCH 010/618] Made it possible to set the date-input background using the theme --- src/components/DateInput.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index 85b86983db..9cffa74b26 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -293,6 +293,7 @@ export default withStyles(({ fontWeight: 200, fontSize: font.input.size, color: color.text, + backgroundColor: color.background, width: '100%', padding: `${spacing.displayTextPaddingVertical}px ${spacing.displayTextPaddingHorizontal}px`, border: border.input.border, From e2cf4b73284dd1f0d2affd0d11fff17ec124c302 Mon Sep 17 00:00:00 2001 From: Stephen Wyatt Bush Date: Wed, 15 Nov 2017 14:25:09 -0800 Subject: [PATCH 011/618] Reset after-hovered-start in componentWillReceiveProps - Fixes #831 after-hovered-start was being unset in onDayClick, which led to an issue where the modifier was left as applied when changing dates via keyboard navigation. This moves that logic into componentWillReceiveProps so that it works in both cases. --- src/components/DayPickerRangeController.jsx | 9 ++-- .../DayPickerRangeController_spec.jsx | 51 ++++++++++--------- 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 01f96bebfe..d3812f0a72 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -258,6 +258,11 @@ export default class DayPickerRangeController extends React.Component { if (didStartDateChange) { modifiers = this.deleteModifier(modifiers, this.props.startDate, 'selected-start'); modifiers = this.addModifier(modifiers, startDate, 'selected-start'); + if (this.props.startDate) { + const startSpan = this.props.startDate.clone().add(1, 'day'); + const endSpan = this.props.startDate.clone().add(this.props.minimumNights + 1, 'days'); + modifiers = this.deleteModifierFromRange(modifiers, startSpan, endSpan, 'after-hovered-start'); + } } if (didEndDateChange) { @@ -462,10 +467,6 @@ export default class DayPickerRangeController extends React.Component { } if (startDate) { - const startSpan = startDate.clone().add(1, 'day'); - const endSpan = startDate.clone().add(minimumNights + 1, 'days'); - modifiers = this.deleteModifierFromRange(modifiers, startSpan, endSpan, 'after-hovered-start'); - if (isSameDay(day, startDate)) { const newStartSpan = startDate.clone().add(1, 'day'); const newEndSpan = startDate.clone().add(minimumNights + 1, 'days'); diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index 0cf38ff725..f64a1d4421 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -296,6 +296,34 @@ describe('DayPickerRangeController', () => { }); expect(wrapper.instance().state.visibleDays).to.equal(visibleDays); }); + describe('startDate changed from one date to another', () => { + it('removes previous `after-hovered-start` range', () => { + const minimumNights = 5; + const startDate = moment().add(7, 'days'); + const dayAfterStartDate = startDate.clone().add(1, 'day'); + const firstAvailableDate = startDate.clone().add(minimumNights + 1, 'days'); + const deleteModifierFromRangeSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifierFromRange'); + const nextStartDate = moment().add(4, 'days'); + const wrapper = shallow(( + + )); + deleteModifierFromRangeSpy.reset(); + wrapper.instance().componentWillReceiveProps({ + ...props, + startDate: nextStartDate, + }); + const afterHoverStartCalls = getCallsByModifier(deleteModifierFromRangeSpy, 'after-hovered-start'); + expect(afterHoverStartCalls.length).to.equal(1); + expect(isSameDay(afterHoverStartCalls[0].args[1], dayAfterStartDate)).to.equal(true); + expect(isSameDay(afterHoverStartCalls[0].args[2], firstAvailableDate)).to.equal(true); + }); + }); }); }); @@ -1615,29 +1643,6 @@ describe('DayPickerRangeController', () => { }); describe('startDate exists', () => { - it('removes previous `after-hovered-start` range', () => { - const minimumNights = 5; - const startDate = moment().add(7, 'days'); - const dayAfterStartDate = startDate.clone().add(1, 'day'); - const firstAvailableDate = startDate.clone().add(minimumNights + 1, 'days'); - const deleteModifierFromRangeSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifierFromRange'); - const wrapper = shallow(( - - )); - deleteModifierFromRangeSpy.reset(); - wrapper.instance().onDayMouseEnter(today); - const afterHoverStartCalls = getCallsByModifier(deleteModifierFromRangeSpy, 'after-hovered-start'); - expect(afterHoverStartCalls.length).to.equal(1); - expect(isSameDay(afterHoverStartCalls[0].args[1], dayAfterStartDate)).to.equal(true); - expect(isSameDay(afterHoverStartCalls[0].args[2], firstAvailableDate)).to.equal(true); - }); - describe('hoverDate is startDate', () => { it('adds new `after-hovered-start` range', () => { const minimumNights = 5; From 11600900ac069fbe7e89980c76620226a8cd4d61 Mon Sep 17 00:00:00 2001 From: Stephen Wyatt Bush Date: Mon, 27 Nov 2017 11:40:31 -0800 Subject: [PATCH 012/618] Destructure this.props in componentWillReceiveProps --- src/components/DayPickerRangeController.jsx | 58 +++++++++++++-------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index d3812f0a72..ff4505e2cc 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -206,23 +206,38 @@ export default class DayPickerRangeController extends React.Component { numberOfMonths, enableOutsideDays, } = nextProps; + + const { + startDate: prevStartDate, + endDate: prevEndDate, + focusedInput: prevFocusedInput, + minimumNights: prevMinimumNights, + isOutsideRange: prevIsOutsideRange, + isDayBlocked: prevIsDayBlocked, + isDayHighlighted: prevIsDayHighlighted, + phrases: prevPhrases, + initialVisibleMonth: prevInitialVisibleMonth, + numberOfMonths: prevNumberOfMonths, + enableOutsideDays: prevEnableOutsideDays, + } = this.props; + let { visibleDays } = this.state; let recomputeOutsideRange = false; let recomputeDayBlocked = false; let recomputeDayHighlighted = false; - if (isOutsideRange !== this.props.isOutsideRange) { + if (isOutsideRange !== prevIsOutsideRange) { this.modifiers['blocked-out-of-range'] = day => isOutsideRange(day); recomputeOutsideRange = true; } - if (isDayBlocked !== this.props.isDayBlocked) { + if (isDayBlocked !== prevIsDayBlocked) { this.modifiers['blocked-calendar'] = day => isDayBlocked(day); recomputeDayBlocked = true; } - if (isDayHighlighted !== this.props.isDayHighlighted) { + if (isDayHighlighted !== prevIsDayHighlighted) { this.modifiers['highlighted-calendar'] = day => isDayHighlighted(day); recomputeDayHighlighted = true; } @@ -231,16 +246,16 @@ export default class DayPickerRangeController extends React.Component { recomputeOutsideRange || recomputeDayBlocked || recomputeDayHighlighted ); - const didStartDateChange = startDate !== this.props.startDate; - const didEndDateChange = endDate !== this.props.endDate; - const didFocusChange = focusedInput !== this.props.focusedInput; + const didStartDateChange = startDate !== prevStartDate; + const didEndDateChange = endDate !== prevEndDate; + const didFocusChange = focusedInput !== prevFocusedInput; if ( - numberOfMonths !== this.props.numberOfMonths || - enableOutsideDays !== this.props.enableOutsideDays || + numberOfMonths !== prevNumberOfMonths || + enableOutsideDays !== prevEnableOutsideDays || ( - initialVisibleMonth !== this.props.initialVisibleMonth && - !this.props.focusedInput && + initialVisibleMonth !== prevInitialVisibleMonth && + !prevFocusedInput && didFocusChange ) ) { @@ -256,26 +271,27 @@ export default class DayPickerRangeController extends React.Component { let modifiers = {}; if (didStartDateChange) { - modifiers = this.deleteModifier(modifiers, this.props.startDate, 'selected-start'); + modifiers = this.deleteModifier(modifiers, prevStartDate, 'selected-start'); modifiers = this.addModifier(modifiers, startDate, 'selected-start'); - if (this.props.startDate) { - const startSpan = this.props.startDate.clone().add(1, 'day'); - const endSpan = this.props.startDate.clone().add(this.props.minimumNights + 1, 'days'); + + if (prevStartDate) { + const startSpan = prevStartDate.clone().add(1, 'day'); + const endSpan = prevStartDate.clone().add(prevMinimumNights + 1, 'days'); modifiers = this.deleteModifierFromRange(modifiers, startSpan, endSpan, 'after-hovered-start'); } } if (didEndDateChange) { - modifiers = this.deleteModifier(modifiers, this.props.endDate, 'selected-end'); + modifiers = this.deleteModifier(modifiers, prevEndDate, 'selected-end'); modifiers = this.addModifier(modifiers, endDate, 'selected-end'); } if (didStartDateChange || didEndDateChange) { - if (this.props.startDate && this.props.endDate) { + if (prevStartDate && prevEndDate) { modifiers = this.deleteModifierFromRange( modifiers, - this.props.startDate, - this.props.endDate.clone().add(1, 'day'), + prevStartDate, + prevEndDate.clone().add(1, 'day'), 'selected-span', ); } @@ -303,9 +319,9 @@ export default class DayPickerRangeController extends React.Component { modifiers = this.addModifierToRange(modifiers, startSpan, endSpan, 'after-hovered-start'); } - if (minimumNights > 0 || minimumNights !== this.props.minimumNights) { + if (minimumNights > 0 || minimumNights !== prevMinimumNights) { if (didFocusChange || didStartDateChange) { - const startSpan = this.props.startDate ? this.props.startDate : this.today; + const startSpan = prevStartDate || this.today; modifiers = this.deleteModifierFromRange( modifiers, startSpan, @@ -378,7 +394,7 @@ export default class DayPickerRangeController extends React.Component { }); } - if (didFocusChange || phrases !== this.props.phrases) { + if (didFocusChange || phrases !== prevPhrases) { // set the appropriate CalendarDay phrase based on focusedInput const chooseAvailableDate = getChooseAvailableDatePhrase(phrases, focusedInput); From e7316bdeac589a131ed1a21b64326521cfca9563 Mon Sep 17 00:00:00 2001 From: steevn Date: Mon, 27 Nov 2017 12:28:31 -0800 Subject: [PATCH 013/618] Fix README typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 59727fbe0b..b5204534d4 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Create a CSS file with the contents of `require.resolve('react-dates/lib/css/_da To see this in action, you can check out https://github.com/majapw/react-dates-demo which adds `react-dates` on top of a simple `create-react-app` setup. #### Overriding styles -Right now, the easiest way to tweak `react-dates` to your heart's contents is to create another stylesheet to override the default react-dates styles. For example, you could create a file named `react_dates_overrides.css` with the following contents: +Right now, the easiest way to tweak `react-dates` to your heart's content is to create another stylesheet to override the default react-dates styles. For example, you could create a file named `react_dates_overrides.css` with the following contents: ```css .CalendarDay__highlighted_calendar { From 0d26f19b55ea93a142226d26e670256783dd92e9 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Mon, 27 Nov 2017 16:20:15 -0800 Subject: [PATCH 014/618] Bump react to v16 in dev deps --- package.json | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 0729f9b379..994206e9a7 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "lint": "eslint --ext .js,.jsx src test", "mocha": "mocha ./test/_helpers", "storybook:uninstall": "npm uninstall --no-save @storybook/react && rimraf node_modules/@storybook node_modules/react-modal node_modules/react-dom-factories", - "react": "enzyme-adapter-react-install 15", + "react": "enzyme-adapter-react-install 16", "pretest": "npm run --silent lint", "pretests-only": "npm run react", "tests-only": "npm run mocha --silent test", @@ -96,13 +96,10 @@ "node-sass": "^4.5.3", "nyc": "^11.2.1", "raw-loader": "^0.5.1", - "react": "^0.14 || ^15.5.4", - "react-addons-shallow-compare": "^0.14 || ^15.5.2", - "react-addons-test-utils": "^0.14 || ^15.5.1", - "react-dom": "^0.14 || ^15.5.4", - "react-test-renderer": "^15.6.1", + "react": "^0.14 || ^15.5.4 || ^16.1.1", + "react-dom": "^0.14 || ^15.5.4 || ^16.1.1", "react-with-styles-interface-aphrodite": "^3.1.1", - "react-with-styles-interface-css-compiler": "^1.0.1", + "react-with-styles-interface-css-compiler": "^1.1.0", "rimraf": "^2.6.2", "safe-publish-latest": "^1.1.1", "sass-loader": "^6.0.6", @@ -119,6 +116,7 @@ "object.assign": "^4.0.4", "object.values": "^1.0.4", "prop-types": "^15.5.10", + "react-addons-shallow-compare": "^15.5.2", "react-moment-proptypes": "^1.5.0", "react-portal": "^3.1.0", "react-with-styles": "^2.2.0", @@ -126,9 +124,8 @@ }, "peerDependencies": { "moment": "^2.18.1", - "react": ">=0.14", - "react-dom": ">=0.14", - "react-addons-shallow-compare": ">=0.14" + "react": "^0.14 || ^15.5.4 || ^16.1.1", + "react-dom": "^0.14 || ^15.5.4 || ^16.1.1" }, "greenkeeper": { "ignore": [ From 6ff22f2dfa5e6ce777b9eb234d4415848eb98cbd Mon Sep 17 00:00:00 2001 From: Toru Kobayashi Date: Tue, 28 Nov 2017 11:23:31 +0900 Subject: [PATCH 015/618] Add dayAriaLabelFormat into DayPickerSingleDateController and DayPickerRangeController --- src/components/DayPickerRangeController.jsx | 3 +++ src/components/DayPickerSingleDateController.jsx | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index ff4505e2cc..7ce61a1254 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -80,6 +80,7 @@ const propTypes = forbidExtraProps({ monthFormat: PropTypes.string, weekDayFormat: PropTypes.string, phrases: PropTypes.shape(getPhrasePropTypes(DayPickerPhrases)), + dayAriaLabelFormat: PropTypes.string, isRTL: PropTypes.bool, }); @@ -901,6 +902,7 @@ export default class DayPickerRangeController extends React.Component { showKeyboardShortcuts, isRTL, weekDayFormat, + dayAriaLabelFormat, verticalHeight, } = this.props; @@ -939,6 +941,7 @@ export default class DayPickerRangeController extends React.Component { phrases={phrases} isRTL={isRTL} weekDayFormat={weekDayFormat} + dayAriaLabelFormat={dayAriaLabelFormat} verticalHeight={verticalHeight} /> ); diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 1b0d037dcd..fb1be51b35 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -73,6 +73,7 @@ const propTypes = forbidExtraProps({ monthFormat: PropTypes.string, weekDayFormat: PropTypes.string, phrases: PropTypes.shape(getPhrasePropTypes(DayPickerPhrases)), + dayAriaLabelFormat: PropTypes.string, isRTL: PropTypes.bool, }); @@ -583,6 +584,7 @@ export default class DayPickerSingleDateController extends React.Component { isFocused, isRTL, phrases, + dayAriaLabelFormat, onOutsideClick, onBlur, showKeyboardShortcuts, @@ -622,6 +624,7 @@ export default class DayPickerSingleDateController extends React.Component { isRTL={isRTL} showKeyboardShortcuts={showKeyboardShortcuts} weekDayFormat={weekDayFormat} + dayAriaLabelFormat={dayAriaLabelFormat} verticalHeight={verticalHeight} /> ); From 0ce7164751c383e8b0e9daa0b6589c8da6bdc685 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Tue, 28 Nov 2017 11:53:48 -0800 Subject: [PATCH 016/618] Adds back today modifier --- src/components/CalendarDay.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index 948b31fd22..603707641e 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -139,6 +139,7 @@ class CalendarDay extends React.Component { {...css( styles.CalendarDay_container, isOutsideDay && styles.CalendarDay__outside, + modifiers.has('today') && styles.CalendarDay__today, modifiers.has('highlighted-calendar') && styles.CalendarDay__highlighted_calendar, modifiers.has('blocked-minimum-nights') && styles.CalendarDay__blocked_minimum_nights, modifiers.has('blocked-calendar') && styles.CalendarDay__blocked_calendar, @@ -351,5 +352,5 @@ export default withStyles(({ reactDates: { color } }) => ({ CalendarDay__selected_start: {}, CalendarDay__selected_end: {}, - + CalendarDay__today: {}, }))(CalendarDay); From c9c35c90aa982b8cef4c63c887c08ab941031a48 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Tue, 28 Nov 2017 13:15:06 -0800 Subject: [PATCH 017/618] Version 15.2.0 --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4e46807e6..1ae09f3d68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 15.2.0 +- [new] Add back today modifier (and class) ([#861](https://github.com/airbnb/react-dates/pull/861)) +- [new] Add ariaLabelFormat prop to CalendarDay ([#842](https://github.com/airbnb/react-dates/pull/842), [#857](https://github.com/airbnb/react-dates/pull/857)) +- [fix] Reset `after-hover-start` in `componentWillReceiveProps` instead of only on click ([#843](https://github.com/airbnb/react-dates/pull/843)) +- [fix] Use `color.background` variable instead of hardcoded #fff for theming ([#852](https://github.com/airbnb/react-dates/pull/852)) +- [fix] Update CalendarMonthGrid months based on locale change ([#795](https://github.com/airbnb/react-dates/pull/795)) + ## 15.1.0 - [fix] Add explicit border-radius on KeyboardShortcuts button ([#792](https://github.com/airbnb/react-dates/pull/792)) - [fix] Pass onClose from SingleDatePicker to DayPickerSingleDateController ([#816](https://github.com/airbnb/react-dates/pull/816)) diff --git a/package.json b/package.json index 994206e9a7..54333db759 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "15.1.0", + "version": "15.2.0", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From c9bb71d2c6e7c73a9ac9b8c4628ab71e9214ce30 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Tue, 28 Nov 2017 11:52:11 -0800 Subject: [PATCH 018/618] Add DateRangePicker example with presets --- .storybook-css/config.js | 1 + .storybook/config.js | 1 + examples/PresetDateRangePicker.jsx | 214 +++++++++++++++++++++++++++++ stories/PresetDateRangePicker.js | 36 +++++ 4 files changed, 252 insertions(+) create mode 100644 examples/PresetDateRangePicker.jsx create mode 100644 stories/PresetDateRangePicker.js diff --git a/.storybook-css/config.js b/.storybook-css/config.js index f3b12328c7..d6652020d0 100644 --- a/.storybook-css/config.js +++ b/.storybook-css/config.js @@ -67,6 +67,7 @@ function loadStories() { require('../stories/DayPickerRangeController'); require('../stories/DayPickerSingleDateController'); require('../stories/DayPicker'); + require('../stories/PresetDateRangePicker'); } setAddon(infoAddon); diff --git a/.storybook/config.js b/.storybook/config.js index f0eca636d6..3764b18a9f 100644 --- a/.storybook/config.js +++ b/.storybook/config.js @@ -67,6 +67,7 @@ function loadStories() { require('../stories/DayPickerRangeController'); require('../stories/DayPickerSingleDateController'); require('../stories/DayPicker'); + require('../stories/PresetDateRangePicker'); } setAddon(infoAddon); diff --git a/examples/PresetDateRangePicker.jsx b/examples/PresetDateRangePicker.jsx new file mode 100644 index 0000000000..31662f5cc5 --- /dev/null +++ b/examples/PresetDateRangePicker.jsx @@ -0,0 +1,214 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import momentPropTypes from 'react-moment-proptypes'; +import moment from 'moment'; +import omit from 'lodash/omit'; + +import { withStyles, withStylesPropTypes, css } from 'react-with-styles'; + +import DateRangePicker from '../src/components/DateRangePicker'; + +import { DateRangePickerPhrases } from '../src/defaultPhrases'; +import DateRangePickerShape from '../src/shapes/DateRangePickerShape'; +import { START_DATE, END_DATE, HORIZONTAL_ORIENTATION, ANCHOR_LEFT } from '../src/constants'; +import isSameDay from '../src/utils/isSameDay'; + +const propTypes = { + ...withStylesPropTypes, + + // example props for the demo + autoFocus: PropTypes.bool, + autoFocusEndDate: PropTypes.bool, + initialStartDate: momentPropTypes.momentObj, + initialEndDate: momentPropTypes.momentObj, + presets: PropTypes.arrayOf(PropTypes.shape({ + text: PropTypes.string, + start: momentPropTypes.momentObj, + end: momentPropTypes.momentObj, + })), + + ...omit(DateRangePickerShape, [ + 'startDate', + 'endDate', + 'onDatesChange', + 'focusedInput', + 'onFocusChange', + ]), +}; + +const defaultProps = { + // example props for the demo + autoFocus: false, + autoFocusEndDate: false, + initialStartDate: null, + initialEndDate: null, + presets: [], + + // input related props + startDateId: START_DATE, + startDatePlaceholderText: 'Start Date', + endDateId: END_DATE, + endDatePlaceholderText: 'End Date', + disabled: false, + required: false, + screenReaderInputMessage: '', + showClearDates: false, + showDefaultInputIcon: false, + customInputIcon: null, + customArrowIcon: null, + customCloseIcon: null, + + // calendar presentation and interaction related props + renderMonth: null, + orientation: HORIZONTAL_ORIENTATION, + anchorDirection: ANCHOR_LEFT, + horizontalMargin: 0, + withPortal: false, + withFullScreenPortal: false, + initialVisibleMonth: null, + numberOfMonths: 2, + keepOpenOnDateSelect: false, + reopenPickerOnClearDates: false, + isRTL: false, + + // navigation related props + navPrev: null, + navNext: null, + onPrevMonthClick() {}, + onNextMonthClick() {}, + onClose() {}, + + // day presentation and interaction related props + renderDay: null, + minimumNights: 0, + enableOutsideDays: false, + isDayBlocked: () => false, + isOutsideRange: day => false, + isDayHighlighted: () => false, + + // internationalization + displayFormat: () => moment.localeData().longDateFormat('L'), + monthFormat: 'MMMM YYYY', + phrases: DateRangePickerPhrases, +}; + +class DateRangePickerWrapper extends React.Component { + constructor(props) { + super(props); + + let focusedInput = null; + if (props.autoFocus) { + focusedInput = START_DATE; + } else if (props.autoFocusEndDate) { + focusedInput = END_DATE; + } + + this.state = { + focusedInput, + startDate: props.initialStartDate, + endDate: props.initialEndDate, + }; + + this.onDatesChange = this.onDatesChange.bind(this); + this.onFocusChange = this.onFocusChange.bind(this); + this.renderDatePresets = this.renderDatePresets.bind(this); + } + + onDatesChange({ startDate, endDate }) { + this.setState({ startDate, endDate }); + } + + onFocusChange(focusedInput) { + this.setState({ focusedInput }); + } + + renderDatePresets() { + const { presets, styles } = this.props; + const { startDate, endDate } = this.state; + + return ( +
+ {presets.map(({ text, start, end }) => { + const isSelected = isSameDay(start, startDate) && isSameDay(end, endDate); + return ( + + ); + })} +
+ ); + } + + render() { + const { focusedInput, startDate, endDate } = this.state; + + // autoFocus, autoFocusEndDate, initialStartDate and initialEndDate are helper props for the + // example wrapper but are not props on the SingleDatePicker itself and + // thus, have to be omitted. + const props = omit(this.props, [ + 'autoFocus', + 'autoFocusEndDate', + 'initialStartDate', + 'initialEndDate', + 'presets', + ]); + + return ( +
+ +
+ ); + } +} + +DateRangePickerWrapper.propTypes = propTypes; +DateRangePickerWrapper.defaultProps = defaultProps; + +export default withStyles(({ reactDates: { color } }) => ({ + PresetDateRangePicker_panel: { + padding: '0 22px 11px 22px', + }, + + PresetDateRangePicker_button: { + position: 'relative', + height: '100%', + textAlign: 'center', + background: 'none', + border: `2px solid ${color.core.primary}`, + color: color.core.primary, + padding: '4px 12px', + marginRight: 8, + font: 'inherit', + fontWeight: 700, + lineHeight: 'normal', + overflow: 'visible', + boxSizing: 'border-box', + cursor: 'pointer', + + ':active': { + outline: 0, + }, + }, + + PresetDateRangePicker_button__selected: { + color: color.core.white, + background: color.core.primary, + }, +}))(DateRangePickerWrapper); diff --git a/stories/PresetDateRangePicker.js b/stories/PresetDateRangePicker.js new file mode 100644 index 0000000000..e5ac247b6e --- /dev/null +++ b/stories/PresetDateRangePicker.js @@ -0,0 +1,36 @@ +import React from 'react'; +import { storiesOf } from '@storybook/react'; +import moment from 'moment'; + +import PresetDateRangePicker from '../examples/PresetDateRangePicker'; + +const today = moment(); +const tomorrow = moment().add(1, 'day'); +const presets = [{ + text: 'Today', + start: today, + end: today, +}, +{ + text: 'Tomorrow', + start: tomorrow, + end: tomorrow, +}, +{ + text: 'Next Week', + start: today, + end: moment().add(1, 'week'), +}, +{ + text: 'Next Month', + start: today, + end: moment().add(1, 'month'), +}]; + +storiesOf('PresetDatePicker', module) + .addWithInfo('default', () => ( + + )); From 61cb8a6d2a75cddb85f817c2ddde1dbf072f2d5f Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Tue, 28 Nov 2017 13:55:28 -0800 Subject: [PATCH 019/618] Separate out InfoPanelDecorator and add usage information for the PresetDateRangePicker --- scripts/buildCSS.js | 8 ++++-- stories/DayPickerRangeController.js | 27 ++---------------- stories/DayPickerSingleDateController.js | 29 +++----------------- stories/InfoPanelDecorator.js | 35 ++++++++++++++++++++++++ stories/PresetDateRangePicker.js | 12 ++++++++ 5 files changed, 59 insertions(+), 52 deletions(-) create mode 100644 stories/InfoPanelDecorator.js diff --git a/scripts/buildCSS.js b/scripts/buildCSS.js index ab1b7a9e7c..0efc8b315d 100644 --- a/scripts/buildCSS.js +++ b/scripts/buildCSS.js @@ -16,12 +16,14 @@ require('../test/_helpers/ignoreSVGStrings'); registerMaxSpecificity(0); registerCSSInterfaceWithDefaultTheme(); -const DateRangePickerPath = './src/components/DateRangePicker.jsx'; -const SingleDatePickerPath = './src/components/SingleDatePicker.jsx'; +const DateRangePickerPath = './examples/DateRangePickerWrapper.jsx'; +const SingleDatePickerPath = './examples/SingleDatePickerWrapper.jsx'; +const PresetDateRangePickerPath = './examples/PresetDateRangePicker.jsx'; const dateRangePickerCSS = compileCSS(DateRangePickerPath); const singleDatePickerCSS = compileCSS(SingleDatePickerPath); -const CSS = dateRangePickerCSS + singleDatePickerCSS; +const presetDatePickerCSS = compileCSS(PresetDateRangePickerPath); +const CSS = dateRangePickerCSS + singleDatePickerCSS + presetDatePickerCSS; const format = new CleanCSS({ level: optimizeForProduction ? 2 : 0, diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index a13ab7a28b..3babfe7af9 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -2,6 +2,8 @@ import React from 'react'; import moment from 'moment'; import { storiesOf, action } from '@storybook/react'; +import InfoPanelDecorator, { monospace } from './InfoPanelDecorator'; + import isSameDay from '../src/utils/isSameDay'; import isInclusivelyAfterDay from '../src/utils/isInclusivelyAfterDay'; @@ -9,8 +11,6 @@ import { VERTICAL_ORIENTATION } from '../src/constants'; import DayPickerRangeControllerWrapper from '../examples/DayPickerRangeControllerWrapper'; -const monospace = text => `${text}`; - const dayPickerRangeControllerInfo = `The ${monospace('DayPickerRangeController')} component is a fully controlled version of the ${monospace('DayPicker')} that has built-in rules for selecting a date range. Unlike the ${monospace('DayPicker')}, which requires the consumer to explicitly define @@ -28,27 +28,6 @@ const dayPickerRangeControllerInfo = `The ${monospace('DayPickerRangeController' ${monospace('DateRangePicker')} functionality and calendar presentation, but would like to implement your own inputs.`; -const InfoPanel = ({ text }) => ( -
- -
-); - -const InfoPanelDecorator = story => ( -
- - {story()} -
-); - const TestPrevIcon = () => ( ( `${text}`; - -const dayPickerRangeControllerInfo = `The ${monospace('DayPickerSingleDateController')} component is a +const dayPickerSingleDateControllerInfo = `The ${monospace('DayPickerSingleDateController')} component is a fully controlled version of the ${monospace('DayPicker')} that has built-in rules for selecting a single date. Unlike the ${monospace('DayPicker')}, which requires the consumer to explicitly define ${monospace('onDayMouseEnter')}, ${monospace('onDayMouseLeave')}, and ${monospace('onDayClick')} @@ -28,27 +28,6 @@ const dayPickerRangeControllerInfo = `The ${monospace('DayPickerSingleDateContro ${monospace('SingleDatePicker')} functionality and calendar presentation, but would like to implement your own input.`; -const InfoPanel = ({ text }) => ( -
- -
-); - -const InfoPanelDecorator = story => ( -
- - {story()} -
-); - const TestPrevIcon = () => ( ( ${text}`; +} + +function InfoPanel({ text }) { + return ( +
+ +
+ ); +} + +InfoPanel.propTypes = { + text: PropTypes.string.isRequired, +}; + +export default function InfoPanelDecorator(text) { + return story => ( +
+ + {story()} +
+ ); +} diff --git a/stories/PresetDateRangePicker.js b/stories/PresetDateRangePicker.js index e5ac247b6e..7d3a9864fb 100644 --- a/stories/PresetDateRangePicker.js +++ b/stories/PresetDateRangePicker.js @@ -4,6 +4,17 @@ import moment from 'moment'; import PresetDateRangePicker from '../examples/PresetDateRangePicker'; +import InfoPanelDecorator, { monospace } from './InfoPanelDecorator'; + +const presetDateRangePickerControllerInfo = `The ${monospace('PresetDateRangePicker')} component is not + exported by ${monospace('react-dates')}. It is instead an example of how you might use the + ${monospace('DateRangePicker')} along with the ${monospace('renderCalendarInfo')} prop in + order to add preset range buttons for easy range selection. You can see the example code + + here and + + here.`; + const today = moment(); const tomorrow = moment().add(1, 'day'); const presets = [{ @@ -28,6 +39,7 @@ const presets = [{ }]; storiesOf('PresetDatePicker', module) + .addDecorator(InfoPanelDecorator(presetDateRangePickerControllerInfo)) .addWithInfo('default', () => ( Date: Tue, 28 Nov 2017 16:37:25 -0800 Subject: [PATCH 020/618] Version 15.2.1 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ae09f3d68..43e6509f09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Change Log +## 15.2.1 +- [fix] Republish `_datepicker.css` + ## 15.2.0 - [new] Add back today modifier (and class) ([#861](https://github.com/airbnb/react-dates/pull/861)) - [new] Add ariaLabelFormat prop to CalendarDay ([#842](https://github.com/airbnb/react-dates/pull/842), [#857](https://github.com/airbnb/react-dates/pull/857)) diff --git a/package.json b/package.json index 54333db759..85af5863f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "15.2.0", + "version": "15.2.1", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From 17899356500562ec62b0f083196ca67a3da1d21c Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Tue, 28 Nov 2017 19:26:44 -0800 Subject: [PATCH 021/618] Remove startDateId and endDateId default prop values --- src/components/DateRangePicker.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 1143009bd8..026b903b45 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -46,9 +46,7 @@ const defaultProps = { focusedInput: null, // input related props - startDateId: START_DATE, startDatePlaceholderText: 'Start Date', - endDateId: END_DATE, endDatePlaceholderText: 'End Date', disabled: false, required: false, From 81ba5d1276cb3b5369f3dbee48fb7b5fd84ca61a Mon Sep 17 00:00:00 2001 From: Weilin Shi <934587911@qq.com> Date: Wed, 29 Nov 2017 15:16:37 +0800 Subject: [PATCH 022/618] Remove unused style in KeyboardShortcutRow.jsx `width: 'auto'` is ignored due to display. --- src/components/KeyboardShortcutRow.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/KeyboardShortcutRow.jsx b/src/components/KeyboardShortcutRow.jsx index 5bfa2fd4de..fed00a93ac 100644 --- a/src/components/KeyboardShortcutRow.jsx +++ b/src/components/KeyboardShortcutRow.jsx @@ -72,7 +72,6 @@ export default withStyles(({ reactDates: { color } }) => ({ }, KeyboardShortcutRow_keyContainer__block: { - width: 'auto', textAlign: 'left', display: 'inline', }, From 2bf64761b825e350b6a2304fd340f3e46a67a03f Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Wed, 29 Nov 2017 16:08:28 -0800 Subject: [PATCH 023/618] Add borderless option to the `DayPicker` variations --- README.md | 1 + src/components/DayPicker.jsx | 12 +++++++++--- src/components/DayPickerRangeController.jsx | 4 ++++ src/components/DayPickerSingleDateController.jsx | 4 ++++ stories/DayPicker.js | 5 ++++- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b5204534d4..1f6af827c9 100644 --- a/README.md +++ b/README.md @@ -262,6 +262,7 @@ The following is a list of other *OPTIONAL* props you may provide to the `DayPic renderCalendarInfo: PropTypes.func, onOutsideClick: PropTypes.func, keepOpenOnDateSelect: PropTypes.bool, + noBorder: PropTypes.bool, // navigation related props navPrev: PropTypes.node, diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index 07cc79efcb..c5aeae48fc 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -56,6 +56,7 @@ const propTypes = forbidExtraProps({ daySize: nonNegativeInteger, isRTL: PropTypes.bool, verticalHeight: nonNegativeInteger, + noBorder: PropTypes.bool, // navigation props navPrev: PropTypes.node, @@ -102,6 +103,7 @@ export const defaultProps = { daySize: DAY_SIZE, isRTL: false, verticalHeight: null, + noBorder: false, // navigation props navPrev: null, @@ -688,6 +690,7 @@ class DayPicker extends React.Component { phrases, verticalHeight, dayAriaLabelFormat, + noBorder, } = this.props; const numOfWeekHeaders = this.isVertical() ? 1 : numberOfMonths; @@ -748,6 +751,7 @@ class DayPicker extends React.Component { this.isVertical() && withPortal && styles.DayPicker_portal__vertical, dayPickerStyle, !hasSetHeight && styles.DayPicker__hidden, + !noBorder && styles.DayPicker__withBorder, )} > @@ -780,7 +784,6 @@ class DayPicker extends React.Component { this.isVertical() && styles.DayPicker_transitionContainer__vertical, verticalScrollable && styles.DayPicker_transitionContainer__verticalScrollable, transitionContainerStyle, - )} ref={this.setTransitionContainerRef} > @@ -844,8 +847,6 @@ export default withStyles(({ reactDates: { color, zIndex } }) => ({ DayPicker__horizontal: { background: color.background, - boxShadow: '0 2px 6px rgba(0, 0, 0, 0.05), 0 0 0 1px rgba(0, 0, 0, 0.07)', - borderRadius: 3, }, DayPicker__verticalScrollable: { @@ -856,6 +857,11 @@ export default withStyles(({ reactDates: { color, zIndex } }) => ({ visibility: 'hidden', }, + DayPicker__withBorder: { + boxShadow: '0 2px 6px rgba(0, 0, 0, 0.05), 0 0 0 1px rgba(0, 0, 0, 0.07)', + borderRadius: 3, + }, + DayPicker_portal__horizontal: { boxShadow: 'none', position: 'absolute', diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 7ce61a1254..64846a2784 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -59,6 +59,7 @@ const propTypes = forbidExtraProps({ initialVisibleMonth: PropTypes.func, hideKeyboardShortcutsPanel: PropTypes.bool, daySize: nonNegativeInteger, + noBorder: PropTypes.bool, navPrev: PropTypes.node, navNext: PropTypes.node, @@ -121,6 +122,7 @@ const defaultProps = { renderCalendarInfo: null, firstDayOfWeek: null, verticalHeight: null, + noBorder: false, // accessibility onBlur() {}, @@ -904,6 +906,7 @@ export default class DayPickerRangeController extends React.Component { weekDayFormat, dayAriaLabelFormat, verticalHeight, + noBorder, } = this.props; const { currentMonth, phrases, visibleDays } = this.state; @@ -943,6 +946,7 @@ export default class DayPickerRangeController extends React.Component { weekDayFormat={weekDayFormat} dayAriaLabelFormat={dayAriaLabelFormat} verticalHeight={verticalHeight} + noBorder={noBorder} /> ); } diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index fb1be51b35..b438b5f47b 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -54,6 +54,7 @@ const propTypes = forbidExtraProps({ hideKeyboardShortcutsPanel: PropTypes.bool, daySize: nonNegativeInteger, verticalHeight: nonNegativeInteger, + noBorder: PropTypes.bool, navPrev: PropTypes.node, navNext: PropTypes.node, @@ -102,6 +103,7 @@ const defaultProps = { firstDayOfWeek: null, daySize: DAY_SIZE, verticalHeight: null, + noBorder: false, navPrev: null, navNext: null, @@ -590,6 +592,7 @@ export default class DayPickerSingleDateController extends React.Component { showKeyboardShortcuts, weekDayFormat, verticalHeight, + noBorder, } = this.props; const { currentMonth, visibleDays } = this.state; @@ -626,6 +629,7 @@ export default class DayPickerSingleDateController extends React.Component { weekDayFormat={weekDayFormat} dayAriaLabelFormat={dayAriaLabelFormat} verticalHeight={verticalHeight} + noBorder={noBorder} /> ); diff --git a/stories/DayPicker.js b/stories/DayPicker.js index cea8b8ae6d..8420f3b050 100644 --- a/stories/DayPicker.js +++ b/stories/DayPicker.js @@ -119,6 +119,9 @@ storiesOf('DayPicker', module) )) .addWithInfo('with custom week day format', () => ( + )) + .addWithInfo('noBorder', () => ( + )); From 9f3238f35639f5a5b86cfd0a9de67f57146f4aad Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Wed, 29 Nov 2017 16:28:53 -0800 Subject: [PATCH 024/618] Add borderless option for inputs --- README.md | 2 ++ src/components/DateRangePicker.jsx | 3 +++ src/components/DateRangePickerInput.jsx | 9 ++++++++- src/components/DateRangePickerInputController.jsx | 4 ++++ src/components/SingleDatePicker.jsx | 3 +++ src/components/SingleDatePickerInput.jsx | 8 ++++++++ src/shapes/DateRangePickerShape.js | 1 + src/shapes/SingleDatePickerShape.js | 1 + stories/DateRangePicker_input.js | 7 +++++++ stories/SingleDatePicker_input.js | 6 ++++++ 10 files changed, 43 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b5204534d4..eb5ebc36dc 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,7 @@ showDefaultInputIcon: PropTypes.bool, customInputIcon: PropTypes.node, customArrowIcon: PropTypes.node, customCloseIcon: PropTypes.node, +noBorder: PropTypes.bool, // calendar presentation and interaction related props renderMonth: PropTypes.func, @@ -196,6 +197,7 @@ showClearDate: PropTypes.bool, customCloseIcon: PropTypes.node, showDefaultInputIcon: PropTypes.bool, customInputIcon: PropTypes.node, +noBorder: PropTypes.bool, // calendar presentation and interaction related props renderMonth: PropTypes.func, diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 1143009bd8..2416003029 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -60,6 +60,7 @@ const defaultProps = { customInputIcon: null, customArrowIcon: null, customCloseIcon: null, + noBorder: false, // calendar presentation and interaction related props renderMonth: null, @@ -436,6 +437,7 @@ class DateRangePicker extends React.Component { onDatesChange, onClose, isRTL, + noBorder, styles, } = this.props; @@ -481,6 +483,7 @@ class DateRangePicker extends React.Component { screenReaderMessage={screenReaderInputMessage} isFocused={isDateRangePickerInputFocused} isRTL={isRTL} + noBorder={noBorder} /> {this.maybeRenderDayPickerWithPortal()} diff --git a/src/components/DateRangePickerInput.jsx b/src/components/DateRangePickerInput.jsx index f561bfe0a6..b92cc7da48 100644 --- a/src/components/DateRangePickerInput.jsx +++ b/src/components/DateRangePickerInput.jsx @@ -58,6 +58,7 @@ const propTypes = forbidExtraProps({ customInputIcon: PropTypes.node, customArrowIcon: PropTypes.node, customCloseIcon: PropTypes.node, + noBorder: PropTypes.bool, // accessibility isFocused: PropTypes.bool, // describes actual DOM focus @@ -100,6 +101,7 @@ const defaultProps = { customInputIcon: null, customArrowIcon: null, customCloseIcon: null, + noBorder: false, // accessibility isFocused: false, @@ -143,6 +145,7 @@ function DateRangePickerInput({ isFocused, phrases, isRTL, + noBorder, styles, }) { const calendarIcon = customInputIcon || ( @@ -174,6 +177,7 @@ function DateRangePickerInput({ styles.DateRangePickerInput, disabled && styles.DateRangePickerInput__disabled, isRTL && styles.DateRangePickerInput__rtl, + !noBorder && styles.DateRangePickerInput__withBorder, )} > {inputIconPosition === ICON_BEFORE_POSITION && inputIcon} @@ -251,7 +255,6 @@ DateRangePickerInput.defaultProps = defaultProps; export default withStyles(({ reactDates: { color, sizing } }) => ({ DateRangePickerInput: { backgroundColor: color.background, - border: `1px solid ${color.core.grayLighter}`, display: 'inline-block', }, @@ -259,6 +262,10 @@ export default withStyles(({ reactDates: { color, sizing } }) => ({ background: color.disabled, }, + DateRangePickerInput__withBorder: { + border: `1px solid ${color.core.grayLighter}`, + }, + DateRangePickerInput__rtl: { direction: 'rtl', }, diff --git a/src/components/DateRangePickerInputController.jsx b/src/components/DateRangePickerInputController.jsx index 5f4fd014cf..68e2deb0af 100644 --- a/src/components/DateRangePickerInputController.jsx +++ b/src/components/DateRangePickerInputController.jsx @@ -41,6 +41,7 @@ const propTypes = forbidExtraProps({ required: PropTypes.bool, readOnly: PropTypes.bool, openDirection: openDirectionShape, + noBorder: PropTypes.bool, keepOpenOnDateSelect: PropTypes.bool, reopenPickerOnClearDates: PropTypes.bool, @@ -88,6 +89,7 @@ const defaultProps = { required: false, readOnly: false, openDirection: OPEN_DOWN, + noBorder: false, keepOpenOnDateSelect: false, reopenPickerOnClearDates: false, @@ -261,6 +263,7 @@ export default class DateRangePickerInputController extends React.Component { onKeyDownArrowDown, onKeyDownQuestionMark, isRTL, + noBorder, } = this.props; const startDateString = this.getDateString(startDate); @@ -300,6 +303,7 @@ export default class DateRangePickerInputController extends React.Component { onKeyDownArrowDown={onKeyDownArrowDown} onKeyDownQuestionMark={onKeyDownQuestionMark} isRTL={isRTL} + noBorder={noBorder} /> ); } diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index bb9a201d59..dc1e63cd5b 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -54,6 +54,7 @@ const defaultProps = { inputIconPosition: ICON_BEFORE_POSITION, customInputIcon: null, customCloseIcon: null, + noBorder: false, // calendar presentation and interaction related props orientation: HORIZONTAL_ORIENTATION, @@ -452,6 +453,7 @@ class SingleDatePicker extends React.Component { withFullScreenPortal, screenReaderInputMessage, isRTL, + noBorder, styles, } = this.props; @@ -490,6 +492,7 @@ class SingleDatePicker extends React.Component { screenReaderMessage={screenReaderInputMessage} phrases={phrases} isRTL={isRTL} + noBorder={noBorder} /> {this.maybeRenderDayPickerWithPortal()} diff --git a/src/components/SingleDatePickerInput.jsx b/src/components/SingleDatePickerInput.jsx index 066db51f11..8ebd67415c 100644 --- a/src/components/SingleDatePickerInput.jsx +++ b/src/components/SingleDatePickerInput.jsx @@ -34,6 +34,8 @@ const propTypes = forbidExtraProps({ inputIconPosition: IconPositionShape, customInputIcon: PropTypes.node, isRTL: PropTypes.bool, + noBorder: PropTypes.bool, + onChange: PropTypes.func, onClearDate: PropTypes.func, onFocus: PropTypes.func, @@ -63,6 +65,7 @@ const defaultProps = { customCloseIcon: null, customInputIcon: null, isRTL: false, + noBorder: false, onChange() {}, onClearDate() {}, @@ -102,6 +105,7 @@ function SingleDatePickerInput({ customInputIcon, openDirection, isRTL, + noBorder, styles, }) { const calendarIcon = customInputIcon || ( @@ -130,6 +134,7 @@ function SingleDatePickerInput({ styles.SingleDatePickerInput, disabled && styles.SingleDatePickerInput__disabled, isRTL && styles.SingleDatePickerInput__rtl, + !noBorder && styles.SingleDatePickerInput__withBorder, )} > {inputIconPosition === ICON_BEFORE_POSITION && inputIcon} @@ -183,6 +188,9 @@ SingleDatePickerInput.defaultProps = defaultProps; export default withStyles(({ reactDates: { color } }) => ({ SingleDatePickerInput: { backgroundColor: color.background, + }, + + SingleDatePickerInput__withBorder: { border: `1px solid ${color.core.border}`, }, diff --git a/src/shapes/DateRangePickerShape.js b/src/shapes/DateRangePickerShape.js index df58104ff3..436621ed30 100644 --- a/src/shapes/DateRangePickerShape.js +++ b/src/shapes/DateRangePickerShape.js @@ -38,6 +38,7 @@ export default { customInputIcon: PropTypes.node, customArrowIcon: PropTypes.node, customCloseIcon: PropTypes.node, + noBorder: PropTypes.bool, // calendar presentation and interaction related props renderMonth: PropTypes.func, diff --git a/src/shapes/SingleDatePickerShape.js b/src/shapes/SingleDatePickerShape.js index 2a949f0626..4f17111f6f 100644 --- a/src/shapes/SingleDatePickerShape.js +++ b/src/shapes/SingleDatePickerShape.js @@ -31,6 +31,7 @@ export default { showDefaultInputIcon: PropTypes.bool, inputIconPosition: IconPositionShape, customInputIcon: PropTypes.node, + noBorder: PropTypes.bool, // calendar presentation and interaction related props renderMonth: PropTypes.func, diff --git a/stories/DateRangePicker_input.js b/stories/DateRangePicker_input.js index 205f63c52d..99dc81ee3b 100644 --- a/stories/DateRangePicker_input.js +++ b/stories/DateRangePicker_input.js @@ -119,4 +119,11 @@ storiesOf('DRP - Input Props', module) initialEndDate={moment().add(10, 'days')} screenReaderInputMessage="Here you could inform screen reader users of the date format, minimum nights, blocked out dates, etc" /> + )) + .addWithInfo('noBorder', () => ( + )); diff --git a/stories/SingleDatePicker_input.js b/stories/SingleDatePicker_input.js index 9ba42e1bc4..1953bdac46 100644 --- a/stories/SingleDatePicker_input.js +++ b/stories/SingleDatePicker_input.js @@ -71,4 +71,10 @@ storiesOf('SDP - Input Props', module) initialDate={moment().add(3, 'days')} screenReaderInputMessage="Here you could inform screen reader users of the date format, minimum nights, blocked out dates, etc" /> + )) + .addWithInfo('noBorder', () => ( + )); From 8a4ea442822ee1dc0f083cc4860214346b25073b Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Wed, 29 Nov 2017 17:06:48 -0800 Subject: [PATCH 025/618] Add block styling to the SDP/DRP inputs --- README.md | 2 ++ src/components/DateRangePicker.jsx | 14 ++++++++++- src/components/DateRangePickerInput.jsx | 22 ++++++++++++++++-- .../DateRangePickerInputController.jsx | 4 ++++ src/components/SingleDatePicker.jsx | 14 ++++++++++- src/components/SingleDatePickerInput.jsx | 23 +++++++++++++++++-- src/shapes/DateRangePickerShape.js | 1 + src/shapes/SingleDatePickerShape.js | 1 + stories/DateRangePicker_input.js | 8 +++++++ stories/SingleDatePicker_input.js | 7 ++++++ 10 files changed, 90 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index eb5ebc36dc..0f86a27e95 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,7 @@ customInputIcon: PropTypes.node, customArrowIcon: PropTypes.node, customCloseIcon: PropTypes.node, noBorder: PropTypes.bool, +block: PropTypes.bool, // calendar presentation and interaction related props renderMonth: PropTypes.func, @@ -198,6 +199,7 @@ customCloseIcon: PropTypes.node, showDefaultInputIcon: PropTypes.bool, customInputIcon: PropTypes.node, noBorder: PropTypes.bool, +block: PropTypes.bool, // calendar presentation and interaction related props renderMonth: PropTypes.func, diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 2416003029..909337cd6a 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -61,6 +61,7 @@ const defaultProps = { customArrowIcon: null, customCloseIcon: null, noBorder: false, + block: false, // calendar presentation and interaction related props renderMonth: null, @@ -438,6 +439,7 @@ class DateRangePicker extends React.Component { onClose, isRTL, noBorder, + block, styles, } = this.props; @@ -446,7 +448,12 @@ class DateRangePicker extends React.Component { const onOutsideClick = (!withPortal && !withFullScreenPortal) ? this.onOutsideClick : undefined; return ( -
+
{this.maybeRenderDayPickerWithPortal()} @@ -503,6 +511,10 @@ export default withStyles(({ reactDates: { color, zIndex, spacing } }) => ({ display: 'inline-block', }, + DateRangePicker__block: { + display: 'block', + }, + DateRangePicker_picker: { zIndex: zIndex + 1, backgroundColor: color.background, diff --git a/src/components/DateRangePickerInput.jsx b/src/components/DateRangePickerInput.jsx index b92cc7da48..330b01f17f 100644 --- a/src/components/DateRangePickerInput.jsx +++ b/src/components/DateRangePickerInput.jsx @@ -59,6 +59,7 @@ const propTypes = forbidExtraProps({ customArrowIcon: PropTypes.node, customCloseIcon: PropTypes.node, noBorder: PropTypes.bool, + block: PropTypes.bool, // accessibility isFocused: PropTypes.bool, // describes actual DOM focus @@ -102,6 +103,7 @@ const defaultProps = { customArrowIcon: null, customCloseIcon: null, noBorder: false, + block: false, // accessibility isFocused: false, @@ -146,6 +148,7 @@ function DateRangePickerInput({ phrases, isRTL, noBorder, + block, styles, }) { const calendarIcon = customInputIcon || ( @@ -178,6 +181,8 @@ function DateRangePickerInput({ disabled && styles.DateRangePickerInput__disabled, isRTL && styles.DateRangePickerInput__rtl, !noBorder && styles.DateRangePickerInput__withBorder, + block && styles.DateRangePickerInput__block, + showClearDates && styles.DateRangePickerInput__showClearDates, )} > {inputIconPosition === ICON_BEFORE_POSITION && inputIcon} @@ -234,6 +239,7 @@ function DateRangePickerInput({ aria-label={phrases.clearDates} {...css( styles.DateRangePickerInput_clearDates, + !customCloseIcon && styles.DateRangePickerInput_clearDates_default, !(startDate || endDate) && styles.DateRangePickerInput_clearDates__hide, )} onClick={onClearDates} @@ -270,6 +276,14 @@ export default withStyles(({ reactDates: { color, sizing } }) => ({ direction: 'rtl', }, + DateRangePickerInput__block: { + display: 'block', + }, + + DateRangePickerInput__showClearDates: { + paddingRight: 30, + }, + DateRangePickerInput_arrow: { display: 'inline-block', verticalAlign: 'middle', @@ -291,11 +305,15 @@ export default withStyles(({ reactDates: { color, sizing } }) => ({ overflow: 'visible', cursor: 'pointer', - display: 'inline-block', - verticalAlign: 'middle', padding: 10, margin: '0 10px 0 5px', + position: 'absolute', + right: 0, + top: '50%', + transform: 'translateY(-50%)', + }, + DateRangePickerInput_clearDates_default: { ':focus': { background: color.core.border, borderRadius: '50%', diff --git a/src/components/DateRangePickerInputController.jsx b/src/components/DateRangePickerInputController.jsx index 68e2deb0af..f8bef6f21f 100644 --- a/src/components/DateRangePickerInputController.jsx +++ b/src/components/DateRangePickerInputController.jsx @@ -42,6 +42,7 @@ const propTypes = forbidExtraProps({ readOnly: PropTypes.bool, openDirection: openDirectionShape, noBorder: PropTypes.bool, + block: PropTypes.bool, keepOpenOnDateSelect: PropTypes.bool, reopenPickerOnClearDates: PropTypes.bool, @@ -90,6 +91,7 @@ const defaultProps = { readOnly: false, openDirection: OPEN_DOWN, noBorder: false, + block: false, keepOpenOnDateSelect: false, reopenPickerOnClearDates: false, @@ -264,6 +266,7 @@ export default class DateRangePickerInputController extends React.Component { onKeyDownQuestionMark, isRTL, noBorder, + block, } = this.props; const startDateString = this.getDateString(startDate); @@ -304,6 +307,7 @@ export default class DateRangePickerInputController extends React.Component { onKeyDownQuestionMark={onKeyDownQuestionMark} isRTL={isRTL} noBorder={noBorder} + block={block} /> ); } diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index dc1e63cd5b..72219f3608 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -55,6 +55,7 @@ const defaultProps = { customInputIcon: null, customCloseIcon: null, noBorder: false, + block: false, // calendar presentation and interaction related props orientation: HORIZONTAL_ORIENTATION, @@ -454,6 +455,7 @@ class SingleDatePicker extends React.Component { screenReaderInputMessage, isRTL, noBorder, + block, styles, } = this.props; @@ -464,7 +466,12 @@ class SingleDatePicker extends React.Component { const onOutsideClick = (!withPortal && !withFullScreenPortal) ? this.onClearFocus : undefined; return ( -
+
{this.maybeRenderDayPickerWithPortal()} @@ -512,6 +520,10 @@ export default withStyles(({ reactDates: { color, spacing, zIndex } }) => ({ display: 'inline-block', }, + SingleDatePicker__block: { + display: 'block', + }, + SingleDatePicker_picker: { zIndex: zIndex + 1, backgroundColor: color.background, diff --git a/src/components/SingleDatePickerInput.jsx b/src/components/SingleDatePickerInput.jsx index 8ebd67415c..e6c992dec9 100644 --- a/src/components/SingleDatePickerInput.jsx +++ b/src/components/SingleDatePickerInput.jsx @@ -35,6 +35,7 @@ const propTypes = forbidExtraProps({ customInputIcon: PropTypes.node, isRTL: PropTypes.bool, noBorder: PropTypes.bool, + block: PropTypes.bool, onChange: PropTypes.func, onClearDate: PropTypes.func, @@ -66,6 +67,7 @@ const defaultProps = { customInputIcon: null, isRTL: false, noBorder: false, + block: false, onChange() {}, onClearDate() {}, @@ -106,6 +108,7 @@ function SingleDatePickerInput({ openDirection, isRTL, noBorder, + block, styles, }) { const calendarIcon = customInputIcon || ( @@ -135,6 +138,8 @@ function SingleDatePickerInput({ disabled && styles.SingleDatePickerInput__disabled, isRTL && styles.SingleDatePickerInput__rtl, !noBorder && styles.SingleDatePickerInput__withBorder, + block && styles.SingleDatePickerInput__block, + showClearDate && styles.SingleDatePickerInput__showClearDate, )} > {inputIconPosition === ICON_BEFORE_POSITION && inputIcon} @@ -163,6 +168,7 @@ function SingleDatePickerInput({
diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 64846a2784..e96675cb0c 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -71,6 +71,7 @@ const propTypes = forbidExtraProps({ renderCalendarInfo: PropTypes.func, firstDayOfWeek: DayOfWeekShape, verticalHeight: nonNegativeInteger, + transitionDuration: nonNegativeInteger, // accessibility onBlur: PropTypes.func, @@ -123,6 +124,7 @@ const defaultProps = { firstDayOfWeek: null, verticalHeight: null, noBorder: false, + transitionDuration: undefined, // accessibility onBlur() {}, @@ -907,6 +909,7 @@ export default class DayPickerRangeController extends React.Component { dayAriaLabelFormat, verticalHeight, noBorder, + transitionDuration, } = this.props; const { currentMonth, phrases, visibleDays } = this.state; @@ -947,6 +950,7 @@ export default class DayPickerRangeController extends React.Component { dayAriaLabelFormat={dayAriaLabelFormat} verticalHeight={verticalHeight} noBorder={noBorder} + transitionDuration={transitionDuration} /> ); } diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index b438b5f47b..08964a845e 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -55,6 +55,7 @@ const propTypes = forbidExtraProps({ daySize: nonNegativeInteger, verticalHeight: nonNegativeInteger, noBorder: PropTypes.bool, + transitionDuration: nonNegativeInteger, navPrev: PropTypes.node, navNext: PropTypes.node, @@ -104,6 +105,7 @@ const defaultProps = { daySize: DAY_SIZE, verticalHeight: null, noBorder: false, + transitionDuration: undefined, navPrev: null, navNext: null, @@ -593,6 +595,7 @@ export default class DayPickerSingleDateController extends React.Component { weekDayFormat, verticalHeight, noBorder, + transitionDuration, } = this.props; const { currentMonth, visibleDays } = this.state; @@ -630,6 +633,7 @@ export default class DayPickerSingleDateController extends React.Component { dayAriaLabelFormat={dayAriaLabelFormat} verticalHeight={verticalHeight} noBorder={noBorder} + transitionDuration={transitionDuration} /> ); diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index 72219f3608..717dfa721b 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -74,6 +74,7 @@ const defaultProps = { daySize: DAY_SIZE, isRTL: false, verticalHeight: null, + transitionDuration: undefined, // navigation related props navPrev: null, @@ -359,6 +360,7 @@ class SingleDatePicker extends React.Component { weekDayFormat, styles, verticalHeight, + transitionDuration, } = this.props; const { dayPickerContainerStyles, isDayPickerFocused, showKeyboardShortcuts } = this.state; @@ -416,6 +418,7 @@ class SingleDatePicker extends React.Component { firstDayOfWeek={firstDayOfWeek} weekDayFormat={weekDayFormat} verticalHeight={verticalHeight} + transitionDuration={transitionDuration} /> {withFullScreenPortal && ( diff --git a/src/shapes/DateRangePickerShape.js b/src/shapes/DateRangePickerShape.js index f30fb444d4..b252d025ad 100644 --- a/src/shapes/DateRangePickerShape.js +++ b/src/shapes/DateRangePickerShape.js @@ -59,6 +59,7 @@ export default { renderCalendarInfo: PropTypes.func, hideKeyboardShortcutsPanel: PropTypes.bool, verticalHeight: nonNegativeInteger, + transitionDuration: nonNegativeInteger, // navigation related props navPrev: PropTypes.node, diff --git a/src/shapes/SingleDatePickerShape.js b/src/shapes/SingleDatePickerShape.js index 6d9dafdb25..df758e981e 100644 --- a/src/shapes/SingleDatePickerShape.js +++ b/src/shapes/SingleDatePickerShape.js @@ -52,6 +52,7 @@ export default { daySize: nonNegativeInteger, isRTL: PropTypes.bool, verticalHeight: nonNegativeInteger, + transitionDuration: nonNegativeInteger, // navigation related props navPrev: PropTypes.node, diff --git a/stories/DateRangePicker_calendar.js b/stories/DateRangePicker_calendar.js index 841d033756..e731958c6c 100644 --- a/stories/DateRangePicker_calendar.js +++ b/stories/DateRangePicker_calendar.js @@ -171,4 +171,10 @@ storiesOf('DRP - Calendar Props', module) onClose={({ startDate, endDate }) => alert(`onClose: startDate = ${startDate}, endDate = ${endDate}`)} autoFocus /> + )) + .addWithInfo('with no animation', () => ( + )); diff --git a/stories/DayPicker.js b/stories/DayPicker.js index 8420f3b050..ab0d59c457 100644 --- a/stories/DayPicker.js +++ b/stories/DayPicker.js @@ -122,6 +122,11 @@ storiesOf('DayPicker', module) weekDayFormat="ddd" /> )) + .addWithInfo('with no animation', () => ( + + )) .addWithInfo('noBorder', () => ( )); diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index 3babfe7af9..74f06d860a 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -235,4 +235,12 @@ storiesOf('DayPickerRangeController', module) )} /> + )) + .addWithInfo('with no animation', () => ( + )); diff --git a/stories/DayPickerSingleDateController.js b/stories/DayPickerSingleDateController.js index ff6979cc26..acdca57dd0 100644 --- a/stories/DayPickerSingleDateController.js +++ b/stories/DayPickerSingleDateController.js @@ -215,4 +215,12 @@ storiesOf('DayPickerSingleDateController', module) )} /> + )) + .addWithInfo('with no animation', () => ( + )); diff --git a/stories/PresetDateRangePicker.js b/stories/PresetDateRangePicker.js index 7d3a9864fb..aa00a3adc0 100644 --- a/stories/PresetDateRangePicker.js +++ b/stories/PresetDateRangePicker.js @@ -38,7 +38,7 @@ const presets = [{ end: moment().add(1, 'month'), }]; -storiesOf('PresetDatePicker', module) +storiesOf('PresetDateRangePicker', module) .addDecorator(InfoPanelDecorator(presetDateRangePickerControllerInfo)) .addWithInfo('default', () => ( alert(`onClose: date = ${date}`)} autoFocus /> + )) + .addWithInfo('with no animation', () => ( + )); From dd5eb9ea2b5974d4cee4d954b7dcf5cc75532183 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Thu, 30 Nov 2017 15:29:39 -0800 Subject: [PATCH 027/618] Version 15.3.0 --- CHANGELOG.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43e6509f09..8f1d0667b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Change Log +## 15.3.0 +- [new] Add `transitionDuration` prop ([#865](https://github.com/airbnb/react-dates/pull/865)) +- [fix] Remove default prop values for required startDateId and endDateId props ([#866](https://github.com/airbnb/react-dates/pull/866)) +- [new] Add `block` styling prop ([#871](https://github.com/airbnb/react-dates/pull/871)) +- [new] Add `noBorder` prop to `DayPicker` variations ([#869](https://github.com/airbnb/react-dates/pull/869)) +- [new] Add `noBorder` prop to inputs ([#870](https://github.com/airbnb/react-dates/pull/870)) +- [fix] Remove unused width style in `KeyboardShortcutsRow` ([#867](https://github.com/airbnb/react-dates/pull/867)) + ## 15.2.1 - [fix] Republish `_datepicker.css` diff --git a/package.json b/package.json index 85af5863f8..2f00b7bc09 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "15.2.1", + "version": "15.3.0", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From 6c1abb609e9ec7b28658c98de16af3e5f6a0e029 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 3 Dec 2017 23:22:47 -0800 Subject: [PATCH 028/618] [Tests] Years only have 12 months, and these tests started failing in December --- test/components/DayPickerRangeController_spec.jsx | 2 +- test/components/DayPickerSingleDateController_spec.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index f64a1d4421..b96956ebc2 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -1934,7 +1934,7 @@ describe('DayPickerRangeController', () => { currentMonth: today, }); wrapper.instance().onNextMonthClick(); - expect(wrapper.state().currentMonth.month()).to.equal(today.month() + 1); + expect(wrapper.state().currentMonth.month()).to.equal(today.clone().add(1, 'month').month()); }); it('new visibleDays has next month', () => { diff --git a/test/components/DayPickerSingleDateController_spec.jsx b/test/components/DayPickerSingleDateController_spec.jsx index a6bb36b559..053ba19771 100644 --- a/test/components/DayPickerSingleDateController_spec.jsx +++ b/test/components/DayPickerSingleDateController_spec.jsx @@ -773,7 +773,7 @@ describe('DayPickerSingleDateController', () => { currentMonth: today, }); wrapper.instance().onNextMonthClick(); - expect(wrapper.state().currentMonth.month()).to.equal(today.month() + 1); + expect(wrapper.state().currentMonth.month()).to.equal(today.clone().add(1, 'month').month()); }); it('new visibleDays has next month', () => { From 8a558c9584e97fcf142dbc53d34376d4a694efc2 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Fri, 1 Dec 2017 16:42:33 -0800 Subject: [PATCH 029/618] Add verticalSpacing prop --- src/components/DateInput.jsx | 262 +++++++++--------- src/components/DateRangePicker.jsx | 38 ++- src/components/DateRangePickerInput.jsx | 7 +- .../DateRangePickerInputController.jsx | 4 + src/components/SingleDatePicker.jsx | 36 ++- src/components/SingleDatePickerInput.jsx | 6 +- src/constants.js | 4 + src/shapes/DateRangePickerShape.js | 1 + src/shapes/SingleDatePickerShape.js | 1 + src/theme/DefaultTheme.js | 2 - src/utils/getInputHeight.js | 9 + stories/DateRangePicker_calendar.js | 7 + stories/SingleDatePicker_calendar.js | 6 + test/utils/getInputHeight_spec.js | 13 + 14 files changed, 241 insertions(+), 155 deletions(-) create mode 100644 src/utils/getInputHeight.js create mode 100644 test/utils/getInputHeight_spec.js diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index 9cffa74b26..4d6cb1eb5e 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -1,12 +1,24 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { forbidExtraProps } from 'airbnb-prop-types'; +import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types'; import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; import throttle from 'lodash/throttle'; import isTouchDevice from 'is-touch-device'; +import getInputHeight from '../utils/getInputHeight'; import openDirectionShape from '../shapes/OpenDirectionShape'; -import { OPEN_DOWN, OPEN_UP } from '../constants'; +import { + OPEN_DOWN, + OPEN_UP, + FANG_HEIGHT_PX, + FANG_WIDTH_PX, + DEFAULT_VERTICAL_SPACING, +} from '../constants'; + +const FANG_PATH_TOP = `M0,${FANG_HEIGHT_PX} ${FANG_WIDTH_PX},${FANG_HEIGHT_PX} ${FANG_WIDTH_PX / 2},0z`; +const FANG_STROKE_TOP = `M0,${FANG_HEIGHT_PX} ${FANG_WIDTH_PX / 2},0 ${FANG_WIDTH_PX},${FANG_HEIGHT_PX}`; +const FANG_PATH_BOTTOM = `M0,0 ${FANG_WIDTH_PX},0 ${FANG_WIDTH_PX / 2},${FANG_HEIGHT_PX}z`; +const FANG_STROKE_BOTTOM = `M0,0 ${FANG_WIDTH_PX / 2},${FANG_HEIGHT_PX} ${FANG_WIDTH_PX},0`; const propTypes = forbidExtraProps({ ...withStylesPropTypes, @@ -20,6 +32,7 @@ const propTypes = forbidExtraProps({ readOnly: PropTypes.bool, openDirection: openDirectionShape, showCaret: PropTypes.bool, + verticalSpacing: nonNegativeInteger, onChange: PropTypes.func, onFocus: PropTypes.func, @@ -43,6 +56,7 @@ const defaultProps = { readOnly: null, openDirection: OPEN_DOWN, showCaret: false, + verticalSpacing: DEFAULT_VERTICAL_SPACING, onChange() {}, onFocus() {}, @@ -154,22 +168,30 @@ class DateInput extends React.Component { required, readOnly, openDirection, + verticalSpacing, styles, + theme: { reactDates }, } = this.props; const value = displayValue || dateString || ''; const screenReaderMessageId = `DateInput__screen-reader-message-${id}`; - const withCaret = showCaret && focused; + const withFang = showCaret && focused; + + const { + font: { input: { lineHeight } }, + spacing: { inputPadding, displayTextPaddingVertical }, + } = reactDates; + const inputHeight = getInputHeight({ lineHeight, inputPadding, displayTextPaddingVertical }); return (
+ {withFang && ( + + + + + )} + {screenReaderMessage && (

{screenReaderMessage} @@ -213,124 +260,85 @@ export default withStyles(({ reactDates: { border, color, sizing, spacing, font, zIndex, }, -}) => { - const inputHeight = parseInt(font.input.lineHeight, 10) - + (2 * spacing.inputPadding) - + (2 * spacing.displayTextPaddingVertical); - - return { - DateInput: { - fontWeight: 200, - fontSize: font.input.size, - lineHeight: font.input.lineHeight, - color: color.placeholderText, - margin: 0, - padding: spacing.inputPadding, - - background: color.background, - position: 'relative', - display: 'inline-block', - width: sizing.inputWidth, - verticalAlign: 'middle', - }, - - DateInput__withCaret: { - ':before': { - content: '""', - display: 'inline-block', - position: 'absolute', - bottom: 'auto', - border: `${sizing.tooltipArrowWidth / 2}px solid transparent`, - left: 22, - zIndex: zIndex + 2, - }, - - ':after': { - content: '""', - display: 'inline-block', - position: 'absolute', - bottom: 'auto', - border: `${sizing.tooltipArrowWidth / 2}px solid transparent`, - left: 22, - zIndex: zIndex + 2, - }, - }, - - DateInput__openUp: { - ':before': { - borderBottom: 0, - top: inputHeight - spacing.inputMarginBottom, - borderTopColor: 'rgba(0, 0, 0, 0.1)', - }, - - ':after': { - borderBottom: 0, - top: inputHeight - spacing.inputMarginBottom - 1, - borderTopColor: color.background, - }, - }, - - DateInput__openDown: { - ':before': { - borderTop: 0, - top: spacing.inputMarginBottom - (sizing.tooltipArrowWidth / 2), - borderBottomColor: 'rgba(0, 0, 0, 0.1)', - }, - - ':after': { - borderTop: 0, - top: spacing.inputMarginBottom - (sizing.tooltipArrowWidth / 2) + 1, - borderBottomColor: color.background, - }, - }, - - DateInput__disabled: { - background: color.disabled, - color: color.textDisabled, - }, - - DateInput_input: { - fontWeight: 200, - fontSize: font.input.size, - color: color.text, - backgroundColor: color.background, - width: '100%', - padding: `${spacing.displayTextPaddingVertical}px ${spacing.displayTextPaddingHorizontal}px`, - border: border.input.border, - borderTop: border.input.borderTop, - borderRight: border.input.borderRight, - borderBottom: border.input.borderBottom, - borderLeft: border.input.borderLeft, - }, - - DateInput_input__readOnly: { - userSelect: 'none', - }, - - DateInput_input__focused: { - outline: border.input.outlineFocused, - background: color.backgroundFocused, - border: border.input.borderFocused, - borderTop: border.input.borderTopFocused, - borderRight: border.input.borderRightFocused, - borderBottom: border.input.borderBottomFocused, - borderLeft: border.input.borderLeftFocused, - }, - - DateInput_input__disabled: { - background: color.disabled, - fontStyle: font.input.styleDisabled, - }, - - DateInput_screenReaderMessage: { - border: 0, - clip: 'rect(0, 0, 0, 0)', - height: 1, - margin: -1, - overflow: 'hidden', - padding: 0, - position: 'absolute', - width: 1, - }, - }; -})(DateInput); +}) => ({ + DateInput: { + fontWeight: 200, + fontSize: font.input.size, + lineHeight: font.input.lineHeight, + color: color.placeholderText, + margin: 0, + padding: spacing.inputPadding, + + background: color.background, + position: 'relative', + display: 'inline-block', + width: sizing.inputWidth, + verticalAlign: 'middle', + }, + + DateInput__disabled: { + background: color.disabled, + color: color.textDisabled, + }, + + DateInput_input: { + fontWeight: 200, + fontSize: font.input.size, + color: color.text, + backgroundColor: color.background, + width: '100%', + padding: `${spacing.displayTextPaddingVertical}px ${spacing.displayTextPaddingHorizontal}px`, + border: border.input.border, + borderTop: border.input.borderTop, + borderRight: border.input.borderRight, + borderBottom: border.input.borderBottom, + borderLeft: border.input.borderLeft, + }, + + DateInput_input__readOnly: { + userSelect: 'none', + }, + + DateInput_input__focused: { + outline: border.input.outlineFocused, + background: color.backgroundFocused, + border: border.input.borderFocused, + borderTop: border.input.borderTopFocused, + borderRight: border.input.borderRightFocused, + borderBottom: border.input.borderBottomFocused, + borderLeft: border.input.borderLeftFocused, + }, + + DateInput_input__disabled: { + background: color.disabled, + fontStyle: font.input.styleDisabled, + }, + + DateInput_screenReaderMessage: { + border: 0, + clip: 'rect(0, 0, 0, 0)', + height: 1, + margin: -1, + overflow: 'hidden', + padding: 0, + position: 'absolute', + width: 1, + }, + + DateInput_fang: { + position: 'absolute', + width: FANG_WIDTH_PX, + height: FANG_HEIGHT_PX, + left: 22, + zIndex: zIndex + 2, + }, + + DateInput_fangShape: { + fill: color.background, + }, + + DateInput_fangStroke: { + stroke: color.core.border, + fill: 'transparent', + }, +}))(DateInput); diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index a2b87155d1..c6948ae535 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -11,6 +11,7 @@ import { DateRangePickerPhrases } from '../defaultPhrases'; import OutsideClickHandler from './OutsideClickHandler'; import getResponsiveContainerStyles from '../utils/getResponsiveContainerStyles'; +import getInputHeight from '../utils/getInputHeight'; import isInclusivelyAfterDay from '../utils/isInclusivelyAfterDay'; @@ -32,6 +33,8 @@ import { OPEN_UP, DAY_SIZE, ICON_BEFORE_POSITION, + FANG_HEIGHT_PX, + DEFAULT_VERTICAL_SPACING, } from '../constants'; const propTypes = forbidExtraProps({ @@ -80,6 +83,7 @@ const defaultProps = { firstDayOfWeek: null, verticalHeight: null, transitionDuration: undefined, + verticalSpacing: DEFAULT_VERTICAL_SPACING, // navigation related props navPrev: null, @@ -324,6 +328,8 @@ class DateRangePicker extends React.Component { styles, verticalHeight, transitionDuration, + verticalSpacing, + theme: { reactDates }, } = this.props; const { dayPickerContainerStyles, isDayPickerFocused, showKeyboardShortcuts } = this.state; @@ -338,6 +344,12 @@ class DateRangePicker extends React.Component { ); + const { + font: { input: { lineHeight } }, + spacing: { inputPadding, displayTextPaddingVertical }, + } = reactDates; + const inputHeight = getInputHeight({ lineHeight, inputPadding, displayTextPaddingVertical }); + return (

{this.maybeRenderDayPickerWithPortal()} @@ -506,7 +526,7 @@ DateRangePicker.propTypes = propTypes; DateRangePicker.defaultProps = defaultProps; export { DateRangePicker as PureDateRangePicker }; -export default withStyles(({ reactDates: { color, zIndex, spacing } }) => ({ +export default withStyles(({ reactDates: { color, zIndex } }) => ({ DateRangePicker: { position: 'relative', display: 'inline-block', @@ -534,14 +554,6 @@ export default withStyles(({ reactDates: { color, zIndex, spacing } }) => ({ right: 0, }, - DateRangePicker_picker__openDown: { - top: spacing.inputMarginBottom, - }, - - DateRangePicker_picker__openUp: { - bottom: spacing.inputMarginBottom, - }, - DateRangePicker_picker__portal: { backgroundColor: 'rgba(0, 0, 0, 0.3)', position: 'fixed', diff --git a/src/components/DateRangePickerInput.jsx b/src/components/DateRangePickerInput.jsx index 330b01f17f..5c60707a2e 100644 --- a/src/components/DateRangePickerInput.jsx +++ b/src/components/DateRangePickerInput.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { forbidExtraProps } from 'airbnb-prop-types'; +import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types'; import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; import { DateRangePickerInputPhrases } from '../defaultPhrases'; @@ -60,6 +60,7 @@ const propTypes = forbidExtraProps({ customCloseIcon: PropTypes.node, noBorder: PropTypes.bool, block: PropTypes.bool, + verticalSpacing: nonNegativeInteger, // accessibility isFocused: PropTypes.bool, // describes actual DOM focus @@ -104,6 +105,7 @@ const defaultProps = { customCloseIcon: null, noBorder: false, block: false, + verticalSpacing: undefined, // accessibility isFocused: false, @@ -149,6 +151,7 @@ function DateRangePickerInput({ isRTL, noBorder, block, + verticalSpacing, styles, }) { const calendarIcon = customInputIcon || ( @@ -204,6 +207,7 @@ function DateRangePickerInput({ onKeyDownShiftTab={onStartDateShiftTab} onKeyDownArrowDown={onKeyDownArrowDown} onKeyDownQuestionMark={onKeyDownQuestionMark} + verticalSpacing={verticalSpacing} />
{showClearDates && ( diff --git a/src/components/DateRangePickerInputController.jsx b/src/components/DateRangePickerInputController.jsx index f8bef6f21f..95837d3af8 100644 --- a/src/components/DateRangePickerInputController.jsx +++ b/src/components/DateRangePickerInputController.jsx @@ -43,6 +43,7 @@ const propTypes = forbidExtraProps({ openDirection: openDirectionShape, noBorder: PropTypes.bool, block: PropTypes.bool, + verticalSpacing: nonNegativeInteger, keepOpenOnDateSelect: PropTypes.bool, reopenPickerOnClearDates: PropTypes.bool, @@ -92,6 +93,7 @@ const defaultProps = { openDirection: OPEN_DOWN, noBorder: false, block: false, + verticalSpacing: undefined, keepOpenOnDateSelect: false, reopenPickerOnClearDates: false, @@ -267,6 +269,7 @@ export default class DateRangePickerInputController extends React.Component { isRTL, noBorder, block, + verticalSpacing, } = this.props; const startDateString = this.getDateString(startDate); @@ -308,6 +311,7 @@ export default class DateRangePickerInputController extends React.Component { isRTL={isRTL} noBorder={noBorder} block={block} + verticalSpacing={verticalSpacing} /> ); } diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index 717dfa721b..c879d7c027 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -13,6 +13,7 @@ import OutsideClickHandler from './OutsideClickHandler'; import toMomentObject from '../utils/toMomentObject'; import toLocalizedDateString from '../utils/toLocalizedDateString'; import getResponsiveContainerStyles from '../utils/getResponsiveContainerStyles'; +import getInputHeight from '../utils/getInputHeight'; import SingleDatePickerInput from './SingleDatePickerInput'; import DayPickerSingleDateController from './DayPickerSingleDateController'; @@ -30,6 +31,8 @@ import { OPEN_UP, DAY_SIZE, ICON_BEFORE_POSITION, + FANG_HEIGHT_PX, + DEFAULT_VERTICAL_SPACING, } from '../constants'; const propTypes = forbidExtraProps({ @@ -56,6 +59,7 @@ const defaultProps = { customCloseIcon: null, noBorder: false, block: false, + verticalSpacing: DEFAULT_VERTICAL_SPACING, // calendar presentation and interaction related props orientation: HORIZONTAL_ORIENTATION, @@ -361,12 +365,20 @@ class SingleDatePicker extends React.Component { styles, verticalHeight, transitionDuration, + verticalSpacing, + theme: { reactDates }, } = this.props; const { dayPickerContainerStyles, isDayPickerFocused, showKeyboardShortcuts } = this.state; const onOutsideClick = (!withFullScreenPortal && withPortal) ? this.onClearFocus : undefined; const closeIcon = customCloseIcon || (); + const { + font: { input: { lineHeight } }, + spacing: { inputPadding, displayTextPaddingVertical }, + } = reactDates; + const inputHeight = getInputHeight({ lineHeight, inputPadding, displayTextPaddingVertical }); + return (
{this.maybeRenderDayPickerWithPortal()} @@ -517,7 +539,7 @@ SingleDatePicker.propTypes = propTypes; SingleDatePicker.defaultProps = defaultProps; export { SingleDatePicker as PureSingleDatePicker }; -export default withStyles(({ reactDates: { color, spacing, zIndex } }) => ({ +export default withStyles(({ reactDates: { color, zIndex } }) => ({ SingleDatePicker: { position: 'relative', display: 'inline-block', @@ -545,14 +567,6 @@ export default withStyles(({ reactDates: { color, spacing, zIndex } }) => ({ right: 0, }, - SingleDatePicker_picker__openDown: { - top: spacing.inputMarginBottom, - }, - - SingleDatePicker_picker__openUp: { - bottom: spacing.inputMarginBottom, - }, - SingleDatePicker_picker__portal: { backgroundColor: 'rgba(0, 0, 0, 0.3)', position: 'fixed', diff --git a/src/components/SingleDatePickerInput.jsx b/src/components/SingleDatePickerInput.jsx index e6c992dec9..b47f2e38d0 100644 --- a/src/components/SingleDatePickerInput.jsx +++ b/src/components/SingleDatePickerInput.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { forbidExtraProps } from 'airbnb-prop-types'; +import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types'; import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; import { SingleDatePickerInputPhrases } from '../defaultPhrases'; @@ -36,6 +36,7 @@ const propTypes = forbidExtraProps({ isRTL: PropTypes.bool, noBorder: PropTypes.bool, block: PropTypes.bool, + verticalSpacing: nonNegativeInteger, onChange: PropTypes.func, onClearDate: PropTypes.func, @@ -68,6 +69,7 @@ const defaultProps = { isRTL: false, noBorder: false, block: false, + verticalSpacing: undefined, onChange() {}, onClearDate() {}, @@ -109,6 +111,7 @@ function SingleDatePickerInput({ isRTL, noBorder, block, + verticalSpacing, styles, }) { const calendarIcon = customInputIcon || ( @@ -162,6 +165,7 @@ function SingleDatePickerInput({ onKeyDownArrowDown={onKeyDownArrowDown} onKeyDownQuestionMark={onKeyDownQuestionMark} openDirection={openDirection} + verticalSpacing={verticalSpacing} /> {showClearDate && ( diff --git a/src/constants.js b/src/constants.js index c41193d173..0e8bb5a4b2 100644 --- a/src/constants.js +++ b/src/constants.js @@ -21,3 +21,7 @@ export const OPEN_UP = 'up'; export const DAY_SIZE = 39; export const BLOCKED_MODIFIER = 'blocked'; export const WEEKDAYS = [0, 1, 2, 3, 4, 5, 6]; + +export const FANG_WIDTH_PX = 20; +export const FANG_HEIGHT_PX = 10; +export const DEFAULT_VERTICAL_SPACING = 22; diff --git a/src/shapes/DateRangePickerShape.js b/src/shapes/DateRangePickerShape.js index b252d025ad..e5e3d16111 100644 --- a/src/shapes/DateRangePickerShape.js +++ b/src/shapes/DateRangePickerShape.js @@ -60,6 +60,7 @@ export default { hideKeyboardShortcutsPanel: PropTypes.bool, verticalHeight: nonNegativeInteger, transitionDuration: nonNegativeInteger, + verticalSpacing: nonNegativeInteger, // navigation related props navPrev: PropTypes.node, diff --git a/src/shapes/SingleDatePickerShape.js b/src/shapes/SingleDatePickerShape.js index df758e981e..bed41ba4d3 100644 --- a/src/shapes/SingleDatePickerShape.js +++ b/src/shapes/SingleDatePickerShape.js @@ -33,6 +33,7 @@ export default { customInputIcon: PropTypes.node, noBorder: PropTypes.bool, block: PropTypes.bool, + verticalSpacing: nonNegativeInteger, // calendar presentation and interaction related props renderMonth: PropTypes.func, diff --git a/src/theme/DefaultTheme.js b/src/theme/DefaultTheme.js index 22e24cf0c8..f5feef8a5c 100644 --- a/src/theme/DefaultTheme.js +++ b/src/theme/DefaultTheme.js @@ -148,14 +148,12 @@ export default { captionPaddingTop: 22, captionPaddingBottom: 37, inputPadding: 0, - inputMarginBottom: 72, // spacing in between the input and the picker displayTextPaddingVertical: 12, displayTextPaddingHorizontal: 12, }, sizing: { inputWidth: 130, - tooltipArrowWidth: 20, arrowWidth: 24, }, diff --git a/src/utils/getInputHeight.js b/src/utils/getInputHeight.js new file mode 100644 index 0000000000..db6af95481 --- /dev/null +++ b/src/utils/getInputHeight.js @@ -0,0 +1,9 @@ +export default function getInputHeight({ + lineHeight, + inputPadding, + displayTextPaddingVertical, +}) { + return parseInt(lineHeight, 10) + + (2 * inputPadding) + + (2 * displayTextPaddingVertical); +} diff --git a/stories/DateRangePicker_calendar.js b/stories/DateRangePicker_calendar.js index e731958c6c..4c085c4c52 100644 --- a/stories/DateRangePicker_calendar.js +++ b/stories/DateRangePicker_calendar.js @@ -177,4 +177,11 @@ storiesOf('DRP - Calendar Props', module) transitionDuration={0} autoFocus /> + )) + .addWithInfo('with custom vertical spacing', () => ( + )); + diff --git a/stories/SingleDatePicker_calendar.js b/stories/SingleDatePicker_calendar.js index 4a1a49bf9c..af2e07c990 100644 --- a/stories/SingleDatePicker_calendar.js +++ b/stories/SingleDatePicker_calendar.js @@ -158,5 +158,11 @@ storiesOf('SDP - Calendar Props', module) transitionDuration={0} autoFocus /> + )) + .addWithInfo('with custom vertical spacing', () => ( + )); diff --git a/test/utils/getInputHeight_spec.js b/test/utils/getInputHeight_spec.js new file mode 100644 index 0000000000..ce220526d0 --- /dev/null +++ b/test/utils/getInputHeight_spec.js @@ -0,0 +1,13 @@ +import { expect } from 'chai'; +import getInputHeight from '../../src/utils/getInputHeight'; + +describe('#getInputHeight', () => { + it('returns the expected value', () => { + const inputHeight = getInputHeight({ + lineHeight: 7, + inputPadding: 13, + displayTextPaddingVertical: 91, + }); + expect(inputHeight).to.equal(215); + }); +}); From e2f494c87d01e4c98ed92b70166650d69a1d0ace Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Wed, 6 Dec 2017 11:21:58 -0800 Subject: [PATCH 030/618] Use theme font size in a meaningful way --- src/components/CalendarDay.jsx | 6 ++++-- src/components/DayPicker.jsx | 3 ++- src/components/DayPickerKeyboardShortcuts.jsx | 19 +++++++++---------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index 603707641e..85f1ccd927 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -178,7 +178,7 @@ CalendarDay.propTypes = propTypes; CalendarDay.defaultProps = defaultProps; export { CalendarDay as PureCalendarDay }; -export default withStyles(({ reactDates: { color } }) => ({ +export default withStyles(({ reactDates: { color, font } }) => ({ CalendarDay_container: { border: `1px solid ${color.core.borderLight}`, padding: 0, @@ -203,11 +203,13 @@ export default withStyles(({ reactDates: { color } }) => ({ margin: 0, padding: 0, color: 'inherit', - font: 'inherit', lineHeight: 'normal', overflow: 'visible', boxSizing: 'border-box', cursor: 'pointer', + fontFamily: 'inherit', + fontStyle: 'inherit', + fontSize: font.size, ':active': { outline: 0, diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index fbff031850..ae9e946d8e 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -842,7 +842,7 @@ DayPicker.propTypes = propTypes; DayPicker.defaultProps = defaultProps; export { DayPicker as PureDayPicker }; -export default withStyles(({ reactDates: { color, zIndex } }) => ({ +export default withStyles(({ reactDates: { color, font, zIndex } }) => ({ DayPicker: { background: color.background, position: 'relative', @@ -918,6 +918,7 @@ export default withStyles(({ reactDates: { color, zIndex } }) => ({ margin: '1px 0', paddingLeft: 0, paddingRight: 0, + fontSize: font.size, }, DayPicker_weekHeader_li: { diff --git a/src/components/DayPickerKeyboardShortcuts.jsx b/src/components/DayPickerKeyboardShortcuts.jsx index 3dd42247d3..eacb6d5640 100644 --- a/src/components/DayPickerKeyboardShortcuts.jsx +++ b/src/components/DayPickerKeyboardShortcuts.jsx @@ -214,17 +214,14 @@ class DayPickerKeyboardShortcuts extends React.Component { {showKeyboardShortcutsPanel &&
{phrases.keyboardShortcuts}
@@ -245,8 +242,8 @@ class DayPickerKeyboardShortcuts extends React.Component {
    {this.keyboardShortcuts.map(({ unicode, label, action }) => ( ({ +export default withStyles(({ reactDates: { color, font, zIndex } }) => ({ DayPickerKeyboardShortcuts_buttonReset: { background: 'none', border: 0, @@ -279,6 +276,7 @@ export default withStyles(({ reactDates: { color, zIndex } }) => ({ overflow: 'visible', padding: 0, cursor: 'pointer', + fontSize: font.size, ':active': { outline: 'none', @@ -368,6 +366,7 @@ export default withStyles(({ reactDates: { color, zIndex } }) => ({ DayPickerKeyboardShortcuts_list: { listStyle: 'none', padding: 0, + fontSize: font.size, }, DayPickerKeyboardShortcuts_close: { From f0eff4f99801a97fd4d9aedd52e9b88ffe85efe7 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Wed, 6 Dec 2017 16:43:40 -0800 Subject: [PATCH 031/618] Version 15.4.0 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f1d0667b8..c6b8417ed9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 15.4.0 +- [fix] Set font sizes according to theme variable ([#885](https://github.com/airbnb/react-dates/pull/885)) +- [new] Add `verticalSpacing` prop ([#883](https://github.com/airbnb/react-dates/pull/883)) + ## 15.3.0 - [new] Add `transitionDuration` prop ([#865](https://github.com/airbnb/react-dates/pull/865)) - [fix] Remove default prop values for required startDateId and endDateId props ([#866](https://github.com/airbnb/react-dates/pull/866)) diff --git a/package.json b/package.json index 2f00b7bc09..4f667a62eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "15.3.0", + "version": "15.4.0", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From 1159c62b5fd97cdb65393ad05ce01c3be20bc95a Mon Sep 17 00:00:00 2001 From: tajo Date: Wed, 6 Dec 2017 22:40:37 -0800 Subject: [PATCH 032/618] Bump react-portal to v4.1.0 --- package.json | 2 +- src/components/DateRangePicker.jsx | 4 ++-- src/components/SingleDatePicker.jsx | 4 ++-- test/components/DateRangePicker_spec.jsx | 24 +-------------------- test/components/SingleDatePicker_spec.jsx | 26 +---------------------- 5 files changed, 7 insertions(+), 53 deletions(-) diff --git a/package.json b/package.json index 4f667a62eb..b87990906d 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "prop-types": "^15.5.10", "react-addons-shallow-compare": "^15.5.2", "react-moment-proptypes": "^1.5.0", - "react-portal": "^3.1.0", + "react-portal": "^4.1.0", "react-with-styles": "^2.2.0", "react-with-styles-interface-css": "^3.0.0" }, diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index c6948ae535..9eeaec431b 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -2,7 +2,7 @@ import React from 'react'; import shallowCompare from 'react-addons-shallow-compare'; import moment from 'moment'; import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; -import Portal from 'react-portal'; +import { Portal } from 'react-portal'; import { forbidExtraProps } from 'airbnb-prop-types'; import { addEventListener } from 'consolidated-events'; import isTouchDevice from 'is-touch-device'; @@ -280,7 +280,7 @@ class DateRangePicker extends React.Component { if (withPortal || withFullScreenPortal) { return ( - + {this.renderDayPicker()} ); diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index c879d7c027..a4f5b5ff09 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -1,7 +1,7 @@ import React from 'react'; import moment from 'moment'; import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; -import Portal from 'react-portal'; +import { Portal } from 'react-portal'; import { forbidExtraProps } from 'airbnb-prop-types'; import { addEventListener } from 'consolidated-events'; import isTouchDevice from 'is-touch-device'; @@ -319,7 +319,7 @@ class SingleDatePicker extends React.Component { if (withPortal || withFullScreenPortal) { return ( - + {this.renderDayPicker()} ); diff --git a/test/components/DateRangePicker_spec.jsx b/test/components/DateRangePicker_spec.jsx index 1d3fdd0767..fcf7a77fa1 100644 --- a/test/components/DateRangePicker_spec.jsx +++ b/test/components/DateRangePicker_spec.jsx @@ -3,7 +3,7 @@ import moment from 'moment'; import { expect } from 'chai'; import sinon from 'sinon-sandbox'; import { shallow } from 'enzyme'; -import Portal from 'react-portal'; +import { Portal } from 'react-portal'; import DateRangePicker, { PureDateRangePicker } from '../../src/components/DateRangePicker'; @@ -76,17 +76,6 @@ describe('DateRangePicker', () => { )).dive(); expect(wrapper.find(Portal)).to.have.length(0); }); - - it('isOpened prop is true if props.focusedInput !== null', () => { - const wrapper = shallow(( - - )).dive(); - expect(wrapper.find(Portal).props().isOpened).to.equal(true); - }); }); }); @@ -114,17 +103,6 @@ describe('DateRangePicker', () => { )).dive(); expect(wrapper.find(Portal)).to.have.length(0); }); - - it('isOpened prop is true if props.focusedInput !== null', () => { - const wrapper = shallow(( - - )).dive(); - expect(wrapper.find(Portal).props().isOpened).to.equal(true); - }); }); }); diff --git a/test/components/SingleDatePicker_spec.jsx b/test/components/SingleDatePicker_spec.jsx index 53e544c623..4659234aad 100644 --- a/test/components/SingleDatePicker_spec.jsx +++ b/test/components/SingleDatePicker_spec.jsx @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { shallow } from 'enzyme'; import sinon from 'sinon-sandbox'; import moment from 'moment'; -import Portal from 'react-portal'; +import { Portal } from 'react-portal'; import CloseButton from '../../src/components/CloseButton'; import DayPickerSingleDateController from '../../src/components/DayPickerSingleDateController'; @@ -115,18 +115,6 @@ describe('SingleDatePicker', () => { )).dive(); expect(wrapper.find(Portal)).to.have.length(0); }); - - it('isOpened prop is true if props.focused is true', () => { - const wrapper = shallow(( - {}} - onFocusChange={() => {}} - withPortal - focused - /> - )).dive(); - expect(wrapper.find(Portal).props().isOpened).to.equal(true); - }); }); }); @@ -166,18 +154,6 @@ describe('SingleDatePicker', () => { )).dive(); expect(wrapper.find(Portal)).to.have.length(0); }); - - it('isOpened prop is true if props.focused is truthy', () => { - const wrapper = shallow(( - {}} - onFocusChange={() => {}} - withFullScreenPortal - focused - /> - )).dive(); - expect(wrapper.find(Portal).props().isOpened).to.equal(true); - }); }); }); }); From f65c1e3656a5023a516800d2c2a60beaab83d056 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Wed, 6 Dec 2017 17:23:37 -0800 Subject: [PATCH 033/618] Add small (input) variant of the DRP/SDP --- examples/DateRangePickerWrapper.jsx | 2 + examples/SingleDatePickerWrapper.jsx | 3 + src/components/DateInput.jsx | 35 ++++++--- src/components/DateRangePicker.jsx | 10 +-- src/components/DateRangePickerInput.jsx | 43 ++++++++++- .../DateRangePickerInputController.jsx | 4 ++ src/components/SingleDatePicker.jsx | 10 +-- src/components/SingleDatePickerInput.jsx | 19 ++++- src/shapes/DateRangePickerShape.js | 1 + src/shapes/SingleDatePickerShape.js | 1 + src/theme/DefaultTheme.js | 18 ++++- src/utils/getInputHeight.js | 72 +++++++++++++++++-- stories/DateRangePicker_input.js | 8 +++ stories/SingleDatePicker_input.js | 7 ++ test/utils/getInputHeight_spec.js | 33 +++++++-- 15 files changed, 226 insertions(+), 40 deletions(-) diff --git a/examples/DateRangePickerWrapper.jsx b/examples/DateRangePickerWrapper.jsx index 97588980cc..5315909510 100644 --- a/examples/DateRangePickerWrapper.jsx +++ b/examples/DateRangePickerWrapper.jsx @@ -47,6 +47,8 @@ const defaultProps = { customInputIcon: null, customArrowIcon: null, customCloseIcon: null, + block: false, + small: false, // calendar presentation and interaction related props renderMonth: null, diff --git a/examples/SingleDatePickerWrapper.jsx b/examples/SingleDatePickerWrapper.jsx index 8562a2b933..feb5d90c8e 100644 --- a/examples/SingleDatePickerWrapper.jsx +++ b/examples/SingleDatePickerWrapper.jsx @@ -38,6 +38,9 @@ const defaultProps = { showClearDate: false, showDefaultInputIcon: false, customInputIcon: null, + block: false, + small: false, + verticalSpacing: undefined, // calendar presentation and interaction related props renderMonth: null, diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index 4d6cb1eb5e..ee8a265d99 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -33,6 +33,7 @@ const propTypes = forbidExtraProps({ openDirection: openDirectionShape, showCaret: PropTypes.bool, verticalSpacing: nonNegativeInteger, + small: PropTypes.bool, onChange: PropTypes.func, onFocus: PropTypes.func, @@ -57,6 +58,7 @@ const defaultProps = { openDirection: OPEN_DOWN, showCaret: false, verticalSpacing: DEFAULT_VERTICAL_SPACING, + small: false, onChange() {}, onFocus() {}, @@ -169,6 +171,7 @@ class DateInput extends React.Component { readOnly, openDirection, verticalSpacing, + small, styles, theme: { reactDates }, } = this.props; @@ -178,16 +181,13 @@ class DateInput extends React.Component { const withFang = showCaret && focused; - const { - font: { input: { lineHeight } }, - spacing: { inputPadding, displayTextPaddingVertical }, - } = reactDates; - const inputHeight = getInputHeight({ lineHeight, inputPadding, displayTextPaddingVertical }); + const inputHeight = getInputHeight(reactDates, small); return (
    ({ DateInput: { - fontWeight: 200, - fontSize: font.input.size, - lineHeight: font.input.lineHeight, - color: color.placeholderText, margin: 0, padding: spacing.inputPadding, - background: color.background, position: 'relative', display: 'inline-block', @@ -276,6 +272,10 @@ export default withStyles(({ verticalAlign: 'middle', }, + DateInput__small: { + width: sizing.inputWidth_small, + }, + DateInput__disabled: { background: color.disabled, color: color.textDisabled, @@ -284,10 +284,15 @@ export default withStyles(({ DateInput_input: { fontWeight: 200, fontSize: font.input.size, + lineHeight: font.input.lineHeight, color: color.text, backgroundColor: color.background, width: '100%', padding: `${spacing.displayTextPaddingVertical}px ${spacing.displayTextPaddingHorizontal}px`, + paddingTop: spacing.displayTextPaddingTop, + paddingBottom: spacing.displayTextPaddingBottom, + paddingLeft: spacing.displayTextPaddingLeft, + paddingRight: spacing.displayTextPaddingRight, border: border.input.border, borderTop: border.input.borderTop, borderRight: border.input.borderRight, @@ -295,6 +300,16 @@ export default withStyles(({ borderLeft: border.input.borderLeft, }, + DateInput_input__small: { + fontSize: font.input.size_small, + lineHeight: font.input.lineHeight_small, + padding: `${spacing.displayTextPaddingVertical_small}px ${spacing.displayTextPaddingHorizontal_small}px`, + paddingTop: spacing.displayTextPaddingTop_small, + paddingBottom: spacing.displayTextPaddingBottom_small, + paddingLeft: spacing.displayTextPaddingLeft_small, + paddingRight: spacing.displayTextPaddingRight_small, + }, + DateInput_input__readOnly: { userSelect: 'none', }, diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 9eeaec431b..fc89cf6c99 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -63,6 +63,7 @@ const defaultProps = { customCloseIcon: null, noBorder: false, block: false, + small: false, // calendar presentation and interaction related props renderMonth: null, @@ -329,6 +330,7 @@ class DateRangePicker extends React.Component { verticalHeight, transitionDuration, verticalSpacing, + small, theme: { reactDates }, } = this.props; const { dayPickerContainerStyles, isDayPickerFocused, showKeyboardShortcuts } = this.state; @@ -344,11 +346,7 @@ class DateRangePicker extends React.Component { ); - const { - font: { input: { lineHeight } }, - spacing: { inputPadding, displayTextPaddingVertical }, - } = reactDates; - const inputHeight = getInputHeight({ lineHeight, inputPadding, displayTextPaddingVertical }); + const inputHeight = getInputHeight(reactDates, small); return (
    diff --git a/src/components/DateRangePickerInput.jsx b/src/components/DateRangePickerInput.jsx index 5c60707a2e..ebf041c4d5 100644 --- a/src/components/DateRangePickerInput.jsx +++ b/src/components/DateRangePickerInput.jsx @@ -60,6 +60,7 @@ const propTypes = forbidExtraProps({ customCloseIcon: PropTypes.node, noBorder: PropTypes.bool, block: PropTypes.bool, + small: PropTypes.bool, verticalSpacing: nonNegativeInteger, // accessibility @@ -105,6 +106,7 @@ const defaultProps = { customCloseIcon: null, noBorder: false, block: false, + small: false, verticalSpacing: undefined, // accessibility @@ -152,17 +154,36 @@ function DateRangePickerInput({ noBorder, block, verticalSpacing, + small, styles, }) { const calendarIcon = customInputIcon || ( ); const arrowIcon = customArrowIcon || (isRTL - ? - : + ? ( + + ) : ( + + ) ); const closeIcon = customCloseIcon || ( - + ); const screenReaderText = screenReaderMessage || phrases.keyboardNavigationInstructions; const inputIcon = (showDefaultInputIcon || customInputIcon !== null) && ( @@ -208,6 +229,7 @@ function DateRangePickerInput({ onKeyDownArrowDown={onKeyDownArrowDown} onKeyDownQuestionMark={onKeyDownQuestionMark} verticalSpacing={verticalSpacing} + small={small} />
    {showClearDates && ( @@ -244,6 +267,7 @@ function DateRangePickerInput({ aria-label={phrases.clearDates} {...css( styles.DateRangePickerInput_clearDates, + small && styles.DateRangePickerInput_clearDates__small, !customCloseIcon && styles.DateRangePickerInput_clearDates_default, !(startDate || endDate) && styles.DateRangePickerInput_clearDates__hide, )} @@ -301,6 +325,11 @@ export default withStyles(({ reactDates: { color, sizing } }) => ({ width: sizing.arrowWidth, }, + DateRangePickerInput_arrow_svg__small: { + height: sizing.arrowWidth_small, + width: sizing.arrowWidth_small, + }, + DateRangePickerInput_clearDates: { background: 'none', border: 0, @@ -318,6 +347,10 @@ export default withStyles(({ reactDates: { color, sizing } }) => ({ transform: 'translateY(-50%)', }, + DateRangePickerInput_clearDates__small: { + padding: 6, + }, + DateRangePickerInput_clearDates_default: { ':focus': { background: color.core.border, @@ -341,6 +374,10 @@ export default withStyles(({ reactDates: { color, sizing } }) => ({ verticalAlign: 'middle', }, + DateRangePickerInput_clearDates_svg__small: { + height: 9, + }, + DateRangePickerInput_calendarIcon: { background: 'none', border: 0, diff --git a/src/components/DateRangePickerInputController.jsx b/src/components/DateRangePickerInputController.jsx index 95837d3af8..aa5843214d 100644 --- a/src/components/DateRangePickerInputController.jsx +++ b/src/components/DateRangePickerInputController.jsx @@ -43,6 +43,7 @@ const propTypes = forbidExtraProps({ openDirection: openDirectionShape, noBorder: PropTypes.bool, block: PropTypes.bool, + small: PropTypes.bool, verticalSpacing: nonNegativeInteger, keepOpenOnDateSelect: PropTypes.bool, @@ -93,6 +94,7 @@ const defaultProps = { openDirection: OPEN_DOWN, noBorder: false, block: false, + small: false, verticalSpacing: undefined, keepOpenOnDateSelect: false, @@ -269,6 +271,7 @@ export default class DateRangePickerInputController extends React.Component { isRTL, noBorder, block, + small, verticalSpacing, } = this.props; @@ -311,6 +314,7 @@ export default class DateRangePickerInputController extends React.Component { isRTL={isRTL} noBorder={noBorder} block={block} + small={small} verticalSpacing={verticalSpacing} /> ); diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index a4f5b5ff09..8d4563aa10 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -59,6 +59,7 @@ const defaultProps = { customCloseIcon: null, noBorder: false, block: false, + small: false, verticalSpacing: DEFAULT_VERTICAL_SPACING, // calendar presentation and interaction related props @@ -366,6 +367,7 @@ class SingleDatePicker extends React.Component { verticalHeight, transitionDuration, verticalSpacing, + small, theme: { reactDates }, } = this.props; const { dayPickerContainerStyles, isDayPickerFocused, showKeyboardShortcuts } = this.state; @@ -373,11 +375,7 @@ class SingleDatePicker extends React.Component { const onOutsideClick = (!withFullScreenPortal && withPortal) ? this.onClearFocus : undefined; const closeIcon = customCloseIcon || (); - const { - font: { input: { lineHeight } }, - spacing: { inputPadding, displayTextPaddingVertical }, - } = reactDates; - const inputHeight = getInputHeight({ lineHeight, inputPadding, displayTextPaddingVertical }); + const inputHeight = getInputHeight(reactDates, small); return (
    diff --git a/src/components/SingleDatePickerInput.jsx b/src/components/SingleDatePickerInput.jsx index b47f2e38d0..af362629f5 100644 --- a/src/components/SingleDatePickerInput.jsx +++ b/src/components/SingleDatePickerInput.jsx @@ -36,6 +36,7 @@ const propTypes = forbidExtraProps({ isRTL: PropTypes.bool, noBorder: PropTypes.bool, block: PropTypes.bool, + small: PropTypes.bool, verticalSpacing: nonNegativeInteger, onChange: PropTypes.func, @@ -111,6 +112,7 @@ function SingleDatePickerInput({ isRTL, noBorder, block, + small, verticalSpacing, styles, }) { @@ -118,7 +120,12 @@ function SingleDatePickerInput({ ); const closeIcon = customCloseIcon || ( - + ); const screenReaderText = screenReaderMessage || phrases.keyboardNavigationInstructions; @@ -166,12 +173,14 @@ function SingleDatePickerInput({ onKeyDownQuestionMark={onKeyDownQuestionMark} openDirection={openDirection} verticalSpacing={verticalSpacing} + small={small} /> {showClearDate && ( + {renderDayContents ? renderDayContents(day, modifiers) : day.format('D')} ); } @@ -179,47 +175,33 @@ CalendarDay.defaultProps = defaultProps; export { CalendarDay as PureCalendarDay }; export default withStyles(({ reactDates: { color, font } }) => ({ - CalendarDay_container: { - border: `1px solid ${color.core.borderLight}`, - padding: 0, - boxSizing: 'border-box', - color: color.text, - background: color.background, - - ':hover': { - background: color.core.borderLight, - border: `1px double ${color.core.borderLight}`, - color: 'inherit', - }, - }, - - CalendarDay_button: { - position: 'relative', - height: '100%', - width: '100%', - textAlign: 'center', - background: 'none', - border: 0, - margin: 0, - padding: 0, - color: 'inherit', - lineHeight: 'normal', - overflow: 'visible', + CalendarDay: { boxSizing: 'border-box', cursor: 'pointer', - fontFamily: 'inherit', - fontStyle: 'inherit', fontSize: font.size, + textAlign: 'center', ':active': { outline: 0, }, }, - CalendarDay_button__default: { + CalendarDay__defaultCursor: { cursor: 'default', }, + CalendarDay__default: { + border: `1px solid ${color.core.borderLight}`, + color: color.text, + background: color.background, + + ':hover': { + background: color.core.borderLight, + border: `1px double ${color.core.borderLight}`, + color: 'inherit', + }, + }, + CalendarDay__outside: { border: 0, diff --git a/src/components/CalendarMonth.jsx b/src/components/CalendarMonth.jsx index 0ec3ab6e7c..dce3a944d9 100644 --- a/src/components/CalendarMonth.jsx +++ b/src/components/CalendarMonth.jsx @@ -11,6 +11,7 @@ import moment from 'moment'; import { CalendarDayPhrases } from '../defaultPhrases'; import getPhrasePropTypes from '../utils/getPhrasePropTypes'; +import CalendarWeek from './CalendarWeek'; import CalendarDay from './CalendarDay'; import calculateDimension from '../utils/calculateDimension'; @@ -40,7 +41,8 @@ const propTypes = forbidExtraProps({ onDayMouseEnter: PropTypes.func, onDayMouseLeave: PropTypes.func, renderMonth: PropTypes.func, - renderDay: PropTypes.func, + renderCalendarDay: PropTypes.func, + renderDayContents: PropTypes.func, firstDayOfWeek: DayOfWeekShape, setMonthHeight: PropTypes.func, @@ -64,7 +66,8 @@ const defaultProps = { onDayMouseEnter() {}, onDayMouseLeave() {}, renderMonth: null, - renderDay: null, + renderCalendarDay: props => (), + renderDayContents: null, firstDayOfWeek: null, setMonthHeight() {}, @@ -149,7 +152,8 @@ class CalendarMonth extends React.Component { onDayMouseEnter, onDayMouseLeave, renderMonth, - renderDay, + renderCalendarDay, + renderDayContents, daySize, focusedDate, isFocused, @@ -189,25 +193,23 @@ class CalendarMonth extends React.Component { > {weeks.map((week, i) => ( - - {week.map((day, dayOfWeek) => ( - - ))} - + + {week.map((day, dayOfWeek) => renderCalendarDay({ + key: dayOfWeek, + day, + daySize, + isOutsideDay: !day || day.month() !== month.month(), + tabIndex: isVisible && isSameDay(day, focusedDate) ? 0 : -1, + isFocused, + onDayMouseEnter, + onDayMouseLeave, + onDayClick, + renderDayContents, + phrases, + modifiers: modifiers[toISODateString(day)], + ariaLabelFormat: dayAriaLabelFormat, + }))} + ))} diff --git a/src/components/CalendarMonthGrid.jsx b/src/components/CalendarMonthGrid.jsx index 07796a6853..dfd5552abc 100644 --- a/src/components/CalendarMonthGrid.jsx +++ b/src/components/CalendarMonthGrid.jsx @@ -42,7 +42,8 @@ const propTypes = forbidExtraProps({ onDayMouseLeave: PropTypes.func, onMonthTransitionEnd: PropTypes.func, renderMonth: PropTypes.func, - renderDay: PropTypes.func, + renderCalendarDay: PropTypes.func, + renderDayContents: PropTypes.func, transformValue: PropTypes.string, daySize: nonNegativeInteger, focusedDate: momentPropTypes.momentObj, // indicates focusable day @@ -71,7 +72,8 @@ const defaultProps = { onDayMouseLeave() {}, onMonthTransitionEnd() {}, renderMonth: null, - renderDay: null, + renderCalendarDay: undefined, + renderDayContents: null, transformValue: 'none', daySize: DAY_SIZE, focusedDate: null, @@ -231,7 +233,8 @@ class CalendarMonthGrid extends React.Component { onDayMouseLeave, onDayClick, renderMonth, - renderDay, + renderCalendarDay, + renderDayContents, onMonthTransitionEnd, firstDayOfWeek, focusedDate, @@ -310,7 +313,8 @@ class CalendarMonthGrid extends React.Component { onDayMouseLeave={onDayMouseLeave} onDayClick={onDayClick} renderMonth={renderMonth} - renderDay={renderDay} + renderCalendarDay={renderCalendarDay} + renderDayContents={renderDayContents} firstDayOfWeek={firstDayOfWeek} daySize={daySize} focusedDate={isVisible ? focusedDate : null} diff --git a/src/components/CalendarWeek.jsx b/src/components/CalendarWeek.jsx new file mode 100644 index 0000000000..ef691ac45f --- /dev/null +++ b/src/components/CalendarWeek.jsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { forbidExtraProps, or, childrenOfType } from 'airbnb-prop-types'; + +import CalendarDay from './CalendarDay'; +import CustomizableCalendarDay from './CustomizableCalendarDay'; + +const propTypes = forbidExtraProps({ + children: or([childrenOfType(CalendarDay), childrenOfType(CustomizableCalendarDay)]).isRequired, +}); + +export default function CalendarWeek({ children }) { + return ( + + {children} + + ); +} + +CalendarWeek.propTypes = propTypes; diff --git a/src/components/CustomizableCalendarDay.jsx b/src/components/CustomizableCalendarDay.jsx new file mode 100644 index 0000000000..867347b66c --- /dev/null +++ b/src/components/CustomizableCalendarDay.jsx @@ -0,0 +1,466 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import shallowCompare from 'react-addons-shallow-compare'; +import momentPropTypes from 'react-moment-proptypes'; +import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types'; +import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import moment from 'moment'; + +import { CalendarDayPhrases } from '../defaultPhrases'; +import getPhrasePropTypes from '../utils/getPhrasePropTypes'; +import getPhrase from '../utils/getPhrase'; + +import { BLOCKED_MODIFIER, DAY_SIZE } from '../constants'; + +function getStyles(stylesObj, isHovered) { + if (!stylesObj) return null; + + const { hover } = stylesObj; + if (isHovered && hover) { + return hover; + } + + return stylesObj; +} + +const DayStyleShape = PropTypes.shape({ + background: PropTypes.string, + border: PropTypes.string, + color: PropTypes.string, + + hover: PropTypes.shape({ + background: PropTypes.string, + border: PropTypes.string, + color: PropTypes.string, + }), +}); + +const propTypes = forbidExtraProps({ + ...withStylesPropTypes, + day: momentPropTypes.momentObj, + daySize: nonNegativeInteger, + isOutsideDay: PropTypes.bool, + modifiers: PropTypes.instanceOf(Set), + isFocused: PropTypes.bool, + tabIndex: PropTypes.oneOf([0, -1]), + onDayClick: PropTypes.func, + onDayMouseEnter: PropTypes.func, + onDayMouseLeave: PropTypes.func, + renderDayContents: PropTypes.func, + ariaLabelFormat: PropTypes.string, + + // style overrides + defaultStyles: DayStyleShape, + outsideStyles: DayStyleShape, + todayStyles: DayStyleShape, + highlightedCalendarStyles: DayStyleShape, + blockedMinNightsStyles: DayStyleShape, + blockedCalendarStyles: DayStyleShape, + blockedOutOfRangeStyles: DayStyleShape, + hoveredSpanStyles: DayStyleShape, + selectedSpanStyles: DayStyleShape, + lastInRangeStyles: DayStyleShape, + selectedStartStyles: DayStyleShape, + selectedEndStyles: DayStyleShape, + selectedStyles: DayStyleShape, + afterHoveredStartStyles: DayStyleShape, + + // internationalization + phrases: PropTypes.shape(getPhrasePropTypes(CalendarDayPhrases)), +}); + +const defaultProps = { + day: moment(), + daySize: DAY_SIZE, + isOutsideDay: false, + modifiers: new Set(), + isFocused: false, + tabIndex: -1, + onDayClick() {}, + onDayMouseEnter() {}, + onDayMouseLeave() {}, + renderDayContents: null, + ariaLabelFormat: 'dddd, LL', + + // internationalization + phrases: CalendarDayPhrases, +}; + +class CustomizableCalendarDay extends React.Component { + constructor(...args) { + super(...args); + + this.state = { + isHovered: false, + }; + + this.setButtonRef = this.setButtonRef.bind(this); + } + + shouldComponentUpdate(nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + + componentDidUpdate(prevProps) { + const { isFocused, tabIndex } = this.props; + if (tabIndex === 0) { + if (isFocused || tabIndex !== prevProps.tabIndex) { + this.buttonRef.focus(); + } + } + } + + onDayClick(day, e) { + const { onDayClick } = this.props; + onDayClick(day, e); + } + + onDayMouseEnter(day, e) { + const { onDayMouseEnter } = this.props; + this.setState({ isHovered: true }); + onDayMouseEnter(day, e); + } + + onDayMouseLeave(day, e) { + const { onDayMouseLeave } = this.props; + this.setState({ isHovered: false }); + onDayMouseLeave(day, e); + } + + onKeyDown(day, e) { + const { + onDayClick, + } = this.props; + + const { key } = e; + if (key === 'Enter' || key === ' ') { + onDayClick(day, e); + } + } + + setButtonRef(ref) { + this.buttonRef = ref; + } + + render() { + const { + day, + ariaLabelFormat, + daySize, + isOutsideDay, + modifiers, + tabIndex, + renderDayContents, + styles, + phrases: { + chooseAvailableDate, + dateIsUnavailable, + }, + + defaultStyles: defaultStylesWithHover, + outsideStyles: outsideStylesWithHover, + todayStyles: todayStylesWithHover, + highlightedCalendarStyles: highlightedCalendarStylesWithHover, + blockedMinNightsStyles: blockedMinNightsStylesWithHover, + blockedCalendarStyles: blockedCalendarStylesWithHover, + blockedOutOfRangeStyles: blockedOutOfRangeStylesWithHover, + hoveredSpanStyles: hoveredSpanStylesWithHover, + selectedSpanStyles: selectedSpanStylesWithHover, + lastInRangeStyles: lastInRangeStylesWithHover, + selectedStartStyles: selectedStartStylesWithHover, + selectedEndStyles: selectedEndStylesWithHover, + selectedStyles: selectedStylesWithHover, + afterHoveredStartStyles: afterHoveredStartStylesWithHover, + } = this.props; + + const { isHovered } = this.state; + + if (!day) return ; + + const formattedDate = { date: day.format(ariaLabelFormat) }; + + const ariaLabel = modifiers.has(BLOCKED_MODIFIER) + ? getPhrase(dateIsUnavailable, formattedDate) + : getPhrase(chooseAvailableDate, formattedDate); + + const daySizeStyles = { + width: daySize, + height: daySize - 1, + }; + + const useDefaultCursor = ( + modifiers.has('blocked-minimum-nights') + || modifiers.has('blocked-calendar') + || modifiers.has('blocked-out-of-range') + ); + + const selected = ( + modifiers.has('selected') + || modifiers.has('selected-start') + || modifiers.has('selected-end') + ); + + const hoveredSpan = !selected && ( + modifiers.has('hovered-span') + || modifiers.has('after-hovered-start') + ); + + const isOutsideRange = modifiers.has('blocked-out-of-range'); + + const defaultStyles = getStyles(defaultStylesWithHover, isHovered); + const outsideStyles = getStyles(outsideStylesWithHover, isHovered); + const todayStyles = getStyles(todayStylesWithHover, isHovered); + const highlightedCalendarStyles = getStyles(highlightedCalendarStylesWithHover, isHovered); + const blockedMinNightsStyles = getStyles(blockedMinNightsStylesWithHover, isHovered); + const blockedCalendarStyles = getStyles(blockedCalendarStylesWithHover, isHovered); + const blockedOutOfRangeStyles = getStyles(blockedOutOfRangeStylesWithHover, isHovered); + const hoveredSpanStyles = getStyles(hoveredSpanStylesWithHover, isHovered); + const selectedSpanStyles = getStyles(selectedSpanStylesWithHover, isHovered); + const lastInRangeStyles = getStyles(lastInRangeStylesWithHover, isHovered); + const selectedStartStyles = getStyles(selectedStartStylesWithHover, isHovered); + const selectedEndStyles = getStyles(selectedEndStylesWithHover, isHovered); + const selectedStyles = getStyles(selectedStylesWithHover, isHovered); + const afterHoveredStartStyles = getStyles(afterHoveredStartStylesWithHover, isHovered); + + const hasCustomSelectedStyles = + defaultStyles || + (selected && selectedStyles) || + (modifiers.has('selected-start') && selectedStartStyles) || + (modifiers.has('selected-end') && selectedEndStyles); + const hasCustomHoveredStyles = + (modifiers.has('hovered-span') && hoveredSpanStyles) || + (modifiers.has('after-hovered-start') && afterHoveredStartStyles); + const hasCustomStyles = + (isOutsideDay && outsideStyles) || + (modifiers.has('today') && todayStyles) || + (modifiers.has('highlighted-calendar') && highlightedCalendarStyles) || + (modifiers.has('blocked-minimum-nights') && blockedMinNightsStyles) || + (modifiers.has('blocked-calendar') && blockedCalendarStyles) || + (modifiers.has('last-in-range') && lastInRangeStyles) || + (modifiers.has('selected-span') && selectedSpanStyles) || + (isOutsideRange && blockedOutOfRangeStyles) || + hasCustomSelectedStyles || + hasCustomHoveredStyles; + + return ( + { this.onDayMouseEnter(day, e); }} + onMouseLeave={(e) => { this.onDayMouseLeave(day, e); }} + onMouseUp={(e) => { e.currentTarget.blur(); }} + onClick={(e) => { this.onDayClick(day, e); }} + onKeyDown={(e) => { this.onKeyDown(day, e); }} + tabIndex={tabIndex} + > + {renderDayContents ? renderDayContents(day, modifiers) : day.format('D')} + + ); + } +} + +CustomizableCalendarDay.propTypes = propTypes; +CustomizableCalendarDay.defaultProps = defaultProps; + +export { CustomizableCalendarDay as PureCustomizableCalendarDay }; +export default withStyles(({ reactDates: { color, font } }) => ({ + CalendarDay: { + boxSizing: 'border-box', + cursor: 'pointer', + fontSize: font.size, + textAlign: 'center', + + ':active': { + outline: 0, + }, + }, + + CalendarDay__defaultCursor: { + cursor: 'default', + }, + + CalendarDay__default: { + border: `1px solid ${color.core.borderLight}`, + color: color.text, + background: color.background, + + ':hover': { + background: color.core.borderLight, + border: `1px double ${color.core.borderLight}`, + color: 'inherit', + }, + }, + + CalendarDay__outside: { + border: 0, + + background: color.outside.backgroundColor, + color: color.outside.color, + }, + + CalendarDay__blocked_minimum_nights: { + background: color.minimumNights.backgroundColor, + border: `1px solid ${color.minimumNights.borderColor}`, + color: color.minimumNights.color, + + ':hover': { + background: color.minimumNights.backgroundColor_hover, + color: color.minimumNights.color_active, + }, + + ':active': { + background: color.minimumNights.backgroundColor_active, + color: color.minimumNights.color_active, + }, + }, + + CalendarDay__highlighted_calendar: { + background: color.highlighted.backgroundColor, + color: color.highlighted.color, + + ':hover': { + background: color.highlighted.backgroundColor_hover, + color: color.highlighted.color_active, + }, + + ':active': { + background: color.highlighted.backgroundColor_active, + color: color.highlighted.color_active, + }, + }, + + CalendarDay__selected_span: { + background: color.selectedSpan.backgroundColor, + border: `1px solid ${color.selectedSpan.borderColor}`, + color: color.selectedSpan.color, + + ':hover': { + background: color.selectedSpan.backgroundColor_hover, + border: `1px solid ${color.selectedSpan.borderColor}`, + color: color.selectedSpan.color_active, + }, + + ':active': { + background: color.selectedSpan.backgroundColor_active, + border: `1px solid ${color.selectedSpan.borderColor}`, + color: color.selectedSpan.color_active, + }, + }, + + CalendarDay__last_in_range: { + borderRight: color.core.primary, + }, + + CalendarDay__selected: { + background: color.selected.backgroundColor, + border: `1px solid ${color.selected.borderColor}`, + color: color.selected.color, + + ':hover': { + background: color.selected.backgroundColor_hover, + border: `1px solid ${color.selected.borderColor}`, + color: color.selected.color_active, + }, + + ':active': { + background: color.selected.backgroundColor_active, + border: `1px solid ${color.selected.borderColor}`, + color: color.selected.color_active, + }, + }, + + CalendarDay__hovered_span: { + background: color.hoveredSpan.backgroundColor, + border: `1px solid ${color.hoveredSpan.borderColor}`, + color: color.hoveredSpan.color, + + ':hover': { + background: color.hoveredSpan.backgroundColor_hover, + border: `1px solid ${color.hoveredSpan.borderColor}`, + color: color.hoveredSpan.color_active, + }, + + ':active': { + background: color.hoveredSpan.backgroundColor_active, + border: `1px solid ${color.hoveredSpan.borderColor}`, + color: color.hoveredSpan.color_active, + }, + }, + + CalendarDay__blocked_calendar: { + background: color.blocked_calendar.backgroundColor, + border: `1px solid ${color.blocked_calendar.borderColor}`, + color: color.blocked_calendar.color, + + ':hover': { + background: color.blocked_calendar.backgroundColor_hover, + border: `1px solid ${color.blocked_calendar.borderColor}`, + color: color.blocked_calendar.color_active, + }, + + ':active': { + background: color.blocked_calendar.backgroundColor_active, + border: `1px solid ${color.blocked_calendar.borderColor}`, + color: color.blocked_calendar.color_active, + }, + }, + + CalendarDay__blocked_out_of_range: { + background: color.blocked_out_of_range.backgroundColor, + border: `1px solid ${color.blocked_out_of_range.borderColor}`, + color: color.blocked_out_of_range.color, + + ':hover': { + background: color.blocked_out_of_range.backgroundColor_hover, + border: `1px solid ${color.blocked_out_of_range.borderColor}`, + color: color.blocked_out_of_range.color_active, + }, + + ':active': { + background: color.blocked_out_of_range.backgroundColor_active, + border: `1px solid ${color.blocked_out_of_range.borderColor}`, + color: color.blocked_out_of_range.color_active, + }, + }, + + CalendarDay__selected_start: {}, + CalendarDay__selected_end: {}, + CalendarDay__today: {}, +}))(CustomizableCalendarDay); diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index fc89cf6c99..0b4484de24 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -96,7 +96,8 @@ const defaultProps = { onClose() {}, // day presentation and interaction related props - renderDay: null, + renderCalendarDay: undefined, + renderDayContents: null, minimumNights: 1, enableOutsideDays: false, isDayBlocked: () => false, @@ -316,7 +317,8 @@ class DateRangePicker extends React.Component { endDate, minimumNights, keepOpenOnDateSelect, - renderDay, + renderCalendarDay, + renderDayContents, renderCalendarInfo, firstDayOfWeek, initialVisibleMonth, @@ -395,7 +397,8 @@ class DateRangePicker extends React.Component { isDayHighlighted={isDayHighlighted} isDayBlocked={isDayBlocked} keepOpenOnDateSelect={keepOpenOnDateSelect} - renderDay={renderDay} + renderCalendarDay={renderCalendarDay} + renderDayContents={renderDayContents} renderCalendarInfo={renderCalendarInfo} isFocused={isDayPickerFocused} showKeyboardShortcuts={showKeyboardShortcuts} diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index ae9e946d8e..9b8414ec07 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -71,7 +71,8 @@ const propTypes = forbidExtraProps({ // day props modifiers: PropTypes.object, - renderDay: PropTypes.func, + renderCalendarDay: PropTypes.func, + renderDayContents: PropTypes.func, onDayClick: PropTypes.func, onDayMouseEnter: PropTypes.func, onDayMouseLeave: PropTypes.func, @@ -119,7 +120,8 @@ export const defaultProps = { // day props modifiers: {}, - renderDay: null, + renderCalendarDay: undefined, + renderDayContents: null, onDayClick() {}, onDayMouseEnter() {}, onDayMouseLeave() {}, @@ -680,7 +682,8 @@ class DayPicker extends React.Component { onDayMouseLeave, firstDayOfWeek, renderMonth, - renderDay, + renderCalendarDay, + renderDayContents, renderCalendarInfo, hideKeyboardShortcutsPanel, onOutsideClick, @@ -804,7 +807,8 @@ class DayPicker extends React.Component { onDayMouseEnter={onDayMouseEnter} onDayMouseLeave={onDayMouseLeave} renderMonth={renderMonth} - renderDay={renderDay} + renderCalendarDay={renderCalendarDay} + renderDayContents={renderDayContents} onMonthTransitionEnd={this.updateStateAfterMonthTransition} monthFormat={monthFormat} daySize={daySize} diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index e96675cb0c..0a03216a1c 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -67,7 +67,8 @@ const propTypes = forbidExtraProps({ onPrevMonthClick: PropTypes.func, onNextMonthClick: PropTypes.func, onOutsideClick: PropTypes.func, - renderDay: PropTypes.func, + renderCalendarDay: PropTypes.func, + renderDayContents: PropTypes.func, renderCalendarInfo: PropTypes.func, firstDayOfWeek: DayOfWeekShape, verticalHeight: nonNegativeInteger, @@ -119,7 +120,8 @@ const defaultProps = { onNextMonthClick() {}, onOutsideClick() {}, - renderDay: null, + renderCalendarDay: undefined, + renderDayContents: null, renderCalendarInfo: null, firstDayOfWeek: null, verticalHeight: null, @@ -899,7 +901,8 @@ export default class DayPickerRangeController extends React.Component { hideKeyboardShortcutsPanel, daySize, focusedInput, - renderDay, + renderCalendarDay, + renderDayContents, renderCalendarInfo, onBlur, isFocused, @@ -936,7 +939,8 @@ export default class DayPickerRangeController extends React.Component { onOutsideClick={onOutsideClick} navPrev={navPrev} navNext={navNext} - renderDay={renderDay} + renderCalendarDay={renderCalendarDay} + renderDayContents={renderDayContents} renderCalendarInfo={renderCalendarInfo} firstDayOfWeek={firstDayOfWeek} hideKeyboardShortcutsPanel={hideKeyboardShortcutsPanel} diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 08964a845e..0773f89f15 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -63,7 +63,8 @@ const propTypes = forbidExtraProps({ onPrevMonthClick: PropTypes.func, onNextMonthClick: PropTypes.func, onOutsideClick: PropTypes.func, - renderDay: PropTypes.func, + renderCalendarDay: PropTypes.func, + renderDayContents: PropTypes.func, renderCalendarInfo: PropTypes.func, // accessibility @@ -114,7 +115,8 @@ const defaultProps = { onNextMonthClick() {}, onOutsideClick: null, - renderDay: null, + renderCalendarDay: undefined, + renderDayContents: null, renderCalendarInfo: null, // accessibility @@ -583,7 +585,8 @@ export default class DayPickerSingleDateController extends React.Component { hideKeyboardShortcutsPanel, daySize, firstDayOfWeek, - renderDay, + renderCalendarDay, + renderDayContents, renderCalendarInfo, isFocused, isRTL, @@ -620,7 +623,8 @@ export default class DayPickerSingleDateController extends React.Component { navPrev={navPrev} navNext={navNext} renderMonth={renderMonth} - renderDay={renderDay} + renderCalendarDay={renderCalendarDay} + renderDayContents={renderDayContents} renderCalendarInfo={renderCalendarInfo} isFocused={isFocused} getFirstFocusableDay={this.getFirstFocusableDay} diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index 8d4563aa10..58b79446b1 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -93,7 +93,8 @@ const defaultProps = { renderMonth: null, // day presentation and interaction related props - renderDay: null, + renderCalendarDay: undefined, + renderDayContents: null, enableOutsideDays: false, isDayBlocked: () => false, isOutsideRange: day => !isInclusivelyAfterDay(day, moment()), @@ -351,7 +352,8 @@ class SingleDatePicker extends React.Component { keepOpenOnDateSelect, initialVisibleMonth, renderMonth, - renderDay, + renderCalendarDay, + renderDayContents, renderCalendarInfo, hideKeyboardShortcutsPanel, firstDayOfWeek, @@ -420,7 +422,8 @@ class SingleDatePicker extends React.Component { onNextMonthClick={onNextMonthClick} onClose={onClose} renderMonth={renderMonth} - renderDay={renderDay} + renderCalendarDay={renderCalendarDay} + renderDayContents={renderDayContents} renderCalendarInfo={renderCalendarInfo} isFocused={isDayPickerFocused} showKeyboardShortcuts={showKeyboardShortcuts} diff --git a/src/shapes/DateRangePickerShape.js b/src/shapes/DateRangePickerShape.js index 2259633096..d1f0d9b2a7 100644 --- a/src/shapes/DateRangePickerShape.js +++ b/src/shapes/DateRangePickerShape.js @@ -70,7 +70,8 @@ export default { onNextMonthClick: PropTypes.func, // day presentation and interaction related props - renderDay: PropTypes.func, + renderCalendarDay: PropTypes.func, + renderDayContents: PropTypes.func, minimumNights: PropTypes.number, enableOutsideDays: PropTypes.bool, isDayBlocked: PropTypes.func, diff --git a/src/shapes/SingleDatePickerShape.js b/src/shapes/SingleDatePickerShape.js index 3628b05308..b464549d76 100644 --- a/src/shapes/SingleDatePickerShape.js +++ b/src/shapes/SingleDatePickerShape.js @@ -65,7 +65,8 @@ export default { onClose: PropTypes.func, // day presentation and interaction related props - renderDay: PropTypes.func, + renderCalendarDay: PropTypes.func, + renderDayContents: PropTypes.func, enableOutsideDays: PropTypes.bool, isDayBlocked: PropTypes.func, isOutsideRange: PropTypes.func, diff --git a/stories/DateRangePicker.js b/stories/DateRangePicker.js index e7df729fd2..f24d00d5bf 100644 --- a/stories/DateRangePicker.js +++ b/stories/DateRangePicker.js @@ -87,7 +87,7 @@ storiesOf('DateRangePicker (DRP)', module) return ( momentJalaali(month).format('jMMMM jYYYY')} - renderDay={day => momentJalaali(day).format('jD')} + renderDayContents={day => momentJalaali(day).format('jD')} /> ); }) diff --git a/stories/DateRangePicker_day.js b/stories/DateRangePicker_day.js index 8ab87322b1..abf69d78b1 100644 --- a/stories/DateRangePicker_day.js +++ b/stories/DateRangePicker_day.js @@ -5,6 +5,8 @@ import { storiesOf } from '@storybook/react'; import isSameDay from '../src/utils/isSameDay'; import isInclusivelyAfterDay from '../src/utils/isInclusivelyAfterDay'; +import CustomizableCalendarDay from '../src/components/CustomizableCalendarDay'; + import DateRangePickerWrapper from '../examples/DateRangePickerWrapper'; const datesList = [ @@ -18,6 +20,43 @@ const datesList = [ moment().add(13, 'days'), ]; +const selectedStyles = { + background: '#590098', + border: '1px solid #590098', + color: '#fff', + + hover: { + background: '#7A32AC', + border: '1px solid #7A32AC', + color: '#fff', + }, +}; + +const hoveredStyles = { + background: '#cd99d0', + border: '1px solid #cd99d0', + color: '#fff', +}; + +const customDayStyles = { + selectedStartStyles: selectedStyles, + selectedEndStyles: selectedStyles, + hoveredSpanStyles: hoveredStyles, + afterHoveredStartStyles: hoveredStyles, + + selectedSpanStyles: { + background: '#9b32a2', + border: '1px solid #9b32a2', + color: '#fff', + + hover: { + background: '#83008b', + border: '1px solid #83008b', + color: '#fff', + }, + }, +}; + storiesOf('DRP - Day Props', module) .addWithInfo('default', () => ( @@ -71,7 +110,13 @@ storiesOf('DRP - Day Props', module) )) .addWithInfo('with custom daily details', () => ( day.format('ddd')} + renderDayContents={day => {day.format('ddd')}} + autoFocus + /> + )) + .addWithInfo('one-off custom styling', () => ( + } autoFocus /> )); diff --git a/stories/DayPicker.js b/stories/DayPicker.js index ab0d59c457..043c3952db 100644 --- a/stories/DayPicker.js +++ b/stories/DayPicker.js @@ -99,7 +99,7 @@ storiesOf('DayPicker', module) )) .addWithInfo('with custom details', () => ( (day.day() % 6 === 5 ? '😻' : day.format('D'))} + renderDayContents={day => (day.day() % 6 === 5 ? '😻' : day.format('D'))} /> )) .addWithInfo('vertical with fixed-width container', () => ( diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index 74f06d860a..ca1c7fb446 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -223,7 +223,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - renderDay={day => day.format('ddd')} + renderDayContents={day => day.format('ddd')} /> )) .addWithInfo('with info panel', () => ( diff --git a/stories/DayPickerSingleDateController.js b/stories/DayPickerSingleDateController.js index acdca57dd0..20abf7463c 100644 --- a/stories/DayPickerSingleDateController.js +++ b/stories/DayPickerSingleDateController.js @@ -203,7 +203,7 @@ storiesOf('DayPickerSingleDateController', module) onOutsideClick={action('DayPickerSingleDateController::onOutsideClick')} onPrevMonthClick={action('DayPickerSingleDateController::onPrevMonthClick')} onNextMonthClick={action('DayPickerSingleDateController::onNextMonthClick')} - renderDay={day => day.format('ddd')} + renderDayContents={day => day.format('ddd')} /> )) .addWithInfo('with info panel', () => ( diff --git a/stories/SingleDatePicker.js b/stories/SingleDatePicker.js index d5bc16f28e..621c8bfc18 100644 --- a/stories/SingleDatePicker.js +++ b/stories/SingleDatePicker.js @@ -55,7 +55,7 @@ storiesOf('SingleDatePicker (SDP)', module) momentJalaali(month).format('jMMMM jYYYY')} - renderDay={day => momentJalaali(day).format('jD')} + renderDayContents={day => momentJalaali(day).format('jD')} /> ); }) diff --git a/stories/SingleDatePicker_day.js b/stories/SingleDatePicker_day.js index beba330472..36689140de 100644 --- a/stories/SingleDatePicker_day.js +++ b/stories/SingleDatePicker_day.js @@ -58,7 +58,7 @@ storiesOf('SDP - Day Props', module) .addWithInfo('with custom daily details', () => ( day.format('ddd')} + renderDayContents={day => day.format('ddd')} autoFocus /> )); diff --git a/stories/withStyles.js b/stories/withStyles.js index 17e05259c5..067cb27015 100644 --- a/stories/withStyles.js +++ b/stories/withStyles.js @@ -2,6 +2,7 @@ import React from 'react'; import { action, storiesOf } from '@storybook/react'; import CalendarDay from '../src/components/CalendarDay'; +import CustomizableCalendarDay from '../src/components/CustomizableCalendarDay'; import CalendarMonth from '../src/components/CalendarMonth'; import CalendarMonthGrid from '../src/components/CalendarMonthGrid'; import DayPickerNavigation from '../src/components/DayPickerNavigation'; @@ -13,23 +14,53 @@ import DateRangePickerInput from '../src/components/DateRangePickerInput'; import { VERTICAL_ORIENTATION, VERTICAL_SCROLLABLE } from '../constants'; +const customStyles = { + background: '#5f4b8b', + border: '2px solid #5f4b8b', + color: '#fff', + + hover: { + background: '#c3b5e3', + border: '2px solid #c3b5e3', + color: '#402b70', + fontWeight: 'bold', + }, +}; + storiesOf('withStyles', module) .addWithInfo('CalendarDay', () => ( - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    )) .addWithInfo('CalendarMonth', () => ( diff --git a/test/components/CalendarDay_spec.jsx b/test/components/CalendarDay_spec.jsx index d9171c2198..3480e66117 100644 --- a/test/components/CalendarDay_spec.jsx +++ b/test/components/CalendarDay_spec.jsx @@ -24,81 +24,80 @@ describe('CalendarDay', () => { it('contains arbitrary content if renderDay is provided', () => { const dayName = moment().format('dddd'); const renderDay = day => day.format('dddd'); - const wrapper = shallow().dive(); + const wrapper = shallow().dive(); expect(wrapper.text()).to.equal(dayName); }); - it('passes modifiers to renderDay', () => { + it('passes modifiers to renderDayContents', () => { const modifiers = new Set().add(BLOCKED_MODIFIER); - const renderDay = (day, mods) => `${day.format('dddd')}${mods.has(BLOCKED_MODIFIER) ? 'BLOCKED' : ''}`; + const renderDayContents = (day, mods) => `${day.format('dddd')}${mods.has(BLOCKED_MODIFIER) ? 'BLOCKED' : ''}`; const expected = `${moment().format('dddd')}BLOCKED`; - const wrapper = shallow().dive(); + const wrapper = + shallow().dive(); expect(wrapper.text()).to.equal(expected); }); - describe('button', () => { - it('contains a button', () => { - const wrapper = shallow().dive(); - expect(wrapper.find('button')).to.have.lengthOf(1); + it('has button role', () => { + const wrapper = shallow().dive(); + expect(wrapper.props().role).to.equal('button'); + }); + + it('has tabIndex equal to props.tabIndex', () => { + const tabIndex = -1; + const wrapper = shallow().dive(); + expect(wrapper.props().tabIndex).to.equal(tabIndex); + }); + + describe('aria-label', () => { + const phrases = {}; + const day = moment('10/10/2017'); + const expectedFormattedDay = { date: 'Tuesday, October 10, 2017' }; + + beforeEach(() => { + phrases.chooseAvailableDate = sinon.stub().returns('chooseAvailableDate text'); + phrases.dateIsUnavailable = sinon.stub().returns('dateIsUnavailable text'); }); - it('has tabIndex equal to props.tabIndex', () => { - const tabIndex = -1; - const wrapper = shallow().dive(); - expect(wrapper.find('button').props().tabIndex).to.equal(tabIndex); + afterEach(() => { + sinon.restore(); }); - describe('aria-label', () => { - const phrases = {}; - const day = moment('10/10/2017'); - const expectedFormattedDay = { date: 'Tuesday, October 10, 2017' }; - - beforeEach(() => { - phrases.chooseAvailableDate = sinon.stub().returns('chooseAvailableDate text'); - phrases.dateIsUnavailable = sinon.stub().returns('dateIsUnavailable text'); - }); - - afterEach(() => { - sinon.restore(); - }); - - it('is formatted with the chooseAvailableDate phrase function when day is available', () => { - const modifiers = new Set(); - - const wrapper = shallow().dive(); - - expect(phrases.chooseAvailableDate.calledWith(expectedFormattedDay)).to.equal(true); - expect(wrapper.find('button').prop('aria-label')).to.equal('chooseAvailableDate text'); - }); - - it('is formatted with the dateIsUnavailable phrase function when day is not available', () => { - const modifiers = new Set().add(BLOCKED_MODIFIER); - - const wrapper = shallow().dive(); - - expect(phrases.dateIsUnavailable.calledWith(expectedFormattedDay)).to.equal(true); - expect(wrapper.find('button').prop('aria-label')).to.equal('dateIsUnavailable text'); - }); - - it('should set aria-label with a value pass through ariaLabelFormat prop if it exists', () => { - const modifiers = new Set(); - - const wrapper = shallow().dive(); - - expect(wrapper.find('button').prop('aria-label')).to.equal('October 10th 2017'); - }); + it('is formatted with the chooseAvailableDate phrase function when day is available', () => { + const modifiers = new Set(); + + const wrapper = shallow().dive(); + + expect(phrases.chooseAvailableDate.calledWith(expectedFormattedDay)).to.equal(true); + expect(wrapper.prop('aria-label')).to.equal('chooseAvailableDate text'); + }); + + it('is formatted with the dateIsUnavailable phrase function when day is not available', () => { + const modifiers = new Set().add(BLOCKED_MODIFIER); + + const wrapper = shallow().dive(); + + expect(phrases.dateIsUnavailable.calledWith(expectedFormattedDay)).to.equal(true); + expect(wrapper.prop('aria-label')).to.equal('dateIsUnavailable text'); + }); + + it('should set aria-label with a value pass through ariaLabelFormat prop if it exists', () => { + const modifiers = new Set(); + + const wrapper = shallow().dive(); + + expect(wrapper.prop('aria-label')).to.equal('October 10th 2017'); }); }); }); @@ -114,7 +113,7 @@ describe('CalendarDay', () => { }); it('gets triggered by click', () => { - const wrapper = shallow().dive().find('button'); + const wrapper = shallow().dive(); wrapper.simulate('click'); expect(onDayClickSpy).to.have.property('callCount', 1); }); @@ -138,7 +137,7 @@ describe('CalendarDay', () => { }); it('gets triggered by mouseenter', () => { - const wrapper = shallow().dive().find('button'); + const wrapper = shallow().dive(); wrapper.simulate('mouseenter'); expect(onDayMouseEnterSpy).to.have.property('callCount', 1); }); @@ -162,7 +161,7 @@ describe('CalendarDay', () => { }); it('gets triggered by mouseleave', () => { - const wrapper = shallow().dive().find('button'); + const wrapper = shallow().dive(); wrapper.simulate('mouseleave'); expect(onDayMouseLeaveSpy).to.have.property('callCount', 1); }); diff --git a/test/components/CalendarWeek_spec.jsx b/test/components/CalendarWeek_spec.jsx new file mode 100644 index 0000000000..91117cbf6d --- /dev/null +++ b/test/components/CalendarWeek_spec.jsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { expect } from 'chai'; +import { shallow } from 'enzyme'; + +import CalendarWeek from '../../src/components/CalendarWeek'; + +import CalendarDay from '../../src/components/CalendarDay'; + +describe('CalendarWeek', () => { + it('renders a tr', () => { + const wrapper = shallow(( + + + + )); + expect(wrapper.is('tr')).to.equal(true); + }); +}); diff --git a/test/components/CustomizableCalendarDay_spec.jsx b/test/components/CustomizableCalendarDay_spec.jsx new file mode 100644 index 0000000000..d3bb0cbf67 --- /dev/null +++ b/test/components/CustomizableCalendarDay_spec.jsx @@ -0,0 +1,196 @@ +import React from 'react'; +import { expect } from 'chai'; +import sinon from 'sinon-sandbox'; +import { shallow } from 'enzyme'; +import moment from 'moment'; + +import { BLOCKED_MODIFIER } from '../../src/constants'; +import CustomizableCalendarDay, { PureCustomizableCalendarDay } from '../../src/components/CustomizableCalendarDay'; + +describe('CustomizableCalendarDay', () => { + describe('#render', () => { + it('contains formatted day for single digit days', () => { + const firstOfMonth = moment().startOf('month'); + const wrapper = shallow().dive(); + expect(wrapper.text()).to.equal(firstOfMonth.format('D')); + }); + + it('contains formatted day for double digit days', () => { + const lastOfMonth = moment().endOf('month'); + const wrapper = shallow().dive(); + expect(wrapper.text()).to.equal(lastOfMonth.format('D')); + }); + + it('contains arbitrary content if renderDay is provided', () => { + const dayName = moment().format('dddd'); + const renderDay = day => day.format('dddd'); + const wrapper = shallow().dive(); + expect(wrapper.text()).to.equal(dayName); + }); + + it('passes modifiers to renderDay', () => { + const modifiers = new Set().add(BLOCKED_MODIFIER); + const renderDay = (day, mods) => `${day.format('dddd')}${mods.has(BLOCKED_MODIFIER) ? 'BLOCKED' : ''}`; + const expected = `${moment().format('dddd')}BLOCKED`; + const wrapper = shallow().dive(); + expect(wrapper.text()).to.equal(expected); + }); + + it('has button role', () => { + const wrapper = shallow().dive(); + expect(wrapper.props().role).to.equal('button'); + }); + + it('has tabIndex equal to props.tabIndex', () => { + const tabIndex = -1; + const wrapper = shallow().dive(); + expect(wrapper.props().tabIndex).to.equal(tabIndex); + }); + + describe('aria-label', () => { + const phrases = {}; + const day = moment('10/10/2017'); + const expectedFormattedDay = { date: 'Tuesday, October 10, 2017' }; + + beforeEach(() => { + phrases.chooseAvailableDate = sinon.stub().returns('chooseAvailableDate text'); + phrases.dateIsUnavailable = sinon.stub().returns('dateIsUnavailable text'); + }); + + afterEach(() => { + sinon.restore(); + }); + + it('is formatted with the chooseAvailableDate phrase function when day is available', () => { + const modifiers = new Set(); + + const wrapper = shallow().dive(); + + expect(phrases.chooseAvailableDate.calledWith(expectedFormattedDay)).to.equal(true); + expect(wrapper.prop('aria-label')).to.equal('chooseAvailableDate text'); + }); + + it('is formatted with the dateIsUnavailable phrase function when day is not available', () => { + const modifiers = new Set().add(BLOCKED_MODIFIER); + + const wrapper = shallow().dive(); + + expect(phrases.dateIsUnavailable.calledWith(expectedFormattedDay)).to.equal(true); + expect(wrapper.prop('aria-label')).to.equal('dateIsUnavailable text'); + }); + + it('should set aria-label with a value pass through ariaLabelFormat prop if it exists', () => { + const modifiers = new Set(); + + const wrapper = shallow().dive(); + + expect(wrapper.prop('aria-label')).to.equal('October 10th 2017'); + }); + }); + }); + + describe('#onDayClick', () => { + let onDayClickSpy; + beforeEach(() => { + onDayClickSpy = sinon.spy(PureCustomizableCalendarDay.prototype, 'onDayClick'); + }); + + afterEach(() => { + sinon.restore(); + }); + + it('gets triggered by click', () => { + const wrapper = shallow().dive(); + wrapper.simulate('click'); + expect(onDayClickSpy).to.have.property('callCount', 1); + }); + + it('calls props.onDayClick', () => { + const onDayClickStub = sinon.stub(); + const wrapper = shallow().dive(); + wrapper.instance().onDayClick(); + expect(onDayClickStub).to.have.property('callCount', 1); + }); + }); + + describe('#onDayMouseEnter', () => { + let onDayMouseEnterSpy; + beforeEach(() => { + onDayMouseEnterSpy = sinon.spy(PureCustomizableCalendarDay.prototype, 'onDayMouseEnter'); + }); + + afterEach(() => { + sinon.restore(); + }); + + it('gets triggered by mouseenter', () => { + const wrapper = shallow().dive(); + wrapper.simulate('mouseenter'); + expect(onDayMouseEnterSpy).to.have.property('callCount', 1); + }); + + it('sets state.isHovered to false', () => { + const wrapper = shallow().dive(); + wrapper.setState({ isHovered: false }); + wrapper.instance().onDayMouseEnter(); + expect(wrapper.state().isHovered).to.equal(true); + }); + + it('calls props.onDayMouseEnter', () => { + const onMouseEnterStub = sinon.stub(); + const wrapper = shallow().dive(); + wrapper.instance().onDayMouseEnter(); + expect(onMouseEnterStub).to.have.property('callCount', 1); + }); + }); + + describe('#onDayMouseLeave', () => { + let onDayMouseLeaveSpy; + beforeEach(() => { + onDayMouseLeaveSpy = sinon.spy(PureCustomizableCalendarDay.prototype, 'onDayMouseLeave'); + }); + + afterEach(() => { + sinon.restore(); + }); + + it('gets triggered by mouseleave', () => { + const wrapper = shallow().dive(); + wrapper.simulate('mouseleave'); + expect(onDayMouseLeaveSpy).to.have.property('callCount', 1); + }); + + it('sets state.isHovered to false', () => { + const wrapper = shallow().dive(); + wrapper.setState({ isHovered: true }); + wrapper.instance().onDayMouseLeave(); + expect(wrapper.state().isHovered).to.equal(false); + }); + + it('calls props.onDayMouseLeave', () => { + const onMouseLeaveStub = sinon.stub(); + const wrapper = shallow().dive(); + wrapper.instance().onDayMouseLeave(); + expect(onMouseLeaveStub).to.have.property('callCount', 1); + }); + }); +}); From 94e18523fa8559fbf68d667b6f42697d8db3cdf4 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Mon, 11 Dec 2017 21:06:46 -0800 Subject: [PATCH 038/618] Version 16.0.0 --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a78d56798..52be4cfa75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 16.0.0 +- [breaking] Simplify `CalendarDay` component ([#894](https://github.com/airbnb/react-dates/pull/894)) +- [breaking] rename `renderDay` prop to `renderDayContents` ([#894](https://github.com/airbnb/react-dates/pull/894)) +- [new] Add `renderCalendarDay` component to allow for easy one-off customization of `CalendarDay` ([#894](https://github.com/airbnb/react-dates/pull/894)) + ## 15.5.1 - [fix] Adjust `small` variant height to be 36px ([#892](https://github.com/airbnb/react-dates/pull/892)) diff --git a/package.json b/package.json index f0d2dd5722..56103c944b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "15.5.1", + "version": "16.0.0", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From 4d3b312a5367c7711a78d9e9e4c43824fef08bbf Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Wed, 13 Dec 2017 10:32:26 -0800 Subject: [PATCH 039/618] Add missing onKeyDown function to CalendarDay --- src/components/CalendarDay.jsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index 7b2926b787..83cde243cf 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -82,6 +82,17 @@ class CalendarDay extends React.Component { onDayMouseLeave(day, e); } + onKeyDown(day, e) { + const { + onDayClick, + } = this.props; + + const { key } = e; + if (key === 'Enter' || key === ' ') { + onDayClick(day, e); + } + } + setButtonRef(ref) { this.buttonRef = ref; } From e57d11939a270d50ed3aaacb8f711e63e5fa5476 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Wed, 13 Dec 2017 13:14:10 -0800 Subject: [PATCH 040/618] Version 16.0.1 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52be4cfa75..bba77e2858 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Change Log +## 16.0.1 +- [fix] Add back missing onKeyDown method to `CalendarDay` ([#901](https://github.com/airbnb/react-dates/pull/901)) + ## 16.0.0 - [breaking] Simplify `CalendarDay` component ([#894](https://github.com/airbnb/react-dates/pull/894)) - [breaking] rename `renderDay` prop to `renderDayContents` ([#894](https://github.com/airbnb/react-dates/pull/894)) diff --git a/package.json b/package.json index 56103c944b..32f70c7cbd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "16.0.0", + "version": "16.0.1", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From 636f61929bbd08a87637f784b52249b9883d4641 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 15 Dec 2017 16:49:23 -0800 Subject: [PATCH 041/618] Revert "Merge pull request #866 from airbnb/maja-make-drp-ids-required" This reverts commit 75a43415e30a2e25697b81470f3bbf8b486c19d0, reversing changes made to f09d924115c0fa523afa52bf3263a43406ba69e0. Turns out this was breaking. --- src/components/DateRangePicker.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index fc89cf6c99..39d1c4f467 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -49,7 +49,9 @@ const defaultProps = { focusedInput: null, // input related props + startDateId: START_DATE, startDatePlaceholderText: 'Start Date', + endDateId: END_DATE, endDatePlaceholderText: 'End Date', disabled: false, required: false, From 065b6a3703367d43f912119db081b7b71dace2c4 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 15 Dec 2017 21:02:36 -0800 Subject: [PATCH 042/618] Version 15.5.2 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a78d56798..5dd5924859 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Change Log +## 15.5.2 +- revert [#866](https://github.com/airbnb/react-dates/pull/866); it turned out to be semver-major + ## 15.5.1 - [fix] Adjust `small` variant height to be 36px ([#892](https://github.com/airbnb/react-dates/pull/892)) diff --git a/package.json b/package.json index f0d2dd5722..fe45fa32ef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "15.5.1", + "version": "15.5.2", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From f48b820449b2a460761ead4c056ccfac2322bb78 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sat, 16 Dec 2017 21:45:51 -0800 Subject: [PATCH 043/618] Version 15.5.3 --- package.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index fe45fa32ef..6e77550421 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,11 @@ { "name": "react-dates", - "version": "15.5.2", + "version": "15.5.3", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { - "build": "npm run clean && npm run build:cjs && npm run build:esm && npm run build:css -- --optimize ", + "prebuild": "npm run clean", + "build": "npm run build:cjs && npm run build:esm && npm run build:css -- --optimize ", "build:cjs": "BABEL_ENV=cjs babel src/ -d lib/", "build:esm": "BABEL_ENV=esm babel src/ -d esm/", "prebuild:css": "rimraf lib/css && mkdir -p lib/css", @@ -119,8 +120,8 @@ "react-addons-shallow-compare": "^15.5.2", "react-moment-proptypes": "^1.5.0", "react-portal": "^4.1.0", - "react-with-styles": "^2.2.0", - "react-with-styles-interface-css": "^3.0.0" + "react-with-styles-interface-css": "^3.0.0", + "react-with-styles": "=2.2.0" }, "peerDependencies": { "moment": "^2.18.1", From 78f62725b1601d220b2082b6b1a034c91ab5b24d Mon Sep 17 00:00:00 2001 From: Mike Lambert Date: Wed, 23 Aug 2017 21:05:19 -0400 Subject: [PATCH 044/618] Fix React warning when Events are referenced later Fixes https://github.com/airbnb/react-dates/issues/514 , which happens due to Events being stored to be delivered on the trailing edge of a keyDown. I believe we don't need the trailing edge, and it is better than a persisted Event. --- src/components/DateInput.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index ee8a265d99..86e2c4e8b4 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -209,7 +209,7 @@ class DateInput extends React.Component { ref={this.setInputRef} value={value} onChange={this.onChange} - onKeyDown={throttle(this.onKeyDown, 300)} + onKeyDown={throttle(this.onKeyDown, 300, {trailing: false})} onFocus={onFocus} placeholder={placeholder} autoComplete="off" From 7ada678eae91429cc12cdcff22ab98932dee3279 Mon Sep 17 00:00:00 2001 From: Mike Lambert Date: Thu, 24 Aug 2017 02:28:32 -0400 Subject: [PATCH 045/618] Throttle at a deeper level, where we can filter out modifier-only key presses (ie, shift event on the way to a shift-tab event). Also turn off trailing edges, so that we don't need to hold onto events past their usefulness. --- src/components/DateInput.jsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index 86e2c4e8b4..8decc89841 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -84,6 +84,7 @@ class DateInput extends React.Component { this.onChange = this.onChange.bind(this); this.onKeyDown = this.onKeyDown.bind(this); this.setInputRef = this.setInputRef.bind(this); + this.throttledKeyDown = throttle(this.onFinalKeyDown, 300, { trailing: false }); } componentDidMount() { @@ -126,15 +127,20 @@ class DateInput extends React.Component { onKeyDown(e) { e.stopPropagation(); + if (!['Shift', 'Control', 'Alt', 'Meta'].includes(e.key)) { + this.throttledKeyDown(e); + } + } + onFinalKeyDown(e) { const { onKeyDownShiftTab, onKeyDownTab, onKeyDownArrowDown, onKeyDownQuestionMark, } = this.props; - const { key } = e; + if (key === 'Tab') { if (e.shiftKey) { onKeyDownShiftTab(e); @@ -209,7 +215,7 @@ class DateInput extends React.Component { ref={this.setInputRef} value={value} onChange={this.onChange} - onKeyDown={throttle(this.onKeyDown, 300, {trailing: false})} + onKeyDown={this.onKeyDown} onFocus={onFocus} placeholder={placeholder} autoComplete="off" From 45037c8c72976a73420cb62a7b330f9dddff1fcb Mon Sep 17 00:00:00 2001 From: Mike Lambert Date: Wed, 20 Dec 2017 01:53:48 -0500 Subject: [PATCH 046/618] Extract out the constant data structure. --- src/components/DateInput.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index 8decc89841..180344f3e7 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -20,6 +20,8 @@ const FANG_STROKE_TOP = `M0,${FANG_HEIGHT_PX} ${FANG_WIDTH_PX / 2},0 ${FANG_WIDT const FANG_PATH_BOTTOM = `M0,0 ${FANG_WIDTH_PX},0 ${FANG_WIDTH_PX / 2},${FANG_HEIGHT_PX}z`; const FANG_STROKE_BOTTOM = `M0,0 ${FANG_WIDTH_PX / 2},${FANG_HEIGHT_PX} ${FANG_WIDTH_PX},0`; +const MODIFIER_NAMES = ['Shift', 'Control', 'Alt', 'Meta']; + const propTypes = forbidExtraProps({ ...withStylesPropTypes, id: PropTypes.string.isRequired, @@ -127,7 +129,7 @@ class DateInput extends React.Component { onKeyDown(e) { e.stopPropagation(); - if (!['Shift', 'Control', 'Alt', 'Meta'].includes(e.key)) { + if (!MODIFIER_NAMES.includes(e.key)) { this.throttledKeyDown(e); } } From 2835d01dd8ab7aba6b17b3470f35ec1b0066feb5 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 29 Dec 2017 09:10:27 -0800 Subject: [PATCH 047/618] [Tests] include year in custom format string --- test/components/DateRangePickerInputController_spec.jsx | 4 ++-- test/components/SingleDatePicker_spec.jsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/components/DateRangePickerInputController_spec.jsx b/test/components/DateRangePickerInputController_spec.jsx index 79fae574bb..5d9c495c6e 100644 --- a/test/components/DateRangePickerInputController_spec.jsx +++ b/test/components/DateRangePickerInputController_spec.jsx @@ -296,7 +296,7 @@ describe('DateRangePickerInputController', () => { }); describe('matches custom display format', () => { - const customFormat = 'MM[foobar]DD'; + const customFormat = 'YY|MM[foobar]DD'; const customFormatDateString = moment(today).add(5, 'days').format(customFormat); it('calls props.onDatesChange with correct arguments', () => { const onDatesChangeStub = sinon.stub(); @@ -627,7 +627,7 @@ describe('DateRangePickerInputController', () => { }); describe('matches custom display format', () => { - const customFormat = 'MM[foobar]DD'; + const customFormat = 'YY|MM[foobar]DD'; const customFormatDateString = moment(today).add(5, 'days').format(customFormat); it('calls props.onDatesChange with correct arguments', () => { const onDatesChangeStub = sinon.stub(); diff --git a/test/components/SingleDatePicker_spec.jsx b/test/components/SingleDatePicker_spec.jsx index 4659234aad..cedd6f3eb5 100644 --- a/test/components/SingleDatePicker_spec.jsx +++ b/test/components/SingleDatePicker_spec.jsx @@ -229,7 +229,7 @@ describe('SingleDatePicker', () => { }); describe('matches custom display format', () => { - const customFormat = 'MM[foobar]DD'; + const customFormat = 'YY|MM[foobar]DD'; const customFormatDateString = moment().add(5, 'days').format(customFormat); it('calls props.onDateChange once', () => { const onDateChangeStub = sinon.stub(); From 37a7c774b8b63188366e89dad50728478da1cad8 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 29 Dec 2017 23:13:36 -0800 Subject: [PATCH 048/618] [Tests] increase test coverage --- test/components/CalendarDay_spec.jsx | 105 ++++++++++++++---- .../CustomizableCalendarDay_spec.jsx | 105 ++++++++++++++---- 2 files changed, 168 insertions(+), 42 deletions(-) diff --git a/test/components/CalendarDay_spec.jsx b/test/components/CalendarDay_spec.jsx index 3480e66117..61cd4aeaa1 100644 --- a/test/components/CalendarDay_spec.jsx +++ b/test/components/CalendarDay_spec.jsx @@ -8,6 +8,10 @@ import { BLOCKED_MODIFIER } from '../../src/constants'; import CalendarDay, { PureCalendarDay } from '../../src/components/CalendarDay'; describe('CalendarDay', () => { + afterEach(() => { + sinon.restore(); + }); + describe('#render', () => { it('contains formatted day for single digit days', () => { const firstOfMonth = moment().startOf('month'); @@ -58,10 +62,6 @@ describe('CalendarDay', () => { phrases.dateIsUnavailable = sinon.stub().returns('dateIsUnavailable text'); }); - afterEach(() => { - sinon.restore(); - }); - it('is formatted with the chooseAvailableDate phrase function when day is available', () => { const modifiers = new Set(); @@ -91,15 +91,90 @@ describe('CalendarDay', () => { it('should set aria-label with a value pass through ariaLabelFormat prop if it exists', () => { const modifiers = new Set(); - const wrapper = shallow().dive(); + const wrapper = shallow(( + + )).dive(); expect(wrapper.prop('aria-label')).to.equal('October 10th 2017'); }); }); + + describe('event handlers', () => { + const day = moment('10/10/2017'); + + let wrapper; + beforeEach(() => { + wrapper = shallow(( + + )).dive(); + }); + + it('onMouseUp blurs the event target', () => { + const handler = wrapper.prop('onMouseUp'); + const blur = sinon.spy(); + handler({ currentTarget: { blur } }); + expect(blur).to.have.property('callCount', 1); + }); + + it('onKeyDown calls this.onKeyDown', () => { + const spy = sinon.spy(wrapper.instance(), 'onKeyDown'); + const handler = wrapper.prop('onKeyDown'); + const event = {}; + handler(event); + expect(spy).to.have.property('callCount', 1); + expect(spy.calledWith(day, event)).to.equal(true); + }); + }); + + it('renders an empty when no day is given', () => { + const wrapper = shallow().dive(); + expect(wrapper.is('td')).to.equal(true); + expect(wrapper.children()).to.have.lengthOf(0); + expect(wrapper.props()).to.eql({}); + }); + }); + + describe('#onKeyDown', () => { + const day = moment('10/10/2017'); + + let onDayClick; + let wrapper; + beforeEach(() => { + onDayClick = sinon.spy(); + wrapper = shallow(( + + )).dive(); + }); + + it('calls onDayClick with the enter key', () => { + const event = { key: 'Enter' }; + wrapper.instance().onKeyDown(day, event); + expect(onDayClick).to.have.property('callCount', 1); + expect(onDayClick.calledWith(day, event)).to.equal(true); + }); + + it('calls onDayClick with the space key', () => { + const event = { key: ' ' }; + wrapper.instance().onKeyDown(day, event); + expect(onDayClick).to.have.property('callCount', 1); + expect(onDayClick.calledWith(day, event)).to.equal(true); + }); + + it('does not call onDayClick otherwise', () => { + const event = { key: 'Shift' }; + wrapper.instance().onKeyDown(day, event); + expect(onDayClick).to.have.property('callCount', 0); + }); }); describe('#onDayClick', () => { @@ -108,10 +183,6 @@ describe('CalendarDay', () => { onDayClickSpy = sinon.spy(PureCalendarDay.prototype, 'onDayClick'); }); - afterEach(() => { - sinon.restore(); - }); - it('gets triggered by click', () => { const wrapper = shallow().dive(); wrapper.simulate('click'); @@ -132,10 +203,6 @@ describe('CalendarDay', () => { onDayMouseEnterSpy = sinon.spy(PureCalendarDay.prototype, 'onDayMouseEnter'); }); - afterEach(() => { - sinon.restore(); - }); - it('gets triggered by mouseenter', () => { const wrapper = shallow().dive(); wrapper.simulate('mouseenter'); @@ -156,10 +223,6 @@ describe('CalendarDay', () => { onDayMouseLeaveSpy = sinon.spy(PureCalendarDay.prototype, 'onDayMouseLeave'); }); - afterEach(() => { - sinon.restore(); - }); - it('gets triggered by mouseleave', () => { const wrapper = shallow().dive(); wrapper.simulate('mouseleave'); diff --git a/test/components/CustomizableCalendarDay_spec.jsx b/test/components/CustomizableCalendarDay_spec.jsx index d3bb0cbf67..499c6d0422 100644 --- a/test/components/CustomizableCalendarDay_spec.jsx +++ b/test/components/CustomizableCalendarDay_spec.jsx @@ -8,6 +8,10 @@ import { BLOCKED_MODIFIER } from '../../src/constants'; import CustomizableCalendarDay, { PureCustomizableCalendarDay } from '../../src/components/CustomizableCalendarDay'; describe('CustomizableCalendarDay', () => { + afterEach(() => { + sinon.restore(); + }); + describe('#render', () => { it('contains formatted day for single digit days', () => { const firstOfMonth = moment().startOf('month'); @@ -60,10 +64,6 @@ describe('CustomizableCalendarDay', () => { phrases.dateIsUnavailable = sinon.stub().returns('dateIsUnavailable text'); }); - afterEach(() => { - sinon.restore(); - }); - it('is formatted with the chooseAvailableDate phrase function when day is available', () => { const modifiers = new Set(); @@ -93,15 +93,90 @@ describe('CustomizableCalendarDay', () => { it('should set aria-label with a value pass through ariaLabelFormat prop if it exists', () => { const modifiers = new Set(); - const wrapper = shallow().dive(); + const wrapper = shallow(( + + )).dive(); expect(wrapper.prop('aria-label')).to.equal('October 10th 2017'); }); }); + + describe('event handlers', () => { + const day = moment('10/10/2017'); + + let wrapper; + beforeEach(() => { + wrapper = shallow(( + + )).dive(); + }); + + it('onMouseUp blurs the event target', () => { + const handler = wrapper.prop('onMouseUp'); + const blur = sinon.spy(); + handler({ currentTarget: { blur } }); + expect(blur).to.have.property('callCount', 1); + }); + + it('onKeyDown calls this.onKeyDown', () => { + const spy = sinon.spy(wrapper.instance(), 'onKeyDown'); + const handler = wrapper.prop('onKeyDown'); + const event = {}; + handler(event); + expect(spy).to.have.property('callCount', 1); + expect(spy.calledWith(day, event)).to.equal(true); + }); + }); + + it('renders an empty when no day is given', () => { + const wrapper = shallow().dive(); + expect(wrapper.is('td')).to.equal(true); + expect(wrapper.children()).to.have.lengthOf(0); + expect(wrapper.props()).to.eql({}); + }); + }); + + describe('#onKeyDown', () => { + const day = moment('10/10/2017'); + + let onDayClick; + let wrapper; + beforeEach(() => { + onDayClick = sinon.spy(); + wrapper = shallow(( + + )).dive(); + }); + + it('calls onDayClick with the enter key', () => { + const event = { key: 'Enter' }; + wrapper.instance().onKeyDown(day, event); + expect(onDayClick).to.have.property('callCount', 1); + expect(onDayClick.calledWith(day, event)).to.equal(true); + }); + + it('calls onDayClick with the space key', () => { + const event = { key: ' ' }; + wrapper.instance().onKeyDown(day, event); + expect(onDayClick).to.have.property('callCount', 1); + expect(onDayClick.calledWith(day, event)).to.equal(true); + }); + + it('does not call onDayClick otherwise', () => { + const event = { key: 'Shift' }; + wrapper.instance().onKeyDown(day, event); + expect(onDayClick).to.have.property('callCount', 0); + }); }); describe('#onDayClick', () => { @@ -110,10 +185,6 @@ describe('CustomizableCalendarDay', () => { onDayClickSpy = sinon.spy(PureCustomizableCalendarDay.prototype, 'onDayClick'); }); - afterEach(() => { - sinon.restore(); - }); - it('gets triggered by click', () => { const wrapper = shallow().dive(); wrapper.simulate('click'); @@ -134,10 +205,6 @@ describe('CustomizableCalendarDay', () => { onDayMouseEnterSpy = sinon.spy(PureCustomizableCalendarDay.prototype, 'onDayMouseEnter'); }); - afterEach(() => { - sinon.restore(); - }); - it('gets triggered by mouseenter', () => { const wrapper = shallow().dive(); wrapper.simulate('mouseenter'); @@ -167,10 +234,6 @@ describe('CustomizableCalendarDay', () => { onDayMouseLeaveSpy = sinon.spy(PureCustomizableCalendarDay.prototype, 'onDayMouseLeave'); }); - afterEach(() => { - sinon.restore(); - }); - it('gets triggered by mouseleave', () => { const wrapper = shallow().dive(); wrapper.simulate('mouseleave'); From 9feb7c70116f150e311a3e4515c130e0a00caf41 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 29 Dec 2017 23:22:16 -0800 Subject: [PATCH 049/618] [Tests] falsey -> falsy --- CHANGELOG.md | 2 +- test/components/CalendarMonth_spec.jsx | 2 +- test/components/DateInput_spec.jsx | 4 ++-- test/components/DateRangePicker_spec.jsx | 2 +- test/components/DayPickerRangeController_spec.jsx | 12 ++++++------ .../DayPickerSingleDateController_spec.jsx | 2 +- test/components/DayPicker_spec.jsx | 14 +++++++------- test/components/SingleDatePickerInput_spec.jsx | 4 ++-- test/components/SingleDatePicker_spec.jsx | 4 ++-- test/utils/getInputHeight_spec.js | 2 +- test/utils/getPhrase_spec.jsx | 2 +- 11 files changed, 25 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d99e98c9a0..9fdebf024e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -148,7 +148,7 @@ ## v11.1.0 - [fix] Patch issues with vertical scrollable datepickers, after-hovered-start and month transitions ([#503](https://github.com/airbnb/react-dates/pull/503)) - [new] Adds a `readOnly` prop on the DRP and SDP ([#501](https://github.com/airbnb/react-dates/pull/501)) -- [fix] Disable hover when `focusedInput` is falsey ([#483](https://github.com/airbnb/react-dates/pull/483)) +- [fix] Disable hover when `focusedInput` is falsy ([#483](https://github.com/airbnb/react-dates/pull/483)) ## v11.0.1 - [fix] Fixes small modifier issues in the DRP after rearchitecture ([#489](https://github.com/airbnb/react-dates/pull/489)) diff --git a/test/components/CalendarMonth_spec.jsx b/test/components/CalendarMonth_spec.jsx index 6aa8bdf47c..777aa73e2d 100644 --- a/test/components/CalendarMonth_spec.jsx +++ b/test/components/CalendarMonth_spec.jsx @@ -13,7 +13,7 @@ describe('CalendarMonth', () => { expect(wrapper.prop('data-visible')).to.equal(true); }); - it('data-visible attribute is falsey if !props.isVisible', () => { + it('data-visible attribute is falsy if !props.isVisible', () => { const wrapper = shallow().dive(); expect(wrapper.prop('data-visible')).to.equal(false); }); diff --git a/test/components/DateInput_spec.jsx b/test/components/DateInput_spec.jsx index 059d844c20..90102761d8 100644 --- a/test/components/DateInput_spec.jsx +++ b/test/components/DateInput_spec.jsx @@ -38,7 +38,7 @@ describe('DateInput', () => { }); }); - describe('props.readOnly is falsey', () => { + describe('props.readOnly is falsy', () => { it('does not set readOnly', () => { const wrapper = shallow().dive(); expect(!!wrapper.find('input').prop('readOnly')).to.equal(false); @@ -73,7 +73,7 @@ describe('DateInput', () => { }); }); - describe('props.screenReaderMessage is falsey', () => { + describe('props.screenReaderMessage is falsy', () => { beforeEach(() => { wrapper = shallow().dive(); }); diff --git a/test/components/DateRangePicker_spec.jsx b/test/components/DateRangePicker_spec.jsx index fcf7a77fa1..382aabc032 100644 --- a/test/components/DateRangePicker_spec.jsx +++ b/test/components/DateRangePicker_spec.jsx @@ -374,7 +374,7 @@ describe('DateRangePicker', () => { }); }); - describe('focusedInput is falsey', () => { + describe('focusedInput is falsy', () => { it('calls onFocusChange', () => { const onFocusChangeStub = sinon.stub(); const wrapper = shallow(( diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index b96956ebc2..eaadb03fcf 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -597,7 +597,7 @@ describe('DayPickerRangeController', () => { }); }); - describe('new start date is falsey', () => { + describe('new start date is falsy', () => { it('does not call addModifierToRange with `after-hovered-start`', () => { const startDate = moment(); const addModifierToRangeSpy = sinon.spy(DayPickerRangeController.prototype, 'addModifierToRange'); @@ -2020,7 +2020,7 @@ describe('DayPickerRangeController', () => { expect(firstFocusableDay.isSame(today, 'day')).to.equal(true); }); - it('returns first day of arg month if startDate is falsey', () => { + it('returns first day of arg month if startDate is falsy', () => { sinon.stub(DayPickerRangeController.prototype, 'isBlocked').returns(false); const wrapper = shallow(( { }); describe('focusedInput === END_DATE', () => { - it('returns endDate if exists and is not blocked and startDate is falsey', () => { + it('returns endDate if exists and is not blocked and startDate is falsy', () => { sinon.stub(DayPickerRangeController.prototype, 'isBlocked').returns(false); const endDate = moment().add(10, 'days'); const wrapper = shallow(( @@ -2070,7 +2070,7 @@ describe('DayPickerRangeController', () => { expect(firstFocusableDay.isSame(startDate.clone().add(minimumNights, 'days'), 'day')).to.equal(true); }); - it('returns first day of arg month if startDate and endDate are falsey', () => { + it('returns first day of arg month if startDate and endDate are falsy', () => { sinon.stub(DayPickerRangeController.prototype, 'isBlocked').returns(false); const wrapper = shallow(( { expect(wrapper.instance().isDayAfterHoveredStartDate(testDate)).to.equal(true); }); - it('returns false if props.startDate is falsey', () => { + it('returns false if props.startDate is falsy', () => { const testDate = moment(today).add(1, 'days'); const wrapper = shallow(); wrapper.setState({ @@ -2775,7 +2775,7 @@ describe('DayPickerRangeController', () => { }); describe('#isHovered', () => { - it('returns false if focusedInput is falsey', () => { + it('returns false if focusedInput is falsy', () => { const wrapper = shallow(); wrapper.setState({ hoverDate: today, diff --git a/test/components/DayPickerSingleDateController_spec.jsx b/test/components/DayPickerSingleDateController_spec.jsx index 053ba19771..bd25ae87d6 100644 --- a/test/components/DayPickerSingleDateController_spec.jsx +++ b/test/components/DayPickerSingleDateController_spec.jsx @@ -844,7 +844,7 @@ describe('DayPickerSingleDateController', () => { }); describe('#getFirstFocusableDay', () => { - it('returns first day of arg month if not blocked and props.date is falsey', () => { + it('returns first day of arg month if not blocked and props.date is falsy', () => { sinon.stub(DayPickerSingleDateController.prototype, 'isBlocked').returns(false); const wrapper = shallow(( { expect(CalendarMonthGridComponent.prop('isAnimating')).to.equal(true); }); - it('is false if state.monthTransition is falsey', () => { + it('is false if state.monthTransition is falsy', () => { const wrapper = shallow().dive(); wrapper.setState({ monthTransition: null }); const CalendarMonthGridComponent = wrapper.find(CalendarMonthGrid); @@ -400,7 +400,7 @@ describe('DayPicker', () => { }); }); - describe('focusedDate is falsey', () => { + describe('focusedDate is falsy', () => { it('does not call maybeTransitionPrevMonth', () => { const maybeTransitionPrevMonthSpy = sinon.spy(PureDayPicker.prototype, 'maybeTransitionPrevMonth'); const wrapper = shallow().dive(); @@ -496,7 +496,7 @@ describe('DayPicker', () => { }); }); - describe('props.getFirstFocusableDay is falsey', () => { + describe('props.getFirstFocusableDay is falsy', () => { it('returns undefined if no arg', () => { const wrapper = shallow().dive(); expect(wrapper.instance().getFocusedDay()).to.equal(undefined); @@ -741,7 +741,7 @@ describe('DayPicker', () => { expect(adjustDayPickerHeightSpy).to.have.property('callCount', 2); }); - it('does not call adjustDayPickerHeight if state.monthTransition is falsey', () => { + it('does not call adjustDayPickerHeight if state.monthTransition is falsy', () => { const wrapper = mount(); wrapper.setState({ monthTransition: null, @@ -757,7 +757,7 @@ describe('DayPicker', () => { expect(updateStateAfterMonthTransitionSpy).to.have.property('callCount', 1); }); - it('does not call updateStateAfterMonthTransition if state.monthTransition is falsey', () => { + it('does not call updateStateAfterMonthTransition if state.monthTransition is falsy', () => { const wrapper = mount(); wrapper.setState({ monthTransition: null, @@ -775,7 +775,7 @@ describe('DayPicker', () => { expect(adjustDayPickerHeightSpy.called).to.equal(false); }); - it('does not call adjustDayPickerHeight if state.monthTransition is falsey', () => { + it('does not call adjustDayPickerHeight if state.monthTransition is falsy', () => { const wrapper = mount(); wrapper.setState({ monthTransition: null, @@ -791,7 +791,7 @@ describe('DayPicker', () => { expect(updateStateAfterMonthTransitionSpy).to.have.property('callCount', 1); }); - it('does not call updateStateAfterMonthTransition if state.monthTransition is falsey', () => { + it('does not call updateStateAfterMonthTransition if state.monthTransition is falsy', () => { const wrapper = mount(); wrapper.setState({ monthTransition: null, diff --git a/test/components/SingleDatePickerInput_spec.jsx b/test/components/SingleDatePickerInput_spec.jsx index 054b60a658..cd82e7ba6f 100644 --- a/test/components/SingleDatePickerInput_spec.jsx +++ b/test/components/SingleDatePickerInput_spec.jsx @@ -7,7 +7,7 @@ import SingleDatePickerInput from '../../src/components/SingleDatePickerInput'; describe('SingleDatePickerInput', () => { describe('clear date', () => { - describe('props.showClearDate is falsey', () => { + describe('props.showClearDate is falsy', () => { it('does not render a clear date button', () => { const wrapper = shallow().dive(); expect(wrapper.find('button')).to.have.lengthOf(0); @@ -36,7 +36,7 @@ describe('SingleDatePickerInput', () => { }); describe('show calendar icon', () => { - describe('props.showInputIcon is falsey', () => { + describe('props.showInputIcon is falsy', () => { it('does not have a calendar button', () => { const wrapper = shallow(( diff --git a/test/components/SingleDatePicker_spec.jsx b/test/components/SingleDatePicker_spec.jsx index cedd6f3eb5..59ba1311e2 100644 --- a/test/components/SingleDatePicker_spec.jsx +++ b/test/components/SingleDatePicker_spec.jsx @@ -105,7 +105,7 @@ describe('SingleDatePicker', () => { expect(wrapper.find(Portal)).to.have.length(1); }); - it('is not rendered if props.focused is falsey', () => { + it('is not rendered if props.focused is falsy', () => { const wrapper = shallow(( {}} @@ -144,7 +144,7 @@ describe('SingleDatePicker', () => { expect(wrapper.find(Portal)).to.have.length(1); }); - it('is not rendered when props.focused is falsey', () => { + it('is not rendered when props.focused is falsy', () => { const wrapper = shallow(( {}} diff --git a/test/utils/getInputHeight_spec.js b/test/utils/getInputHeight_spec.js index c5155669d0..d7d9adee93 100644 --- a/test/utils/getInputHeight_spec.js +++ b/test/utils/getInputHeight_spec.js @@ -20,7 +20,7 @@ const theme = { }; describe('#getInputHeight', () => { - it('returns the expected value with falsey second arg', () => { + it('returns the expected value with falsy second arg', () => { const inputHeight = getInputHeight(theme); expect(inputHeight).to.equal(55); }); diff --git a/test/utils/getPhrase_spec.jsx b/test/utils/getPhrase_spec.jsx index 7c06af346f..b204065324 100644 --- a/test/utils/getPhrase_spec.jsx +++ b/test/utils/getPhrase_spec.jsx @@ -3,7 +3,7 @@ import sinon from 'sinon-sandbox'; import getPhrase from '../../src/utils/getPhrase'; describe('getPhrase', () => { - it('returns empty string when arg is falsey', () => { + it('returns empty string when arg is falsy', () => { expect(getPhrase()).to.equal(''); }); From 85d5f3d8698473d5bd5fdf999404e8e2098bdca4 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 29 Dec 2017 23:26:37 -0800 Subject: [PATCH 050/618] [Refactor] destructure this.props more; ensure no `this.props` as the receiver --- src/components/CalendarMonthGrid.jsx | 3 ++- src/components/DateRangePickerInputController.jsx | 5 +++-- src/components/DayPicker.jsx | 12 +++++++----- src/components/DayPickerRangeController.jsx | 14 +++++++++++--- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/components/CalendarMonthGrid.jsx b/src/components/CalendarMonthGrid.jsx index dfd5552abc..d94e87b33a 100644 --- a/src/components/CalendarMonthGrid.jsx +++ b/src/components/CalendarMonthGrid.jsx @@ -199,7 +199,8 @@ class CalendarMonthGrid extends React.Component { } onTransitionEnd() { - this.props.onMonthTransitionEnd(); + const { onMonthTransitionEnd } = this.props; + onMonthTransitionEnd(); } setContainerRef(ref) { diff --git a/src/components/DateRangePickerInputController.jsx b/src/components/DateRangePickerInputController.jsx index aa5843214d..94e384b9ff 100644 --- a/src/components/DateRangePickerInputController.jsx +++ b/src/components/DateRangePickerInputController.jsx @@ -216,8 +216,9 @@ export default class DateRangePickerInputController extends React.Component { } onStartDateFocus() { - if (!this.props.disabled) { - this.props.onFocusChange(START_DATE); + const { disabled, onFocusChange } = this.props; + if (!disabled) { + onFocusChange(START_DATE); } } diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index 9b8414ec07..a06e109457 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -488,18 +488,20 @@ class DayPicker extends React.Component { } isHorizontal() { - return this.props.orientation === HORIZONTAL_ORIENTATION; + const { orientation } = this.props; + return orientation === HORIZONTAL_ORIENTATION; } isVertical() { - return this.props.orientation === VERTICAL_ORIENTATION || - this.props.orientation === VERTICAL_SCROLLABLE; + const { orientation } = this.props; + return orientation === VERTICAL_ORIENTATION || orientation === VERTICAL_SCROLLABLE; } updateStateAfterMonthTransition() { const { onPrevMonthClick, onNextMonthClick, + isRTL, } = this.props; const { @@ -532,7 +534,7 @@ class DayPicker extends React.Component { this.setState({ currentMonth: newMonth, monthTransition: null, - translationValue: (this.props.isRTL && this.isHorizontal()) ? -calendarMonthWidth : 0, + translationValue: (isRTL && this.isHorizontal()) ? -calendarMonthWidth : 0, nextFocusedDate: null, focusedDate: newFocusedDate, }, () => { @@ -705,7 +707,7 @@ class DayPicker extends React.Component { weekHeaders.push(this.renderWeekHeader(i)); } - const verticalScrollable = this.props.orientation === VERTICAL_SCROLLABLE; + const verticalScrollable = orientation === VERTICAL_SCROLLABLE; const firstVisibleMonthIndex = this.getFirstVisibleIndex(); const horizontalWidth = (calendarMonthWidth * numberOfMonths) + (2 * DAY_PICKER_PADDING); diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 0a03216a1c..6204ac7e11 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -415,11 +415,19 @@ export default class DayPickerRangeController extends React.Component { } onDayClick(day, e) { - const { keepOpenOnDateSelect, minimumNights, onBlur } = this.props; + const { + keepOpenOnDateSelect, + minimumNights, + onBlur, + focusedInput, + onFocusChange, + onClose, + onDatesChange, + } = this.props; + if (e) e.preventDefault(); if (this.isBlocked(day)) return; - const { focusedInput, onFocusChange, onClose } = this.props; let { startDate, endDate } = this.props; if (focusedInput === START_DATE) { @@ -448,7 +456,7 @@ export default class DayPickerRangeController extends React.Component { } } - this.props.onDatesChange({ startDate, endDate }); + onDatesChange({ startDate, endDate }); onBlur(); } From fb6f04c81af1d371fc3cee9e790d0b2d719c2cc4 Mon Sep 17 00:00:00 2001 From: Ben Thompson Date: Sun, 31 Dec 2017 00:53:43 -0500 Subject: [PATCH 051/618] Requires startDateId, endDateId on DateRangePicker Based on #866 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 54614a0721..1333774ae6 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,9 @@ Here is the minimum *REQUIRED* setup you need to get the `DateRangePicker` worki ```jsx this.setState({ startDate, endDate })} // PropTypes.func.isRequired, focusedInput={this.state.focusedInput} // PropTypes.oneOf([START_DATE, END_DATE]) or null, onFocusChange={focusedInput => this.setState({ focusedInput })} // PropTypes.func.isRequired, @@ -113,9 +115,7 @@ Here is the minimum *REQUIRED* setup you need to get the `DateRangePicker` worki The following is a list of other *OPTIONAL* props you may provide to the `DateRangePicker` to customize appearance and behavior to your heart's desire. Again, please explore the [storybook](http://airbnb.io/react-dates/?selectedKind=DRP%20-%20Input%20Props&selectedStory=default&full=0&down=1&left=1&panelRight=0&downPanel=kadirahq%2Fstorybook-addon-actions%2Factions-panel) for more information on what each of these props do. ```js // input related props -startDateId: PropTypes.string.isRequired, startDatePlaceholderText: PropTypes.string, -endDateId: PropTypes.string.isRequired, endDatePlaceholderText: PropTypes.string, disabled: PropTypes.bool, required: PropTypes.bool, From 7d9cad100b7681d91ec5d06fff4e3b00f62e71a5 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 31 Dec 2017 22:02:21 -0800 Subject: [PATCH 052/618] =?UTF-8?q?[Tests]=20fix=20more=20tests=20that=20f?= =?UTF-8?q?ail=20when=20it=E2=80=99s=20January=201st?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/components/DayPickerRangeController_spec.jsx | 2 +- test/components/DayPickerSingleDateController_spec.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index eaadb03fcf..1d3d854831 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -1851,7 +1851,7 @@ describe('DayPickerRangeController', () => { currentMonth: today, }); wrapper.instance().onPrevMonthClick(); - expect(wrapper.state().currentMonth.month()).to.equal(today.month() - 1); + expect(wrapper.state().currentMonth.month()).to.equal(today.clone().subtract(1, 'month').month()); }); it('new visibleDays has previous month', () => { diff --git a/test/components/DayPickerSingleDateController_spec.jsx b/test/components/DayPickerSingleDateController_spec.jsx index bd25ae87d6..9bbaef9ab5 100644 --- a/test/components/DayPickerSingleDateController_spec.jsx +++ b/test/components/DayPickerSingleDateController_spec.jsx @@ -690,7 +690,7 @@ describe('DayPickerSingleDateController', () => { currentMonth: today, }); wrapper.instance().onPrevMonthClick(); - expect(wrapper.state().currentMonth.month()).to.equal(today.month() - 1); + expect(wrapper.state().currentMonth.month()).to.equal(today.clone().subtract(1, 'month').month()); }); it('new visibleDays has previous month', () => { From 9780545c35831d793032538243bf560f254bbe60 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 1 Jan 2018 21:47:43 -0800 Subject: [PATCH 053/618] [Dev Deps] update `sinon`, `nyc`, `node-sass`, `moment`, `karma-firefox-launcher`, `karma-webpack`, `cross-env`, `enzyme`, `enzyme-adapter-react-helper`, `eslint`, `eslint-plugin-jsx-a11y`, `eslint-plugin-react`, `@storybook/react`, `@storybook/addon-links`, `@storybook/addon-info`, `@storybook/addon-options` --- package.json | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index ff6c97989c..05e832927a 100644 --- a/package.json +++ b/package.json @@ -52,10 +52,10 @@ "homepage": "https://github.com/airbnb/react-dates#readme", "devDependencies": { "@storybook/addon-actions": "^3.2.12", - "@storybook/addon-info": "^3.2.9", - "@storybook/addon-links": "^3.2.12", - "@storybook/addon-options": "^3.2.6", - "@storybook/react": "^3.2.8", + "@storybook/addon-info": "^3.3.3", + "@storybook/addon-links": "^3.3.3", + "@storybook/addon-options": "^3.3.3", + "@storybook/react": "^3.3.3", "airbnb-js-shims": "^1.3.0", "aphrodite": "^1.2.5", "babel-cli": "^6.26.0", @@ -71,14 +71,14 @@ "chai": "^4.1.2", "clean-css": "^4.1.9", "coveralls": "^2.13.3", - "cross-env": "^5.1.1", - "enzyme": "^3.2.0", - "enzyme-adapter-react-helper": "^1.0.3", - "eslint": "^4.11.0", + "cross-env": "^5.1.3", + "enzyme": "^3.3.0", + "enzyme-adapter-react-helper": "^1.2.2", + "eslint": "^4.14.0", "eslint-config-airbnb": "^16.1.0", "eslint-plugin-import": "^2.8.0", - "eslint-plugin-jsx-a11y": "^6.0.2", - "eslint-plugin-react": "^7.4.0", + "eslint-plugin-jsx-a11y": "^6.0.3", + "eslint-plugin-react": "^7.5.1", "eslint-plugin-react-with-styles": "^1.1.1", "git-directory-deploy": "^1.5.1", "imports-loader": "^0.7.1", @@ -86,16 +86,16 @@ "json-loader": "^0.5.7", "karma": "^1.7.1", "karma-chai": "^0.1.0", - "karma-firefox-launcher": "^1.0.1", + "karma-firefox-launcher": "^1.1.0", "karma-mocha": "^1.3.0", "karma-sinon": "^1.0.5", - "karma-webpack": "^2.0.5", + "karma-webpack": "^2.0.9", "mocha": "^3.5.3", "mocha-wrap": "^2.1.1", - "moment": "^2.19.2", + "moment": "^2.20.1", "moment-jalaali": "^0.7.2", - "node-sass": "^4.5.3", - "nyc": "^11.2.1", + "node-sass": "^4.7.2", + "nyc": "^11.4.1", "raw-loader": "^0.5.1", "react": "^0.14 || ^15.5.4 || ^16.1.1", "react-dom": "^0.14 || ^15.5.4 || ^16.1.1", @@ -104,7 +104,7 @@ "rimraf": "^2.6.2", "safe-publish-latest": "^1.1.1", "sass-loader": "^6.0.6", - "sinon": "^4.1.2", + "sinon": "^4.1.3", "sinon-sandbox": "^1.0.2", "style-loader": "^0.19.0", "webpack": "^2.6.1" @@ -130,6 +130,7 @@ }, "greenkeeper": { "ignore": [ + "mocha", "webpack" ] } From c388b4b71b21ee9630f673f5334660be8a17b9d2 Mon Sep 17 00:00:00 2001 From: Erin Doyle Date: Fri, 22 Dec 2017 14:42:11 -0500 Subject: [PATCH 054/618] Modified the throttling of the onKeyDown handler updates --- src/components/DateInput.jsx | 5 ++--- src/components/DayPicker.jsx | 9 ++++++++- src/constants.js | 2 ++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index 180344f3e7..1b27130e2b 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -13,6 +13,7 @@ import { FANG_HEIGHT_PX, FANG_WIDTH_PX, DEFAULT_VERTICAL_SPACING, + MODIFIER_KEY_NAMES, } from '../constants'; const FANG_PATH_TOP = `M0,${FANG_HEIGHT_PX} ${FANG_WIDTH_PX},${FANG_HEIGHT_PX} ${FANG_WIDTH_PX / 2},0z`; @@ -20,8 +21,6 @@ const FANG_STROKE_TOP = `M0,${FANG_HEIGHT_PX} ${FANG_WIDTH_PX / 2},0 ${FANG_WIDT const FANG_PATH_BOTTOM = `M0,0 ${FANG_WIDTH_PX},0 ${FANG_WIDTH_PX / 2},${FANG_HEIGHT_PX}z`; const FANG_STROKE_BOTTOM = `M0,0 ${FANG_WIDTH_PX / 2},${FANG_HEIGHT_PX} ${FANG_WIDTH_PX},0`; -const MODIFIER_NAMES = ['Shift', 'Control', 'Alt', 'Meta']; - const propTypes = forbidExtraProps({ ...withStylesPropTypes, id: PropTypes.string.isRequired, @@ -129,7 +128,7 @@ class DateInput extends React.Component { onKeyDown(e) { e.stopPropagation(); - if (!MODIFIER_NAMES.includes(e.key)) { + if (!MODIFIER_KEY_NAMES.includes(e.key)) { this.throttledKeyDown(e); } } diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index a06e109457..c2ff958b82 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -32,6 +32,7 @@ import { VERTICAL_ORIENTATION, VERTICAL_SCROLLABLE, DAY_SIZE, + MODIFIER_KEY_NAMES, } from '../constants'; const MONTH_PADDING = 23; @@ -173,6 +174,7 @@ class DayPicker extends React.Component { this.calendarMonthGridHeight = 0; this.onKeyDown = this.onKeyDown.bind(this); + this.throttledKeyDown = throttle(this.onFinalKeyDown, 200, { trailing: false }); this.onPrevMonthClick = this.onPrevMonthClick.bind(this); this.onNextMonthClick = this.onNextMonthClick.bind(this); this.multiplyScrollableMonths = this.multiplyScrollableMonths.bind(this); @@ -251,7 +253,12 @@ class DayPicker extends React.Component { onKeyDown(e) { e.stopPropagation(); + if (!MODIFIER_KEY_NAMES.includes(e.key)) { + this.throttledKeyDown(e); + } + } + onFinalKeyDown(e) { this.setState({ withMouseInteractions: false }); const { onBlur, isRTL } = this.props; @@ -778,7 +785,7 @@ class DayPicker extends React.Component { {...css(styles.DayPicker_focusRegion)} ref={this.setContainerRef} onClick={(e) => { e.stopPropagation(); }} - onKeyDown={throttle(this.onKeyDown, 300)} + onKeyDown={this.onKeyDown} onMouseUp={() => { this.setState({ withMouseInteractions: true }); }} role="region" tabIndex={-1} diff --git a/src/constants.js b/src/constants.js index 0e8bb5a4b2..6f9ab9b348 100644 --- a/src/constants.js +++ b/src/constants.js @@ -25,3 +25,5 @@ export const WEEKDAYS = [0, 1, 2, 3, 4, 5, 6]; export const FANG_WIDTH_PX = 20; export const FANG_HEIGHT_PX = 10; export const DEFAULT_VERTICAL_SPACING = 22; + +export const MODIFIER_KEY_NAMES = ['Shift', 'Control', 'Alt', 'Meta']; From 0150880b35d953fc931696185644eb4729b34762 Mon Sep 17 00:00:00 2001 From: Erin Doyle Date: Fri, 22 Dec 2017 15:12:00 -0500 Subject: [PATCH 055/618] Cleaned up the onKeyDown handler when the Keyboard Shortcuts panel is open so that the correct keys are being used to close the panel. --- src/components/DayPickerKeyboardShortcuts.jsx | 17 ++++++++--------- .../DayPickerKeyboardShortcuts_spec.jsx | 7 +++---- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/components/DayPickerKeyboardShortcuts.jsx b/src/components/DayPickerKeyboardShortcuts.jsx index eacb6d5640..d571a82cc7 100644 --- a/src/components/DayPickerKeyboardShortcuts.jsx +++ b/src/components/DayPickerKeyboardShortcuts.jsx @@ -3,11 +3,10 @@ import PropTypes from 'prop-types'; import { forbidExtraProps } from 'airbnb-prop-types'; import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; -import KeyboardShortcutRow from './KeyboardShortcutRow'; - import { DayPickerKeyboardShortcutsPhrases } from '../defaultPhrases'; import getPhrasePropTypes from '../utils/getPhrasePropTypes'; +import KeyboardShortcutRow from './KeyboardShortcutRow'; import CloseButton from './CloseButton'; export const TOP_LEFT = 'top-left'; @@ -97,35 +96,35 @@ class DayPickerKeyboardShortcuts extends React.Component { } onKeyDown(e) { + e.stopPropagation(); + const { closeKeyboardShortcutsPanel } = this.props; // Because the close button is the only focusable element inside of the panel, this - // amount to a very basic focus trap. The user can exit the panel by "pressing" the + // amounts to a very basic focus trap. The user can exit the panel by "pressing" the // close button or hitting escape switch (e.key) { - case 'Space': + case 'Enter': + case ' ': + case 'Spacebar': // for older browsers case 'Escape': - e.stopPropagation(); closeKeyboardShortcutsPanel(); break; - // only stopPropagation here - this allows the up and down arrows continue their + // do nothing - this allows the up and down arrows continue their // default behavior of scrolling the content of the Keyboard Shortcuts Panel // which is needed when only a single month is shown for instance. case 'ArrowUp': case 'ArrowDown': - e.stopPropagation(); break; // completely block the rest of the keys that have functionality outside of this panel case 'Tab': - case 'Enter': case 'Home': case 'End': case 'PageUp': case 'PageDown': case 'ArrowLeft': case 'ArrowRight': - e.stopPropagation(); e.preventDefault(); break; diff --git a/test/components/DayPickerKeyboardShortcuts_spec.jsx b/test/components/DayPickerKeyboardShortcuts_spec.jsx index 35f15c237e..8550e5271a 100644 --- a/test/components/DayPickerKeyboardShortcuts_spec.jsx +++ b/test/components/DayPickerKeyboardShortcuts_spec.jsx @@ -216,7 +216,7 @@ describe('DayPickerKeyboardShortcuts', () => { }); it('onKeyDown Space calls e.stopPropagation and onShowKeyboardShortcutsButtonClick', () => { - closeButton.prop('onKeyDown')({ ...event, key: 'Space' }); + closeButton.prop('onKeyDown')({ ...event, key: ' ' }); expect(event.stopPropagation.callCount).to.equal(1); expect(closeKeyboardShortcutsPanelStub.callCount).to.equal(1); }); @@ -227,11 +227,10 @@ describe('DayPickerKeyboardShortcuts', () => { expect(closeKeyboardShortcutsPanelStub.callCount).to.equal(1); }); - it('onKeyDown Enter calls e.stopPropagation and e.preventDefault and NOT onShowKeyboardShortcutsButtonClick', () => { + it('onKeyDown Enter calls e.stopPropagation and onShowKeyboardShortcutsButtonClick', () => { closeButton.prop('onKeyDown')({ ...event, key: 'Enter' }); expect(event.stopPropagation.callCount).to.equal(1); - expect(event.preventDefault.callCount).to.equal(1); - expect(closeKeyboardShortcutsPanelStub.notCalled).to.equal(true); + expect(closeKeyboardShortcutsPanelStub.callCount).to.equal(1); }); it('onKeyDown Tab calls e.stopPropagation and e.preventDefault and NOT onShowKeyboardShortcutsButtonClick', () => { From db515e777e8f65d917802019268194c259376bce Mon Sep 17 00:00:00 2001 From: Erin Doyle Date: Fri, 22 Dec 2017 16:29:39 -0500 Subject: [PATCH 056/618] Changed the MODIFIER_KEY_NAMES constant from an array to a Set --- src/components/DateInput.jsx | 2 +- src/components/DayPicker.jsx | 2 +- src/constants.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index 1b27130e2b..0e36647501 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -128,7 +128,7 @@ class DateInput extends React.Component { onKeyDown(e) { e.stopPropagation(); - if (!MODIFIER_KEY_NAMES.includes(e.key)) { + if (!MODIFIER_KEY_NAMES.has(e.key)) { this.throttledKeyDown(e); } } diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index c2ff958b82..2ec875befd 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -253,7 +253,7 @@ class DayPicker extends React.Component { onKeyDown(e) { e.stopPropagation(); - if (!MODIFIER_KEY_NAMES.includes(e.key)) { + if (!MODIFIER_KEY_NAMES.has(e.key)) { this.throttledKeyDown(e); } } diff --git a/src/constants.js b/src/constants.js index 6f9ab9b348..8a6a798116 100644 --- a/src/constants.js +++ b/src/constants.js @@ -26,4 +26,4 @@ export const FANG_WIDTH_PX = 20; export const FANG_HEIGHT_PX = 10; export const DEFAULT_VERTICAL_SPACING = 22; -export const MODIFIER_KEY_NAMES = ['Shift', 'Control', 'Alt', 'Meta']; +export const MODIFIER_KEY_NAMES = new Set(['Shift', 'Control', 'Alt', 'Meta']); From 91d9b82b4d3ac1d50b3efeff9aae8f37899b7c26 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 7 Jan 2018 22:27:03 -0800 Subject: [PATCH 057/618] [Dev Deps] update `@storybook/react`, `@storybook/addon-links`, `@storybook/addon-info`, `@storybook/addon-options`, `@storybook/addon-actions`, `airbnb-js-shims` --- package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 05e832927a..a88c577f37 100644 --- a/package.json +++ b/package.json @@ -51,12 +51,12 @@ }, "homepage": "https://github.com/airbnb/react-dates#readme", "devDependencies": { - "@storybook/addon-actions": "^3.2.12", - "@storybook/addon-info": "^3.3.3", - "@storybook/addon-links": "^3.3.3", - "@storybook/addon-options": "^3.3.3", - "@storybook/react": "^3.3.3", - "airbnb-js-shims": "^1.3.0", + "@storybook/addon-actions": "^3.3.5", + "@storybook/addon-info": "^3.3.5", + "@storybook/addon-links": "^3.3.5", + "@storybook/addon-options": "^3.3.5", + "@storybook/react": "^3.3.5", + "airbnb-js-shims": "^1.4.0", "aphrodite": "^1.2.5", "babel-cli": "^6.26.0", "babel-core": "^6.26.0", From e1949f2b3c8bf5fb25dcec0010d1f39c6e624e1a Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 7 Jan 2018 22:55:03 -0800 Subject: [PATCH 058/618] [Dev Deps] update `babel-loader`, `babel-plugin-inline-react-svg`, `eslint` --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a88c577f37..144b4dcf91 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "babel-cli": "^6.26.0", "babel-core": "^6.26.0", "babel-loader": "^6.4.1", - "babel-plugin-inline-react-svg": "^0.5.1", + "babel-plugin-inline-react-svg": "^0.5.2", "babel-plugin-inline-svg": "^0.1.0", "babel-plugin-istanbul": "^4.1.5", "babel-plugin-syntax-jsx": "^6.18.0", @@ -74,7 +74,7 @@ "cross-env": "^5.1.3", "enzyme": "^3.3.0", "enzyme-adapter-react-helper": "^1.2.2", - "eslint": "^4.14.0", + "eslint": "^4.15.0", "eslint-config-airbnb": "^16.1.0", "eslint-plugin-import": "^2.8.0", "eslint-plugin-jsx-a11y": "^6.0.3", From da7bb3678f26c6a5ba86f928549a4e0461bd8f19 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 7 Jan 2018 22:57:05 -0800 Subject: [PATCH 059/618] [Deps] update `consolidated-events`, `object.assign`, `prop-types`, `react-addons-shallow-compare`, `react-portal` --- package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 144b4dcf91..b7a47f0ced 100644 --- a/package.json +++ b/package.json @@ -111,17 +111,17 @@ }, "dependencies": { "airbnb-prop-types": "^2.8.1", - "consolidated-events": "^1.1.0", + "consolidated-events": "^1.1.1", "is-touch-device": "^1.0.1", "lodash": "^4.1.1", - "object.assign": "^4.0.4", + "object.assign": "^4.1.0", "object.values": "^1.0.4", - "prop-types": "^15.5.10", - "react-addons-shallow-compare": "^15.5.2", + "prop-types": "^15.6.0", + "react-addons-shallow-compare": "^15.6.2", "react-moment-proptypes": "^1.5.0", - "react-portal": "^4.1.0", - "react-with-styles-interface-css": "^3.0.0", - "react-with-styles": "=2.2.0" + "react-portal": "^4.1.2", + "react-with-styles": "=2.2.0", + "react-with-styles-interface-css": "^3.0.0" }, "peerDependencies": { "moment": "^2.18.1", From 048612ad7fa82ff1cfba8716195567c7026797f5 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Mon, 8 Jan 2018 16:49:20 -0800 Subject: [PATCH 060/618] Version 16.0.2 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fdebf024e..283f7b972b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 16.0.2 +- [fix] Fix keyboard navigation issues ([#916](https://github.com/airbnb/react-dates/pull/916)) +- [fix] Fix React warnings when events are referenced later ([#682](https://github.com/airbnb/react-dates/pull/682)) + ## 16.0.1 - [fix] Add back missing onKeyDown method to `CalendarDay` ([#901](https://github.com/airbnb/react-dates/pull/901)) diff --git a/package.json b/package.json index b7a47f0ced..30774d22ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "16.0.1", + "version": "16.0.2", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From e6cf29eb84fa5b6b3d9b7b3e8c25f5f6655bb235 Mon Sep 17 00:00:00 2001 From: Erin Doyle Date: Fri, 15 Dec 2017 19:21:17 -0500 Subject: [PATCH 061/618] Added the setting of the CalendarDay aria-label with a dateIsSelected phrase when the day is the selected day. --- src/components/CalendarDay.jsx | 16 ++++++++++------ src/components/CustomizableCalendarDay.jsx | 16 ++++++++++------ src/defaultPhrases.js | 9 ++++++--- test/components/CalendarDay_spec.jsx | 18 ++++++++++++++++++ .../CustomizableCalendarDay_spec.jsx | 18 ++++++++++++++++++ 5 files changed, 62 insertions(+), 15 deletions(-) diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index 83cde243cf..d568a68a3c 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -110,17 +110,12 @@ class CalendarDay extends React.Component { phrases: { chooseAvailableDate, dateIsUnavailable, + dateIsSelected, }, } = this.props; if (!day) return ; - const formattedDate = { date: day.format(ariaLabelFormat) }; - - const ariaLabel = modifiers.has(BLOCKED_MODIFIER) - ? getPhrase(dateIsUnavailable, formattedDate) - : getPhrase(chooseAvailableDate, formattedDate); - const daySizeStyles = { width: daySize, height: daySize - 1, @@ -145,6 +140,15 @@ class CalendarDay extends React.Component { const isOutsideRange = modifiers.has('blocked-out-of-range'); + const formattedDate = { date: day.format(ariaLabelFormat) }; + + let ariaLabel = getPhrase(chooseAvailableDate, formattedDate); + if (modifiers.has(BLOCKED_MODIFIER)) { + ariaLabel = getPhrase(dateIsUnavailable, formattedDate); + } else if (selected) { + ariaLabel = getPhrase(dateIsSelected, formattedDate); + } + return ( ; - const formattedDate = { date: day.format(ariaLabelFormat) }; - - const ariaLabel = modifiers.has(BLOCKED_MODIFIER) - ? getPhrase(dateIsUnavailable, formattedDate) - : getPhrase(chooseAvailableDate, formattedDate); - const daySizeStyles = { width: daySize, height: daySize - 1, @@ -207,6 +202,15 @@ class CustomizableCalendarDay extends React.Component { const isOutsideRange = modifiers.has('blocked-out-of-range'); + const formattedDate = { date: day.format(ariaLabelFormat) }; + + let ariaLabel = getPhrase(chooseAvailableDate, formattedDate); + if (modifiers.has(BLOCKED_MODIFIER)) { + ariaLabel = getPhrase(dateIsUnavailable, formattedDate); + } else if (selected) { + ariaLabel = getPhrase(dateIsSelected, formattedDate); + } + const defaultStyles = getStyles(defaultStylesWithHover, isHovered); const outsideStyles = getStyles(outsideStylesWithHover, isHovered); const todayStyles = getStyles(todayStylesWithHover, isHovered); diff --git a/src/defaultPhrases.js b/src/defaultPhrases.js index 985677751e..acc6a45ca0 100644 --- a/src/defaultPhrases.js +++ b/src/defaultPhrases.js @@ -25,13 +25,11 @@ const returnFocusToInput = 'Return to the date input field.'; const keyboardNavigationInstructions = `Press the down arrow key to interact with the calendar and select a date. Press the question mark key to get the keyboard shortcuts for changing dates.`; -// eslint-disable-next-line camelcase const chooseAvailableStartDate = ({ date }) => `Choose ${date} as your check-in date. It's available.`; - -// eslint-disable-next-line camelcase const chooseAvailableEndDate = ({ date }) => `Choose ${date} as your check-out date. It's available.`; const chooseAvailableDate = ({ date }) => date; const dateIsUnavailable = ({ date }) => `Not available. ${date}`; +const dateIsSelected = ({ date }) => `Selected. ${date}`; export default { calendarLabel, @@ -63,6 +61,7 @@ export default { chooseAvailableStartDate, chooseAvailableEndDate, dateIsUnavailable, + dateIsSelected, }; export const DateRangePickerPhrases = { @@ -93,6 +92,7 @@ export const DateRangePickerPhrases = { chooseAvailableStartDate, chooseAvailableEndDate, dateIsUnavailable, + dateIsSelected, }; export const DateRangePickerInputPhrases = { @@ -127,6 +127,7 @@ export const SingleDatePickerPhrases = { keyboardNavigationInstructions, chooseAvailableDate, dateIsUnavailable, + dateIsSelected, }; export const SingleDatePickerInputPhrases = { @@ -159,6 +160,7 @@ export const DayPickerPhrases = { chooseAvailableEndDate, chooseAvailableDate, dateIsUnavailable, + dateIsSelected, }; export const DayPickerKeyboardShortcutsPhrases = { @@ -189,4 +191,5 @@ export const DayPickerNavigationPhrases = { export const CalendarDayPhrases = { chooseAvailableDate, dateIsUnavailable, + dateIsSelected, }; diff --git a/test/components/CalendarDay_spec.jsx b/test/components/CalendarDay_spec.jsx index 61cd4aeaa1..9cc4c832b0 100644 --- a/test/components/CalendarDay_spec.jsx +++ b/test/components/CalendarDay_spec.jsx @@ -59,6 +59,7 @@ describe('CalendarDay', () => { beforeEach(() => { phrases.chooseAvailableDate = sinon.stub().returns('chooseAvailableDate text'); + phrases.dateIsSelected = sinon.stub().returns('dateIsSelected text'); phrases.dateIsUnavailable = sinon.stub().returns('dateIsUnavailable text'); }); @@ -75,6 +76,23 @@ describe('CalendarDay', () => { expect(wrapper.prop('aria-label')).to.equal('chooseAvailableDate text'); }); + it('is formatted with the dateIsSelected phrase function when day is selected', () => { + const selectedModifiers = new Set(['selected', 'selected-start', 'selected-end']); + + selectedModifiers.forEach((selectedModifier) => { + const modifiers = new Set().add(selectedModifier); + + const wrapper = shallow().dive(); + + expect(phrases.dateIsSelected.calledWith(expectedFormattedDay)).to.equal(true); + expect(wrapper.prop('aria-label')).to.equal('dateIsSelected text'); + }); + }); + it('is formatted with the dateIsUnavailable phrase function when day is not available', () => { const modifiers = new Set().add(BLOCKED_MODIFIER); diff --git a/test/components/CustomizableCalendarDay_spec.jsx b/test/components/CustomizableCalendarDay_spec.jsx index 499c6d0422..d8365a2265 100644 --- a/test/components/CustomizableCalendarDay_spec.jsx +++ b/test/components/CustomizableCalendarDay_spec.jsx @@ -61,6 +61,7 @@ describe('CustomizableCalendarDay', () => { beforeEach(() => { phrases.chooseAvailableDate = sinon.stub().returns('chooseAvailableDate text'); + phrases.dateIsSelected = sinon.stub().returns('dateIsSelected text'); phrases.dateIsUnavailable = sinon.stub().returns('dateIsUnavailable text'); }); @@ -77,6 +78,23 @@ describe('CustomizableCalendarDay', () => { expect(wrapper.prop('aria-label')).to.equal('chooseAvailableDate text'); }); + it('is formatted with the dateIsSelected phrase function when day is selected', () => { + const selectedModifiers = new Set(['selected', 'selected-start', 'selected-end']); + + selectedModifiers.forEach((selectedModifier) => { + const modifiers = new Set().add(selectedModifier); + + const wrapper = shallow().dive(); + + expect(phrases.dateIsSelected.calledWith(expectedFormattedDay)).to.equal(true); + expect(wrapper.prop('aria-label')).to.equal('dateIsSelected text'); + }); + }); + it('is formatted with the dateIsUnavailable phrase function when day is not available', () => { const modifiers = new Set().add(BLOCKED_MODIFIER); From de495709a9754b6a3571c91a16434f924391f5cf Mon Sep 17 00:00:00 2001 From: Erin Doyle Date: Mon, 18 Dec 2017 15:24:19 -0500 Subject: [PATCH 062/618] Added a getCalendarDaySettings utility function that can be shared by CalendarDay and CustomizableCalendarDay. --- src/components/CalendarDay.jsx | 50 +--- src/components/CustomizableCalendarDay.jsx | 50 +--- src/utils/getCalendarDaySettings.js | 52 ++++ test/components/CalendarDay_spec.jsx | 4 - .../CustomizableCalendarDay_spec.jsx | 4 - test/utils/getCalendarDaySettings_spec.js | 240 ++++++++++++++++++ 6 files changed, 314 insertions(+), 86 deletions(-) create mode 100644 src/utils/getCalendarDaySettings.js create mode 100644 test/utils/getCalendarDaySettings_spec.js diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index d568a68a3c..f7a362af13 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -8,9 +8,9 @@ import moment from 'moment'; import { CalendarDayPhrases } from '../defaultPhrases'; import getPhrasePropTypes from '../utils/getPhrasePropTypes'; -import getPhrase from '../utils/getPhrase'; +import getCalendarDaySettings from '../utils/getCalendarDaySettings'; -import { BLOCKED_MODIFIER, DAY_SIZE } from '../constants'; +import { DAY_SIZE } from '../constants'; const propTypes = forbidExtraProps({ ...withStylesPropTypes, @@ -107,47 +107,19 @@ class CalendarDay extends React.Component { renderDayContents, tabIndex, styles, - phrases: { - chooseAvailableDate, - dateIsUnavailable, - dateIsSelected, - }, + phrases, } = this.props; if (!day) return ; - const daySizeStyles = { - width: daySize, - height: daySize - 1, - }; - - const useDefaultCursor = ( - modifiers.has('blocked-minimum-nights') - || modifiers.has('blocked-calendar') - || modifiers.has('blocked-out-of-range') - ); - - const selected = ( - modifiers.has('selected') - || modifiers.has('selected-start') - || modifiers.has('selected-end') - ); - - const hoveredSpan = !selected && ( - modifiers.has('hovered-span') - || modifiers.has('after-hovered-start') - ); - - const isOutsideRange = modifiers.has('blocked-out-of-range'); - - const formattedDate = { date: day.format(ariaLabelFormat) }; - - let ariaLabel = getPhrase(chooseAvailableDate, formattedDate); - if (modifiers.has(BLOCKED_MODIFIER)) { - ariaLabel = getPhrase(dateIsUnavailable, formattedDate); - } else if (selected) { - ariaLabel = getPhrase(dateIsSelected, formattedDate); - } + const { + daySizeStyles, + useDefaultCursor, + selected, + hoveredSpan, + isOutsideRange, + ariaLabel, + } = getCalendarDaySettings(day, ariaLabelFormat, daySize, modifiers, phrases); return ( ; - const daySizeStyles = { - width: daySize, - height: daySize - 1, - }; - - const useDefaultCursor = ( - modifiers.has('blocked-minimum-nights') - || modifiers.has('blocked-calendar') - || modifiers.has('blocked-out-of-range') - ); - - const selected = ( - modifiers.has('selected') - || modifiers.has('selected-start') - || modifiers.has('selected-end') - ); - - const hoveredSpan = !selected && ( - modifiers.has('hovered-span') - || modifiers.has('after-hovered-start') - ); - - const isOutsideRange = modifiers.has('blocked-out-of-range'); - - const formattedDate = { date: day.format(ariaLabelFormat) }; - - let ariaLabel = getPhrase(chooseAvailableDate, formattedDate); - if (modifiers.has(BLOCKED_MODIFIER)) { - ariaLabel = getPhrase(dateIsUnavailable, formattedDate); - } else if (selected) { - ariaLabel = getPhrase(dateIsSelected, formattedDate); - } + const { + daySizeStyles, + useDefaultCursor, + selected, + hoveredSpan, + isOutsideRange, + ariaLabel, + } = getCalendarDaySettings(day, ariaLabelFormat, daySize, modifiers, phrases); const defaultStyles = getStyles(defaultStylesWithHover, isHovered); const outsideStyles = getStyles(outsideStylesWithHover, isHovered); diff --git a/src/utils/getCalendarDaySettings.js b/src/utils/getCalendarDaySettings.js new file mode 100644 index 0000000000..d599c67f69 --- /dev/null +++ b/src/utils/getCalendarDaySettings.js @@ -0,0 +1,52 @@ +import getPhrase from '../utils/getPhrase'; +import { BLOCKED_MODIFIER } from '../constants'; + +export default function getCalendarDaySettings(day, ariaLabelFormat, daySize, modifiers, phrases) { + const { + chooseAvailableDate, + dateIsUnavailable, + dateIsSelected, + } = phrases; + + const daySizeStyles = { + width: daySize, + height: daySize - 1, + }; + + const useDefaultCursor = ( + modifiers.has('blocked-minimum-nights') + || modifiers.has('blocked-calendar') + || modifiers.has('blocked-out-of-range') + ); + + const selected = ( + modifiers.has('selected') + || modifiers.has('selected-start') + || modifiers.has('selected-end') + ); + + const hoveredSpan = !selected && ( + modifiers.has('hovered-span') + || modifiers.has('after-hovered-start') + ); + + const isOutsideRange = modifiers.has('blocked-out-of-range'); + + const formattedDate = { date: day.format(ariaLabelFormat) }; + + let ariaLabel = getPhrase(chooseAvailableDate, formattedDate); + if (modifiers.has(BLOCKED_MODIFIER)) { + ariaLabel = getPhrase(dateIsUnavailable, formattedDate); + } else if (selected) { + ariaLabel = getPhrase(dateIsSelected, formattedDate); + } + + return { + daySizeStyles, + useDefaultCursor, + selected, + hoveredSpan, + isOutsideRange, + ariaLabel, + }; +} diff --git a/test/components/CalendarDay_spec.jsx b/test/components/CalendarDay_spec.jsx index 9cc4c832b0..b9780d3688 100644 --- a/test/components/CalendarDay_spec.jsx +++ b/test/components/CalendarDay_spec.jsx @@ -55,7 +55,6 @@ describe('CalendarDay', () => { describe('aria-label', () => { const phrases = {}; const day = moment('10/10/2017'); - const expectedFormattedDay = { date: 'Tuesday, October 10, 2017' }; beforeEach(() => { phrases.chooseAvailableDate = sinon.stub().returns('chooseAvailableDate text'); @@ -72,7 +71,6 @@ describe('CalendarDay', () => { day={day} />).dive(); - expect(phrases.chooseAvailableDate.calledWith(expectedFormattedDay)).to.equal(true); expect(wrapper.prop('aria-label')).to.equal('chooseAvailableDate text'); }); @@ -88,7 +86,6 @@ describe('CalendarDay', () => { day={day} />).dive(); - expect(phrases.dateIsSelected.calledWith(expectedFormattedDay)).to.equal(true); expect(wrapper.prop('aria-label')).to.equal('dateIsSelected text'); }); }); @@ -102,7 +99,6 @@ describe('CalendarDay', () => { day={day} />).dive(); - expect(phrases.dateIsUnavailable.calledWith(expectedFormattedDay)).to.equal(true); expect(wrapper.prop('aria-label')).to.equal('dateIsUnavailable text'); }); diff --git a/test/components/CustomizableCalendarDay_spec.jsx b/test/components/CustomizableCalendarDay_spec.jsx index d8365a2265..83fc8bfa44 100644 --- a/test/components/CustomizableCalendarDay_spec.jsx +++ b/test/components/CustomizableCalendarDay_spec.jsx @@ -57,7 +57,6 @@ describe('CustomizableCalendarDay', () => { describe('aria-label', () => { const phrases = {}; const day = moment('10/10/2017'); - const expectedFormattedDay = { date: 'Tuesday, October 10, 2017' }; beforeEach(() => { phrases.chooseAvailableDate = sinon.stub().returns('chooseAvailableDate text'); @@ -74,7 +73,6 @@ describe('CustomizableCalendarDay', () => { day={day} />).dive(); - expect(phrases.chooseAvailableDate.calledWith(expectedFormattedDay)).to.equal(true); expect(wrapper.prop('aria-label')).to.equal('chooseAvailableDate text'); }); @@ -90,7 +88,6 @@ describe('CustomizableCalendarDay', () => { day={day} />).dive(); - expect(phrases.dateIsSelected.calledWith(expectedFormattedDay)).to.equal(true); expect(wrapper.prop('aria-label')).to.equal('dateIsSelected text'); }); }); @@ -104,7 +101,6 @@ describe('CustomizableCalendarDay', () => { day={day} />).dive(); - expect(phrases.dateIsUnavailable.calledWith(expectedFormattedDay)).to.equal(true); expect(wrapper.prop('aria-label')).to.equal('dateIsUnavailable text'); }); diff --git a/test/utils/getCalendarDaySettings_spec.js b/test/utils/getCalendarDaySettings_spec.js new file mode 100644 index 0000000000..98ceacd061 --- /dev/null +++ b/test/utils/getCalendarDaySettings_spec.js @@ -0,0 +1,240 @@ +import moment from 'moment'; +import { expect } from 'chai'; +import sinon from 'sinon-sandbox'; + +import getCalendarDaySettings from '../../src/utils/getCalendarDaySettings'; +import { BLOCKED_MODIFIER } from '../../src/constants'; + +const testDay = moment('10/10/2017'); +const expectedFormattedDay = { date: 'Tuesday, October 10, 2017' }; +const testAriaLabelFormat = 'dddd, LL'; +const testDaySize = 39; +const testModifiers = new Set(); +const testPhrases = { + chooseAvailableDate: sinon.stub(), + dateIsSelected: sinon.stub(), + dateIsUnavailable: sinon.stub(), +}; + +describe('getCalendarDaySettings', () => { + describe('daySizeStyles', () => { + const { daySizeStyles } = getCalendarDaySettings( + testDay, + testAriaLabelFormat, + testDaySize, + testModifiers, + testPhrases, + ); + + it('includes width equal to daySize', () => { + expect(daySizeStyles.width).to.equal(testDaySize); + }); + + it('includes height equal to daySize - 1', () => { + expect(daySizeStyles.height).to.equal(testDaySize - 1); + }); + }); + + describe('useDefaultCursor', () => { + it('should be true when day is some kind of blocked', () => { + const blockedModifiers = new Set([ + 'blocked-minimum-nights', + 'blocked-calendar', + 'blocked-out-of-range', + ]); + + blockedModifiers.forEach((blockedModifier) => { + const modifiers = new Set([blockedModifier]); + const { useDefaultCursor } = getCalendarDaySettings( + testDay, + testAriaLabelFormat, + testDaySize, + modifiers, + testPhrases, + ); + expect(useDefaultCursor).to.equal(true); + }); + }); + + it('should be false when day is not blocked', () => { + const modifiers = new Set(['some-kind-of-not-blocked']); + const { useDefaultCursor } = getCalendarDaySettings( + testDay, + testAriaLabelFormat, + testDaySize, + modifiers, + testPhrases, + ); + expect(useDefaultCursor).to.equal(false); + }); + }); + + describe('selected', () => { + it('should be true when day is some kind of selected', () => { + const selectedModifiers = new Set([ + 'selected', + 'selected-start', + 'selected-end', + ]); + + selectedModifiers.forEach((selectedModifier) => { + const modifiers = new Set([selectedModifier]); + const { selected } = getCalendarDaySettings( + testDay, + testAriaLabelFormat, + testDaySize, + modifiers, + testPhrases, + ); + expect(selected).to.equal(true); + }); + }); + + it('should be false when day is not selected', () => { + const modifiers = new Set(['some-kind-of-not-selected']); + const { selected } = getCalendarDaySettings( + testDay, + testAriaLabelFormat, + testDaySize, + modifiers, + testPhrases, + ); + expect(selected).to.equal(false); + }); + }); + + describe('hoveredSpan', () => { + it('should be true when day is not selected and hovered-span', () => { + const modifiers = new Set(['hovered-span']); + const { hoveredSpan } = getCalendarDaySettings( + testDay, + testAriaLabelFormat, + testDaySize, + modifiers, + testPhrases, + ); + expect(hoveredSpan).to.equal(true); + }); + + it('should be true when day is not selected and after-hovered-start', () => { + const modifiers = new Set(['hovered-span']); + const { hoveredSpan } = getCalendarDaySettings( + testDay, + testAriaLabelFormat, + testDaySize, + modifiers, + testPhrases, + ); + expect(hoveredSpan).to.equal(true); + }); + + it('should be false when day is some kind of selected', () => { + const selectedModifiers = new Set([ + 'selected', + 'selected-start', + 'selected-end', + ]); + + selectedModifiers.forEach((selectedModifier) => { + const modifiers = new Set([selectedModifier]); + const { hoveredSpan } = getCalendarDaySettings( + testDay, + testAriaLabelFormat, + testDaySize, + modifiers, + testPhrases, + ); + expect(hoveredSpan).to.equal(false); + }); + }); + }); + + describe('isOutsideRange', () => { + it('should be true when blocked-out-of-range', () => { + const modifiers = new Set(['blocked-out-of-range']); + const { isOutsideRange } = getCalendarDaySettings( + testDay, + testAriaLabelFormat, + testDaySize, + modifiers, + testPhrases, + ); + expect(isOutsideRange).to.equal(true); + }); + + it('should be false when not blocked-out-of-range', () => { + const modifiers = new Set(); + const { isOutsideRange } = getCalendarDaySettings( + testDay, + testAriaLabelFormat, + testDaySize, + modifiers, + testPhrases, + ); + expect(isOutsideRange).to.equal(false); + }); + }); + + describe('ariaLabel', () => { + const phrases = {}; + + beforeEach(() => { + phrases.chooseAvailableDate = sinon.stub().returns('chooseAvailableDate text'); + phrases.dateIsSelected = sinon.stub().returns('dateIsSelected text'); + phrases.dateIsUnavailable = sinon.stub().returns('dateIsUnavailable text'); + }); + + afterEach(() => { + sinon.restore(); + }); + + it('is formatted with the chooseAvailableDate phrase function when day is available', () => { + const modifiers = new Set(); + + const { ariaLabel } = getCalendarDaySettings( + testDay, + testAriaLabelFormat, + testDaySize, + modifiers, + phrases, + ); + + expect(phrases.chooseAvailableDate.calledWith(expectedFormattedDay)).to.equal(true); + expect(ariaLabel).to.equal('chooseAvailableDate text'); + }); + + it('is formatted with the dateIsSelected phrase function when day is selected', () => { + const selectedModifiers = new Set(['selected', 'selected-start', 'selected-end']); + + selectedModifiers.forEach((selectedModifier) => { + const modifiers = new Set().add(selectedModifier); + + const { ariaLabel } = getCalendarDaySettings( + testDay, + testAriaLabelFormat, + testDaySize, + modifiers, + phrases, + ); + + expect(phrases.dateIsSelected.calledWith(expectedFormattedDay)).to.equal(true); + expect(ariaLabel).to.equal('dateIsSelected text'); + }); + }); + + it('is formatted with the dateIsUnavailable phrase function when day is not available', () => { + const modifiers = new Set().add(BLOCKED_MODIFIER); + + const { ariaLabel } = getCalendarDaySettings( + testDay, + testAriaLabelFormat, + testDaySize, + modifiers, + phrases, + ); + + expect(phrases.dateIsUnavailable.calledWith(expectedFormattedDay)).to.equal(true); + expect(ariaLabel).to.equal('dateIsUnavailable text'); + }); + }); +}); From c9979779bcebec061c30f8ca93682a66bafe3c07 Mon Sep 17 00:00:00 2001 From: Erin Doyle Date: Mon, 18 Dec 2017 15:43:37 -0500 Subject: [PATCH 063/618] Tests clean up --- test/components/CalendarDay_spec.jsx | 42 ++++++++------ .../CustomizableCalendarDay_spec.jsx | 58 +++++++++++-------- 2 files changed, 58 insertions(+), 42 deletions(-) diff --git a/test/components/CalendarDay_spec.jsx b/test/components/CalendarDay_spec.jsx index b9780d3688..e062954d9a 100644 --- a/test/components/CalendarDay_spec.jsx +++ b/test/components/CalendarDay_spec.jsx @@ -33,7 +33,7 @@ describe('CalendarDay', () => { }); it('passes modifiers to renderDayContents', () => { - const modifiers = new Set().add(BLOCKED_MODIFIER); + const modifiers = new Set([BLOCKED_MODIFIER]); const renderDayContents = (day, mods) => `${day.format('dddd')}${mods.has(BLOCKED_MODIFIER) ? 'BLOCKED' : ''}`; const expected = `${moment().format('dddd')}BLOCKED`; const wrapper = @@ -65,11 +65,13 @@ describe('CalendarDay', () => { it('is formatted with the chooseAvailableDate phrase function when day is available', () => { const modifiers = new Set(); - const wrapper = shallow().dive(); + const wrapper = shallow(( + + )).dive(); expect(wrapper.prop('aria-label')).to.equal('chooseAvailableDate text'); }); @@ -78,26 +80,30 @@ describe('CalendarDay', () => { const selectedModifiers = new Set(['selected', 'selected-start', 'selected-end']); selectedModifiers.forEach((selectedModifier) => { - const modifiers = new Set().add(selectedModifier); + const modifiers = new Set([selectedModifier]); - const wrapper = shallow().dive(); + const wrapper = shallow(( + + )).dive(); expect(wrapper.prop('aria-label')).to.equal('dateIsSelected text'); }); }); it('is formatted with the dateIsUnavailable phrase function when day is not available', () => { - const modifiers = new Set().add(BLOCKED_MODIFIER); + const modifiers = new Set([BLOCKED_MODIFIER]); - const wrapper = shallow().dive(); + const wrapper = shallow(( + + )).dive(); expect(wrapper.prop('aria-label')).to.equal('dateIsUnavailable text'); }); diff --git a/test/components/CustomizableCalendarDay_spec.jsx b/test/components/CustomizableCalendarDay_spec.jsx index 83fc8bfa44..9534defead 100644 --- a/test/components/CustomizableCalendarDay_spec.jsx +++ b/test/components/CustomizableCalendarDay_spec.jsx @@ -33,7 +33,7 @@ describe('CustomizableCalendarDay', () => { }); it('passes modifiers to renderDay', () => { - const modifiers = new Set().add(BLOCKED_MODIFIER); + const modifiers = new Set([BLOCKED_MODIFIER]); const renderDay = (day, mods) => `${day.format('dddd')}${mods.has(BLOCKED_MODIFIER) ? 'BLOCKED' : ''}`; const expected = `${moment().format('dddd')}BLOCKED`; const wrapper = shallow( { it('is formatted with the chooseAvailableDate phrase function when day is available', () => { const modifiers = new Set(); - const wrapper = shallow().dive(); + const wrapper = shallow(( + + )).dive(); expect(wrapper.prop('aria-label')).to.equal('chooseAvailableDate text'); }); @@ -80,26 +82,30 @@ describe('CustomizableCalendarDay', () => { const selectedModifiers = new Set(['selected', 'selected-start', 'selected-end']); selectedModifiers.forEach((selectedModifier) => { - const modifiers = new Set().add(selectedModifier); + const modifiers = new Set([selectedModifier]); - const wrapper = shallow().dive(); + const wrapper = shallow(( + + )).dive(); expect(wrapper.prop('aria-label')).to.equal('dateIsSelected text'); }); }); it('is formatted with the dateIsUnavailable phrase function when day is not available', () => { - const modifiers = new Set().add(BLOCKED_MODIFIER); + const modifiers = new Set([BLOCKED_MODIFIER]); - const wrapper = shallow().dive(); + const wrapper = shallow(( + + )).dive(); expect(wrapper.prop('aria-label')).to.equal('dateIsUnavailable text'); }); @@ -234,9 +240,11 @@ describe('CustomizableCalendarDay', () => { it('calls props.onDayMouseEnter', () => { const onMouseEnterStub = sinon.stub(); - const wrapper = shallow().dive(); + const wrapper = shallow(( + + )).dive(); wrapper.instance().onDayMouseEnter(); expect(onMouseEnterStub).to.have.property('callCount', 1); }); @@ -263,9 +271,11 @@ describe('CustomizableCalendarDay', () => { it('calls props.onDayMouseLeave', () => { const onMouseLeaveStub = sinon.stub(); - const wrapper = shallow().dive(); + const wrapper = shallow(( + + )).dive(); wrapper.instance().onDayMouseLeave(); expect(onMouseLeaveStub).to.have.property('callCount', 1); }); From 44f384273c7f869753d73b66d10ed36cd7eb61cc Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Wed, 10 Jan 2018 01:10:19 -0800 Subject: [PATCH 064/618] [Dev Deps] update `sinon` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 30774d22ec..032a138672 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "rimraf": "^2.6.2", "safe-publish-latest": "^1.1.1", "sass-loader": "^6.0.6", - "sinon": "^4.1.3", + "sinon": "^4.1.4", "sinon-sandbox": "^1.0.2", "style-loader": "^0.19.0", "webpack": "^2.6.1" From 6ccee38879a41951f474cda421fb87466ceff81e Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Wed, 3 Jan 2018 14:12:22 -0800 Subject: [PATCH 065/618] Hide invisible first month using `visibility: hidden` --- src/components/CalendarMonthGrid.jsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/CalendarMonthGrid.jsx b/src/components/CalendarMonthGrid.jsx index d94e87b33a..8dfd8ee940 100644 --- a/src/components/CalendarMonthGrid.jsx +++ b/src/components/CalendarMonthGrid.jsx @@ -301,6 +301,7 @@ class CalendarMonthGrid extends React.Component { position: 'absolute', top: -this.calendarMonthHeights[0], }, + !isVisible && !isAnimating && styles.CalendarMonthGrid_month__hidden, )} > ({ opacity: 0, pointerEvents: 'none', }, + + CalendarMonthGrid_month__hidden: { + visibility: 'hidden', + }, }))(CalendarMonthGrid); From 32913a2621c973952e27c9e05f4bbe050a3726f2 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Tue, 16 Jan 2018 12:49:51 -0800 Subject: [PATCH 066/618] Add example id values for startDateId/endDateId --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1333774ae6..b3253f426b 100644 --- a/README.md +++ b/README.md @@ -103,9 +103,9 @@ Here is the minimum *REQUIRED* setup you need to get the `DateRangePicker` worki ```jsx this.setState({ startDate, endDate })} // PropTypes.func.isRequired, focusedInput={this.state.focusedInput} // PropTypes.oneOf([START_DATE, END_DATE]) or null, onFocusChange={focusedInput => this.setState({ focusedInput })} // PropTypes.func.isRequired, From 26b6786444e098812ef00a147bbc5feeb6f68a5d Mon Sep 17 00:00:00 2001 From: codejunkienick Date: Thu, 28 Dec 2017 13:22:42 +0500 Subject: [PATCH 067/618] fix typo --- src/components/DateInput.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index 0e36647501..946384c01f 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -235,7 +235,7 @@ class DateInput extends React.Component { openDirection === OPEN_DOWN && { top: inputHeight + verticalSpacing - FANG_HEIGHT_PX - 1, }, - openDirection === OPEN_DOWN && { + openDirection === OPEN_UP && { bottom: inputHeight + verticalSpacing - FANG_HEIGHT_PX - 1, }, )} From 361a31b04723abc59b9ad60f2d9b09cb451f72c6 Mon Sep 17 00:00:00 2001 From: Dmytriy Lytvynenko <> Date: Sun, 7 Jan 2018 20:00:11 +0200 Subject: [PATCH 068/618] Fix issue with clear dates after manually changing date by highlighting text --- src/components/DateInput.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index 946384c01f..e7c7707fd8 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -93,7 +93,7 @@ class DateInput extends React.Component { } componentWillReceiveProps(nextProps) { - if (!this.props.displayValue && nextProps.displayValue) { + if (nextProps.displayValue) { this.setState({ dateString: '', }); From 67985dcc9d02159ad21351bf20f7b60876e2d1e8 Mon Sep 17 00:00:00 2001 From: Dmytriy Lytvynenko <> Date: Sun, 7 Jan 2018 22:22:37 +0200 Subject: [PATCH 069/618] Update condition and move onChange to setState callback --- src/components/DateInput.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index e7c7707fd8..cbba041ff8 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -93,7 +93,7 @@ class DateInput extends React.Component { } componentWillReceiveProps(nextProps) { - if (nextProps.displayValue) { + if (this.state.dateString && nextProps.displayValue) { this.setState({ dateString: '', }); @@ -121,8 +121,7 @@ class DateInput extends React.Component { if (dateString[dateString.length - 1] === '?') { onKeyDownQuestionMark(e); } else { - this.setState({ dateString }); - onChange(dateString); + this.setState({ dateString }, () => onChange(dateString)); } } From 2f6f2cc782f28e2228057a8591206fa8814a339b Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Tue, 16 Jan 2018 10:05:07 -0800 Subject: [PATCH 070/618] Add DateInput regression test --- test/components/DateInput_spec.jsx | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/components/DateInput_spec.jsx b/test/components/DateInput_spec.jsx index 90102761d8..1ca40249d5 100644 --- a/test/components/DateInput_spec.jsx +++ b/test/components/DateInput_spec.jsx @@ -89,6 +89,28 @@ describe('DateInput', () => { }); }); + describe('#componentWillReceiveProps', () => { + describe('nextProps.displayValue exists', () => { + it('sets state.dateString to \'\'', () => { + const dateString = 'foo123'; + const wrapper = shallow().dive(); + wrapper.setState({ dateString }); + wrapper.instance().componentWillReceiveProps({ displayValue: '1991-07-13' }); + expect(wrapper.state()).to.have.property('dateString', ''); + }); + }); + + describe('nextProps.displayValue does not exist', () => { + it('does not change state.dateString', () => { + const dateString = 'foo123'; + const wrapper = shallow().dive(); + wrapper.setState({ dateString }); + wrapper.instance().componentWillReceiveProps({ displayValue: null }); + expect(wrapper.state()).to.have.property('dateString', dateString); + }); + }); + }); + describe('#onChange', () => { const evt = { target: { value: 'foobar' } }; it('sets state.dateString to e.target.value', () => { From 90b25bf38c7fd9afa5d10ab604bc4274a5fa796b Mon Sep 17 00:00:00 2001 From: Ben Rudolph Date: Wed, 17 Jan 2018 11:01:00 -0800 Subject: [PATCH 071/618] Add small property to docs --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b3253f426b..48c9ecdd07 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,7 @@ customArrowIcon: PropTypes.node, customCloseIcon: PropTypes.node, noBorder: PropTypes.bool, block: PropTypes.bool, +small: PropTypes.bool, // calendar presentation and interaction related props renderMonth: PropTypes.func, From 9f0b8fb8b9968b10b50bd23ddf51cb737e256ede Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Wed, 17 Jan 2018 13:44:09 -0800 Subject: [PATCH 072/618] Version 16.1.0 --- CHANGELOG.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 283f7b972b..0624ccf3ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## 16.1.0 +- [fix] Allow for changing of the input value via highlight and replace ([#955](https://github.com/airbnb/react-dates/pull/955)) +- [fix] Fix OPEN_UP styling ([#925](https://github.com/airbnb/react-dates/pull/925)) +- [fix] Don't read invisible months to the screen reader ([#940](https://github.com/airbnb/react-dates/pull/940)) +- [new] Add phrase for aria-label for the selected day ([#905](https://github.com/airbnb/react-dates/pull/905)) + ## 16.0.2 - [fix] Fix keyboard navigation issues ([#916](https://github.com/airbnb/react-dates/pull/916)) - [fix] Fix React warnings when events are referenced later ([#682](https://github.com/airbnb/react-dates/pull/682)) diff --git a/package.json b/package.json index 032a138672..21691b92d8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "16.0.2", + "version": "16.1.0", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From 14771e343a97003b40acb3e07453e083376db39d Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Wed, 17 Jan 2018 13:32:21 -0800 Subject: [PATCH 073/618] Add regular option to DRP and SDP --- README.md | 3 +++ examples/DateRangePickerWrapper.jsx | 1 + examples/SingleDatePickerWrapper.jsx | 1 + src/components/DateInput.jsx | 8 ++++++++ src/components/DateRangePicker.jsx | 3 +++ src/components/DateRangePickerInput.jsx | 5 +++++ src/components/DateRangePickerInputController.jsx | 4 ++++ src/components/SingleDatePicker.jsx | 3 +++ src/components/SingleDatePickerInput.jsx | 5 +++++ src/shapes/DateRangePickerShape.js | 1 + src/shapes/SingleDatePickerShape.js | 1 + stories/DateRangePicker_input.js | 8 ++++++++ stories/SingleDatePicker_input.js | 7 +++++++ 13 files changed, 50 insertions(+) diff --git a/README.md b/README.md index 48c9ecdd07..11cdbf6b90 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,7 @@ customCloseIcon: PropTypes.node, noBorder: PropTypes.bool, block: PropTypes.bool, small: PropTypes.bool, +regular: PropTypes.bool, // calendar presentation and interaction related props renderMonth: PropTypes.func, @@ -203,6 +204,8 @@ showDefaultInputIcon: PropTypes.bool, customInputIcon: PropTypes.node, noBorder: PropTypes.bool, block: PropTypes.bool, +small: PropTypes.bool, +regular: PropTypes.bool, // calendar presentation and interaction related props renderMonth: PropTypes.func, diff --git a/examples/DateRangePickerWrapper.jsx b/examples/DateRangePickerWrapper.jsx index ba0018e69c..9a43817293 100644 --- a/examples/DateRangePickerWrapper.jsx +++ b/examples/DateRangePickerWrapper.jsx @@ -49,6 +49,7 @@ const defaultProps = { customCloseIcon: null, block: false, small: false, + regular: false, // calendar presentation and interaction related props renderMonth: null, diff --git a/examples/SingleDatePickerWrapper.jsx b/examples/SingleDatePickerWrapper.jsx index 328b12a0b6..4c3d91ec32 100644 --- a/examples/SingleDatePickerWrapper.jsx +++ b/examples/SingleDatePickerWrapper.jsx @@ -40,6 +40,7 @@ const defaultProps = { customInputIcon: null, block: false, small: false, + regular: false, verticalSpacing: undefined, // calendar presentation and interaction related props diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index cbba041ff8..10bf83f714 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -35,6 +35,7 @@ const propTypes = forbidExtraProps({ showCaret: PropTypes.bool, verticalSpacing: nonNegativeInteger, small: PropTypes.bool, + regular: PropTypes.bool, onChange: PropTypes.func, onFocus: PropTypes.func, @@ -60,6 +61,7 @@ const defaultProps = { showCaret: false, verticalSpacing: DEFAULT_VERTICAL_SPACING, small: false, + regular: false, onChange() {}, onFocus() {}, @@ -178,6 +180,7 @@ class DateInput extends React.Component { openDirection, verticalSpacing, small, + regular, styles, theme: { reactDates }, } = this.props; @@ -204,6 +207,7 @@ class DateInput extends React.Component { {...css( styles.DateInput_input, small && styles.DateInput_input__small, + regular && styles.DateInput_input__regular, readOnly && styles.DateInput_input__readOnly, focused && styles.DateInput_input__focused, disabled && styles.DateInput_input__disabled, @@ -316,6 +320,10 @@ export default withStyles(({ paddingRight: spacing.displayTextPaddingRight_small, }, + DateInput_input__regular: { + fontWeight: 'auto', + }, + DateInput_input__readOnly: { userSelect: 'none', }, diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 0b4484de24..b523a2c814 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -64,6 +64,7 @@ const defaultProps = { noBorder: false, block: false, small: false, + regular: false, // calendar presentation and interaction related props renderMonth: null, @@ -460,6 +461,7 @@ class DateRangePicker extends React.Component { block, verticalSpacing, small, + regular, styles, } = this.props; @@ -515,6 +517,7 @@ class DateRangePicker extends React.Component { noBorder={noBorder} block={block} small={small} + regular={regular} verticalSpacing={verticalSpacing} /> diff --git a/src/components/DateRangePickerInput.jsx b/src/components/DateRangePickerInput.jsx index ebf041c4d5..a07c9cf540 100644 --- a/src/components/DateRangePickerInput.jsx +++ b/src/components/DateRangePickerInput.jsx @@ -61,6 +61,7 @@ const propTypes = forbidExtraProps({ noBorder: PropTypes.bool, block: PropTypes.bool, small: PropTypes.bool, + regular: PropTypes.bool, verticalSpacing: nonNegativeInteger, // accessibility @@ -107,6 +108,7 @@ const defaultProps = { noBorder: false, block: false, small: false, + regular: false, verticalSpacing: undefined, // accessibility @@ -155,6 +157,7 @@ function DateRangePickerInput({ block, verticalSpacing, small, + regular, styles, }) { const calendarIcon = customInputIcon || ( @@ -230,6 +233,7 @@ function DateRangePickerInput({ onKeyDownQuestionMark={onKeyDownQuestionMark} verticalSpacing={verticalSpacing} small={small} + regular={regular} />
    {showClearDates && ( diff --git a/src/components/DateRangePickerInputController.jsx b/src/components/DateRangePickerInputController.jsx index 94e384b9ff..54062c5162 100644 --- a/src/components/DateRangePickerInputController.jsx +++ b/src/components/DateRangePickerInputController.jsx @@ -44,6 +44,7 @@ const propTypes = forbidExtraProps({ noBorder: PropTypes.bool, block: PropTypes.bool, small: PropTypes.bool, + regular: PropTypes.bool, verticalSpacing: nonNegativeInteger, keepOpenOnDateSelect: PropTypes.bool, @@ -95,6 +96,7 @@ const defaultProps = { noBorder: false, block: false, small: false, + regular: false, verticalSpacing: undefined, keepOpenOnDateSelect: false, @@ -273,6 +275,7 @@ export default class DateRangePickerInputController extends React.Component { noBorder, block, small, + regular, verticalSpacing, } = this.props; @@ -316,6 +319,7 @@ export default class DateRangePickerInputController extends React.Component { noBorder={noBorder} block={block} small={small} + regular={regular} verticalSpacing={verticalSpacing} /> ); diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index 58b79446b1..952cfcdcf7 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -60,6 +60,7 @@ const defaultProps = { noBorder: false, block: false, small: false, + regular: false, verticalSpacing: DEFAULT_VERTICAL_SPACING, // calendar presentation and interaction related props @@ -479,6 +480,7 @@ class SingleDatePicker extends React.Component { noBorder, block, small, + regular, verticalSpacing, styles, } = this.props; @@ -528,6 +530,7 @@ class SingleDatePicker extends React.Component { noBorder={noBorder} block={block} small={small} + regular={regular} verticalSpacing={verticalSpacing} /> diff --git a/src/components/SingleDatePickerInput.jsx b/src/components/SingleDatePickerInput.jsx index af362629f5..f68a371b03 100644 --- a/src/components/SingleDatePickerInput.jsx +++ b/src/components/SingleDatePickerInput.jsx @@ -37,6 +37,7 @@ const propTypes = forbidExtraProps({ noBorder: PropTypes.bool, block: PropTypes.bool, small: PropTypes.bool, + regular: PropTypes.bool, verticalSpacing: nonNegativeInteger, onChange: PropTypes.func, @@ -70,6 +71,8 @@ const defaultProps = { isRTL: false, noBorder: false, block: false, + small: false, + regular: false, verticalSpacing: undefined, onChange() {}, @@ -113,6 +116,7 @@ function SingleDatePickerInput({ noBorder, block, small, + regular, verticalSpacing, styles, }) { @@ -174,6 +178,7 @@ function SingleDatePickerInput({ openDirection={openDirection} verticalSpacing={verticalSpacing} small={small} + regular={regular} /> {showClearDate && ( diff --git a/src/shapes/DateRangePickerShape.js b/src/shapes/DateRangePickerShape.js index d1f0d9b2a7..4e713b1fbe 100644 --- a/src/shapes/DateRangePickerShape.js +++ b/src/shapes/DateRangePickerShape.js @@ -41,6 +41,7 @@ export default { noBorder: PropTypes.bool, block: PropTypes.bool, small: PropTypes.bool, + regular: PropTypes.bool, // calendar presentation and interaction related props renderMonth: PropTypes.func, diff --git a/src/shapes/SingleDatePickerShape.js b/src/shapes/SingleDatePickerShape.js index b464549d76..2361ad08eb 100644 --- a/src/shapes/SingleDatePickerShape.js +++ b/src/shapes/SingleDatePickerShape.js @@ -34,6 +34,7 @@ export default { noBorder: PropTypes.bool, block: PropTypes.bool, small: PropTypes.bool, + regular: PropTypes.bool, verticalSpacing: nonNegativeInteger, // calendar presentation and interaction related props diff --git a/stories/DateRangePicker_input.js b/stories/DateRangePicker_input.js index 420cdcea91..395c1a6971 100644 --- a/stories/DateRangePicker_input.js +++ b/stories/DateRangePicker_input.js @@ -142,4 +142,12 @@ storiesOf('DRP - Input Props', module) showClearDates small /> + )) + .addWithInfo('regular styling', () => ( + )); diff --git a/stories/SingleDatePicker_input.js b/stories/SingleDatePicker_input.js index f5e88b8c82..3e368f0835 100644 --- a/stories/SingleDatePicker_input.js +++ b/stories/SingleDatePicker_input.js @@ -91,4 +91,11 @@ storiesOf('SDP - Input Props', module) showClearDate small /> + )) + .addWithInfo('regular styling', () => ( + )); From d188c27f30870bd6e97561e205f0d64b808bab11 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Thu, 18 Jan 2018 16:44:14 -0800 Subject: [PATCH 074/618] Quick fixes for CustomizableCalendarDay --- src/components/CustomizableCalendarDay.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/CustomizableCalendarDay.jsx b/src/components/CustomizableCalendarDay.jsx index dbf45b51e3..5218b811a2 100644 --- a/src/components/CustomizableCalendarDay.jsx +++ b/src/components/CustomizableCalendarDay.jsx @@ -224,7 +224,7 @@ class CustomizableCalendarDay extends React.Component { styles.CalendarDay, useDefaultCursor && styles.CalendarDay__defaultCursor, daySizeStyles, - ...hasCustomStyles && [ + ...!!hasCustomStyles && [ defaultStyles, isOutsideDay && outsideStyles, modifiers.has('today') && todayStyles, @@ -253,6 +253,7 @@ class CustomizableCalendarDay extends React.Component { modifiers.has('last-in-range') && styles.CalendarDay__last_in_range, modifiers.has('selected-start') && styles.CalendarDay__selected_start, modifiers.has('selected-end') && styles.CalendarDay__selected_end, + selected && styles.CalendarDay__selected, isOutsideRange && (blockedOutOfRangeStyles || styles.CalendarDay__blocked_out_of_range), ], )} From bc338d248e79be4da0995c2264e13a105ccdbed1 Mon Sep 17 00:00:00 2001 From: Jake Clements Date: Fri, 19 Jan 2018 11:40:53 +1100 Subject: [PATCH 075/618] Add date-offset picker --- examples/DayPickerRangeControllerWrapper.jsx | 4 + src/components/CalendarDay.jsx | 7 ++ src/components/DayPickerRangeController.jsx | 104 +++++++++++++----- src/theme/DefaultTheme.js | 1 + src/utils/getSelectedDateOffset.js | 6 + stories/DayPickerRangeController.js | 35 ++++++ .../DayPickerRangeController_spec.jsx | 60 ++++++++++ test/utils/getSelectedDateOffset_spec.js | 32 ++++++ 8 files changed, 221 insertions(+), 28 deletions(-) create mode 100644 src/utils/getSelectedDateOffset.js create mode 100644 test/utils/getSelectedDateOffset_spec.js diff --git a/examples/DayPickerRangeControllerWrapper.jsx b/examples/DayPickerRangeControllerWrapper.jsx index 29e04b639f..16199d3c0c 100644 --- a/examples/DayPickerRangeControllerWrapper.jsx +++ b/examples/DayPickerRangeControllerWrapper.jsx @@ -18,6 +18,8 @@ const propTypes = forbidExtraProps({ autoFocusEndDate: PropTypes.bool, initialStartDate: momentPropTypes.momentObj, initialEndDate: momentPropTypes.momentObj, + startDateOffset: PropTypes.func, + endDateOffset: PropTypes.func, keepOpenOnDateSelect: PropTypes.bool, minimumNights: PropTypes.number, @@ -53,6 +55,8 @@ const defaultProps = { autoFocusEndDate: false, initialStartDate: null, initialEndDate: null, + startDateOffset: undefined, + endDateOffset: undefined, // day presentation and interaction related props renderCalendarDay: undefined, diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index f7a362af13..5ea590a222 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -129,6 +129,7 @@ class CalendarDay extends React.Component { styles.CalendarDay__default, isOutsideDay && styles.CalendarDay__outside, modifiers.has('today') && styles.CalendarDay__today, + modifiers.has('hovered-offset') && styles.CalendarDay__hovered_offset, modifiers.has('highlighted-calendar') && styles.CalendarDay__highlighted_calendar, modifiers.has('blocked-minimum-nights') && styles.CalendarDay__blocked_minimum_nights, modifiers.has('blocked-calendar') && styles.CalendarDay__blocked_calendar, @@ -189,6 +190,12 @@ export default withStyles(({ reactDates: { color, font } }) => ({ }, }, + CalendarDay__hovered_offset: { + background: color.core.borderBright, + border: `1px double ${color.core.borderLight}`, + color: 'inherit', + }, + CalendarDay__outside: { border: 0, diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 6204ac7e11..4272e36478 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -18,6 +18,8 @@ import isBeforeDay from '../utils/isBeforeDay'; import getVisibleDays from '../utils/getVisibleDays'; import isDayVisible from '../utils/isDayVisible'; +import getSelectedDateOffset from '../utils/getSelectedDateOffset'; + import toISODateString from '../utils/toISODateString'; import toISOMonthString from '../utils/toISOMonthString'; @@ -39,6 +41,8 @@ const propTypes = forbidExtraProps({ startDate: momentPropTypes.momentObj, endDate: momentPropTypes.momentObj, onDatesChange: PropTypes.func, + startDateOffset: PropTypes.func, + endDateOffset: PropTypes.func, focusedInput: FocusedInputShape, onFocusChange: PropTypes.func, @@ -92,6 +96,8 @@ const defaultProps = { startDate: undefined, // TODO: use null endDate: undefined, // TODO: use null onDatesChange() {}, + startDateOffset: undefined, + endDateOffset: undefined, focusedInput: null, onFocusChange() {}, @@ -170,6 +176,7 @@ export default class DayPickerRangeController extends React.Component { 'last-in-range': day => this.isLastInRange(day), hovered: day => this.isHovered(day), 'hovered-span': day => this.isInHoveredSpan(day), + 'hovered-offset': day => this.isInHoveredSpan(day), 'after-hovered-start': day => this.isDayAfterHoveredStartDate(day), }; @@ -423,6 +430,8 @@ export default class DayPickerRangeController extends React.Component { onFocusChange, onClose, onDatesChange, + startDateOffset, + endDateOffset, } = this.props; if (e) e.preventDefault(); @@ -430,7 +439,15 @@ export default class DayPickerRangeController extends React.Component { let { startDate, endDate } = this.props; - if (focusedInput === START_DATE) { + if (startDateOffset || endDateOffset) { + startDate = getSelectedDateOffset(startDateOffset, day); + endDate = getSelectedDateOffset(endDateOffset, day); + + if (!keepOpenOnDateSelect) { + onFocusChange(null); + onClose({ startDate, endDate }); + } + } else if (focusedInput === START_DATE) { onFocusChange(END_DATE); startDate = day; @@ -467,51 +484,78 @@ export default class DayPickerRangeController extends React.Component { endDate, focusedInput, minimumNights, + startDateOffset, + endDateOffset, } = this.props; const { hoverDate, visibleDays } = this.state; + let dateOffset = null; if (focusedInput) { + const hasOffset = startDateOffset || endDateOffset; let modifiers = {}; - modifiers = this.deleteModifier(modifiers, hoverDate, 'hovered'); - modifiers = this.addModifier(modifiers, day, 'hovered'); - if (startDate && !endDate && focusedInput === END_DATE) { - if (isAfterDay(hoverDate, startDate)) { - const endSpan = hoverDate.clone().add(1, 'day'); - modifiers = this.deleteModifierFromRange(modifiers, startDate, endSpan, 'hovered-span'); - } + if (hasOffset) { + const start = getSelectedDateOffset(startDateOffset, day); + const end = getSelectedDateOffset(endDateOffset, day, rangeDay => rangeDay.add(1, 'day')); - if (!this.isBlocked(day) && isAfterDay(day, startDate)) { - const endSpan = day.clone().add(1, 'day'); - modifiers = this.addModifierToRange(modifiers, startDate, endSpan, 'hovered-span'); + dateOffset = { + start, + end, + }; + + if (this.state.dateOffset && this.state.dateOffset.start && this.state.dateOffset.end) { + modifiers = this.deleteModifierFromRange(modifiers, this.state.dateOffset.start, this.state.dateOffset.end, 'hovered-offset'); } + modifiers = this.addModifierToRange(modifiers, start, end, 'hovered-offset'); } - if (!startDate && endDate && focusedInput === START_DATE) { - if (isBeforeDay(hoverDate, endDate)) { - modifiers = this.deleteModifierFromRange(modifiers, hoverDate, endDate, 'hovered-span'); + if (!hasOffset) { + modifiers = this.deleteModifier(modifiers, hoverDate, 'hovered'); + modifiers = this.addModifier(modifiers, day, 'hovered'); + + if (startDate && !endDate && focusedInput === END_DATE) { + if (isAfterDay(hoverDate, startDate)) { + const endSpan = hoverDate.clone().add(1, 'day'); + modifiers = this.deleteModifierFromRange(modifiers, startDate, endSpan, 'hovered-span'); + } + + if (!this.isBlocked(day) && isAfterDay(day, startDate)) { + const endSpan = day.clone().add(1, 'day'); + modifiers = this.addModifierToRange(modifiers, startDate, endSpan, 'hovered-span'); + } } - if (!this.isBlocked(day) && isBeforeDay(day, endDate)) { - modifiers = this.addModifierToRange(modifiers, day, endDate, 'hovered-span'); + if (!startDate && endDate && focusedInput === START_DATE) { + if (isBeforeDay(hoverDate, endDate)) { + modifiers = this.deleteModifierFromRange(modifiers, hoverDate, endDate, 'hovered-span'); + } + + if (!this.isBlocked(day) && isBeforeDay(day, endDate)) { + modifiers = this.addModifierToRange(modifiers, day, endDate, 'hovered-span'); + } } - } - if (startDate) { - if (isSameDay(day, startDate)) { - const newStartSpan = startDate.clone().add(1, 'day'); - const newEndSpan = startDate.clone().add(minimumNights + 1, 'days'); - modifiers = this.addModifierToRange( - modifiers, - newStartSpan, - newEndSpan, - 'after-hovered-start', - ); + if (startDate) { + const startSpan = startDate.clone().add(1, 'day'); + const endSpan = startDate.clone().add(minimumNights + 1, 'days'); + modifiers = this.deleteModifierFromRange(modifiers, startSpan, endSpan, 'after-hovered-start'); + + if (isSameDay(day, startDate)) { + const newStartSpan = startDate.clone().add(1, 'day'); + const newEndSpan = startDate.clone().add(minimumNights + 1, 'days'); + modifiers = this.addModifierToRange( + modifiers, + newStartSpan, + newEndSpan, + 'after-hovered-start', + ); + } } } this.setState({ hoverDate: day, + dateOffset, visibleDays: { ...visibleDays, ...modifiers, @@ -522,12 +566,16 @@ export default class DayPickerRangeController extends React.Component { onDayMouseLeave(day) { const { startDate, endDate, minimumNights } = this.props; - const { hoverDate, visibleDays } = this.state; + const { hoverDate, visibleDays, dateOffset } = this.state; if (this.isTouchDevice || !hoverDate) return; let modifiers = {}; modifiers = this.deleteModifier(modifiers, hoverDate, 'hovered'); + if (dateOffset) { + modifiers = this.deleteModifierFromRange(modifiers, this.state.dateOffset.start, this.state.dateOffset.end, 'hovered-offset'); + } + if (startDate && !endDate && isAfterDay(hoverDate, startDate)) { const endSpan = hoverDate.clone().add(1, 'day'); modifiers = this.deleteModifierFromRange(modifiers, startDate, endSpan, 'hovered-span'); diff --git a/src/theme/DefaultTheme.js b/src/theme/DefaultTheme.js index d2e278aeac..81216ac40a 100644 --- a/src/theme/DefaultTheme.js +++ b/src/theme/DefaultTheme.js @@ -9,6 +9,7 @@ const core = { border: '#dbdbdb', borderLight: '#e4e7e7', borderLighter: '#eceeee', + borderBright: '#f4f5f5', primary: '#00a699', primaryShade_1: '#33dacd', diff --git a/src/utils/getSelectedDateOffset.js b/src/utils/getSelectedDateOffset.js new file mode 100644 index 0000000000..6200f9b65d --- /dev/null +++ b/src/utils/getSelectedDateOffset.js @@ -0,0 +1,6 @@ +const defaultModifier = day => day; + +export default function getSelectedDateOffset(fn, day, modifier = defaultModifier) { + if (!fn) return day; + return modifier(fn(day.clone())); +} diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index ca1c7fb446..657e78c57b 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -86,6 +86,41 @@ storiesOf('DayPickerRangeController', module) onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} /> )) + .addWithInfo('with 7 days range selection', () => ( + day.subtract(3, 'days')} + endDateOffset={day => day.add(3, 'days')} + /> + )) + .addWithInfo('with 45 days range selection', () => ( + day.subtract(22, 'days')} + endDateOffset={day => day.add(22, 'days')} + /> + )) + .addWithInfo('with 4 days after today range selection', () => ( + day.add(4, 'days')} + /> + )) + .addWithInfo('with current week range selection', () => ( + day.startOf('week')} + endDateOffset={day => day.endOf('week')} + /> + )) .addWithInfo('with custom inputs', () => ( { ); }); }); + + describe('props.startDateOffset / props.endDateOffset', () => { + it('calls props.onDatesChange with startDate === startDateOffset(date) and endDate === endDateOffset(date)', () => { + const clickDate = moment(today).clone().add(2, 'days'); + const onDatesChangeStub = sinon.stub(); + const wrapper = shallow(( + day.subtract(2, 'days')} + endDateOffset={day => day.add(4, 'days')} + /> + )); + wrapper.instance().onDayClick(clickDate); + const args = onDatesChangeStub.getCall(0).args[0]; + expect(args.startDate.format()).to.equal(clickDate.clone().subtract(2, 'days').format()); + expect(args.endDate.format()).to.equal(clickDate.clone().add(4, 'days').format()); + }); + + it('calls props.onDatesChange with startDate === startDateOffset(date) and endDate === selectedDate when endDateOffset not provided', () => { + const clickDate = moment(today).clone().add(2, 'days'); + const onDatesChangeStub = sinon.stub(); + const wrapper = shallow(( + day.subtract(5, 'days')} + /> + )); + wrapper.instance().onDayClick(clickDate); + const args = onDatesChangeStub.getCall(0).args[0]; + expect(args.startDate.format()).to.equal(clickDate.clone().subtract(5, 'days').format()); + expect(args.endDate.format()).to.equal(clickDate.format()); + }); + + it('calls props.onDatesChange with startDate === selectedDate and endDate === endDateOffset(date) when startDateOffset not provided', () => { + const clickDate = moment(today).clone().add(12, 'days'); + const onDatesChangeStub = sinon.stub(); + const wrapper = shallow(( + day.add(12, 'days')} + /> + )); + wrapper.instance().onDayClick(clickDate); + const args = onDatesChangeStub.getCall(0).args[0]; + expect(args.startDate.format()).to.equal(clickDate.format()); + expect(args.endDate.format()).to.equal(clickDate.clone().add(12, 'days').format()); + }); + }); }); describe('#onDayMouseEnter', () => { @@ -1485,6 +1533,18 @@ describe('DayPickerRangeController', () => { expect(wrapper.state().hoverDate).to.equal(today); }); + it('sets state.dateOffset to the start and end date range when range included', () => { + const wrapper = shallow(( + day.add(2, 'days')} + /> + )); + wrapper.instance().onDayMouseEnter(today); + expect(wrapper.state().dateOffset.start.format()).to.equal(today.format()); + expect(wrapper.state().dateOffset.end.format()).to.equal(today.clone().add(3, 'days').format()); + }); + describe('modifiers', () => { it('calls addModifier', () => { const addModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'addModifier'); diff --git a/test/utils/getSelectedDateOffset_spec.js b/test/utils/getSelectedDateOffset_spec.js new file mode 100644 index 0000000000..dfaff4b2c7 --- /dev/null +++ b/test/utils/getSelectedDateOffset_spec.js @@ -0,0 +1,32 @@ +import { expect } from 'chai'; +import moment from 'moment'; + +import getSelectedDateOffset from '../../src/utils/getSelectedDateOffset'; + +const today = moment(); + +describe('#getSelectedDateOffset', () => { + it('returns a function modified moment object', () => { + const fn = day => day.add(2, 'days'); + const modifiedDay = getSelectedDateOffset(fn, today); + expect(modifiedDay.format()).to.equal(today.clone().add(2, 'days').format()); + }); + + it('returns the passed day when function is undefined', () => { + const modifiedDay = getSelectedDateOffset(undefined, today); + expect(modifiedDay.format()).to.equal(today.format()); + }); + + it('modifies the returned day using the modifier callback', () => { + const fn = day => day.add(2, 'days'); + const modifier = day => day.subtract(2, 'days'); + const modifiedDay = getSelectedDateOffset(fn, today, modifier); + expect(modifiedDay.format()).to.equal(today.clone().format()); + }); + + it('does not apply the modifier if function is undefined', () => { + const modifier = day => day.subtract(2, 'days'); + const modifiedDay = getSelectedDateOffset(undefined, today, modifier); + expect(modifiedDay.format()).to.equal(today.format()); + }); +}); From bd5ca6c361066cc702a3a006458fcfdf8a836e97 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Thu, 18 Jan 2018 17:07:53 -0800 Subject: [PATCH 076/618] Version 16.1.1 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0624ccf3ef..4dc1349498 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Change Log +## 16.1.1 +- [fix] Address some small bugs with `CustomizableCalendarDay` ([#962](https://github.com/airbnb/react-dates/pull/962)) + ## 16.1.0 - [fix] Allow for changing of the input value via highlight and replace ([#955](https://github.com/airbnb/react-dates/pull/955)) - [fix] Fix OPEN_UP styling ([#925](https://github.com/airbnb/react-dates/pull/925)) diff --git a/package.json b/package.json index 21691b92d8..005f3d2749 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "16.1.0", + "version": "16.1.1", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From 285033d793bcb58d8c3d8acd8025369088b551fb Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Fri, 19 Jan 2018 13:14:08 -0800 Subject: [PATCH 077/618] Move all styles in the CCD (custom and default) to rely on custom styles --- src/components/CustomizableCalendarDay.jsx | 324 ++++++++------------- 1 file changed, 120 insertions(+), 204 deletions(-) diff --git a/src/components/CustomizableCalendarDay.jsx b/src/components/CustomizableCalendarDay.jsx index 5218b811a2..8f19902f7c 100644 --- a/src/components/CustomizableCalendarDay.jsx +++ b/src/components/CustomizableCalendarDay.jsx @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import shallowCompare from 'react-addons-shallow-compare'; import momentPropTypes from 'react-moment-proptypes'; -import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types'; +import { forbidExtraProps, nonNegativeInteger, or } from 'airbnb-prop-types'; import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; import moment from 'moment'; @@ -11,6 +11,9 @@ import getPhrasePropTypes from '../utils/getPhrasePropTypes'; import getCalendarDaySettings from '../utils/getCalendarDaySettings'; import { DAY_SIZE } from '../constants'; +import DefaultTheme from '../theme/DefaultTheme'; + +const { reactDates: { color } } = DefaultTheme; function getStyles(stylesObj, isHovered) { if (!stylesObj) return null; @@ -25,12 +28,12 @@ function getStyles(stylesObj, isHovered) { const DayStyleShape = PropTypes.shape({ background: PropTypes.string, - border: PropTypes.string, + border: or([PropTypes.string, PropTypes.number]), color: PropTypes.string, hover: PropTypes.shape({ background: PropTypes.string, - border: PropTypes.string, + border: or([PropTypes.string, PropTypes.number]), color: PropTypes.string, }), }); @@ -82,6 +85,105 @@ const defaultProps = { renderDayContents: null, ariaLabelFormat: 'dddd, LL', + // style defaults + defaultStyles: { + border: `1px solid ${color.core.borderLight}`, + color: color.text, + background: color.background, + + hover: { + background: color.core.borderLight, + border: `1px double ${color.core.borderLight}`, + color: 'inherit', + }, + }, + outsideStyles: { + background: color.outside.backgroundColor, + border: 0, + color: color.outside.color, + }, + todayStyles: {}, + highlightedCalendarStyles: { + background: color.highlighted.backgroundColor, + color: color.highlighted.color, + + hover: { + background: color.highlighted.backgroundColor_hover, + color: color.highlighted.color_active, + }, + }, + blockedMinNightsStyles: { + background: color.minimumNights.backgroundColor, + border: `1px solid ${color.minimumNights.borderColor}`, + color: color.minimumNights.color, + + hover: { + background: color.minimumNights.backgroundColor_hover, + color: color.minimumNights.color_active, + }, + }, + blockedCalendarStyles: { + background: color.blocked_calendar.backgroundColor, + border: `1px solid ${color.blocked_calendar.borderColor}`, + color: color.blocked_calendar.color, + + hover: { + background: color.blocked_calendar.backgroundColor_hover, + border: `1px solid ${color.blocked_calendar.borderColor}`, + color: color.blocked_calendar.color_active, + }, + }, + blockedOutOfRangeStyles: { + background: color.blocked_out_of_range.backgroundColor, + border: `1px solid ${color.blocked_out_of_range.borderColor}`, + color: color.blocked_out_of_range.color, + + hover: { + background: color.blocked_out_of_range.backgroundColor_hover, + border: `1px solid ${color.blocked_out_of_range.borderColor}`, + color: color.blocked_out_of_range.color_active, + }, + }, + hoveredSpanStyles: { + background: color.hoveredSpan.backgroundColor, + border: `1px solid ${color.hoveredSpan.borderColor}`, + color: color.hoveredSpan.color, + + hover: { + background: color.hoveredSpan.backgroundColor_hover, + border: `1px solid ${color.hoveredSpan.borderColor}`, + color: color.hoveredSpan.color_active, + }, + }, + selectedSpanStyles: { + background: color.selectedSpan.backgroundColor, + border: `1px solid ${color.selectedSpan.borderColor}`, + color: color.selectedSpan.color, + + hover: { + background: color.selectedSpan.backgroundColor_hover, + border: `1px solid ${color.selectedSpan.borderColor}`, + color: color.selectedSpan.color_active, + }, + }, + lastInRangeStyles: { + borderRight: color.core.primary, + }, + selectedStartStyles: {}, + selectedEndStyles: {}, + selectedStyles: { + background: color.selected.backgroundColor, + border: `1px solid ${color.selected.borderColor}`, + color: color.selected.color, + + hover: { + background: color.selected.backgroundColor_hover, + border: `1px solid ${color.selected.borderColor}`, + color: color.selected.color_active, + }, + }, + afterHoveredStartStyles: {}, + // internationalization phrases: CalendarDayPhrases, }; @@ -198,64 +300,26 @@ class CustomizableCalendarDay extends React.Component { const selectedStyles = getStyles(selectedStylesWithHover, isHovered); const afterHoveredStartStyles = getStyles(afterHoveredStartStylesWithHover, isHovered); - const hasCustomSelectedStyles = - defaultStyles || - (selected && selectedStyles) || - (modifiers.has('selected-start') && selectedStartStyles) || - (modifiers.has('selected-end') && selectedEndStyles); - const hasCustomHoveredStyles = - (modifiers.has('hovered-span') && hoveredSpanStyles) || - (modifiers.has('after-hovered-start') && afterHoveredStartStyles); - const hasCustomStyles = - (isOutsideDay && outsideStyles) || - (modifiers.has('today') && todayStyles) || - (modifiers.has('highlighted-calendar') && highlightedCalendarStyles) || - (modifiers.has('blocked-minimum-nights') && blockedMinNightsStyles) || - (modifiers.has('blocked-calendar') && blockedCalendarStyles) || - (modifiers.has('last-in-range') && lastInRangeStyles) || - (modifiers.has('selected-span') && selectedSpanStyles) || - (isOutsideRange && blockedOutOfRangeStyles) || - hasCustomSelectedStyles || - hasCustomHoveredStyles; - return ( ({ +export default withStyles(({ reactDates: { font } }) => ({ CalendarDay: { boxSizing: 'border-box', cursor: 'pointer', @@ -292,152 +356,4 @@ export default withStyles(({ reactDates: { color, font } }) => ({ CalendarDay__defaultCursor: { cursor: 'default', }, - - CalendarDay__default: { - border: `1px solid ${color.core.borderLight}`, - color: color.text, - background: color.background, - - ':hover': { - background: color.core.borderLight, - border: `1px double ${color.core.borderLight}`, - color: 'inherit', - }, - }, - - CalendarDay__outside: { - border: 0, - - background: color.outside.backgroundColor, - color: color.outside.color, - }, - - CalendarDay__blocked_minimum_nights: { - background: color.minimumNights.backgroundColor, - border: `1px solid ${color.minimumNights.borderColor}`, - color: color.minimumNights.color, - - ':hover': { - background: color.minimumNights.backgroundColor_hover, - color: color.minimumNights.color_active, - }, - - ':active': { - background: color.minimumNights.backgroundColor_active, - color: color.minimumNights.color_active, - }, - }, - - CalendarDay__highlighted_calendar: { - background: color.highlighted.backgroundColor, - color: color.highlighted.color, - - ':hover': { - background: color.highlighted.backgroundColor_hover, - color: color.highlighted.color_active, - }, - - ':active': { - background: color.highlighted.backgroundColor_active, - color: color.highlighted.color_active, - }, - }, - - CalendarDay__selected_span: { - background: color.selectedSpan.backgroundColor, - border: `1px solid ${color.selectedSpan.borderColor}`, - color: color.selectedSpan.color, - - ':hover': { - background: color.selectedSpan.backgroundColor_hover, - border: `1px solid ${color.selectedSpan.borderColor}`, - color: color.selectedSpan.color_active, - }, - - ':active': { - background: color.selectedSpan.backgroundColor_active, - border: `1px solid ${color.selectedSpan.borderColor}`, - color: color.selectedSpan.color_active, - }, - }, - - CalendarDay__last_in_range: { - borderRight: color.core.primary, - }, - - CalendarDay__selected: { - background: color.selected.backgroundColor, - border: `1px solid ${color.selected.borderColor}`, - color: color.selected.color, - - ':hover': { - background: color.selected.backgroundColor_hover, - border: `1px solid ${color.selected.borderColor}`, - color: color.selected.color_active, - }, - - ':active': { - background: color.selected.backgroundColor_active, - border: `1px solid ${color.selected.borderColor}`, - color: color.selected.color_active, - }, - }, - - CalendarDay__hovered_span: { - background: color.hoveredSpan.backgroundColor, - border: `1px solid ${color.hoveredSpan.borderColor}`, - color: color.hoveredSpan.color, - - ':hover': { - background: color.hoveredSpan.backgroundColor_hover, - border: `1px solid ${color.hoveredSpan.borderColor}`, - color: color.hoveredSpan.color_active, - }, - - ':active': { - background: color.hoveredSpan.backgroundColor_active, - border: `1px solid ${color.hoveredSpan.borderColor}`, - color: color.hoveredSpan.color_active, - }, - }, - - CalendarDay__blocked_calendar: { - background: color.blocked_calendar.backgroundColor, - border: `1px solid ${color.blocked_calendar.borderColor}`, - color: color.blocked_calendar.color, - - ':hover': { - background: color.blocked_calendar.backgroundColor_hover, - border: `1px solid ${color.blocked_calendar.borderColor}`, - color: color.blocked_calendar.color_active, - }, - - ':active': { - background: color.blocked_calendar.backgroundColor_active, - border: `1px solid ${color.blocked_calendar.borderColor}`, - color: color.blocked_calendar.color_active, - }, - }, - - CalendarDay__blocked_out_of_range: { - background: color.blocked_out_of_range.backgroundColor, - border: `1px solid ${color.blocked_out_of_range.borderColor}`, - color: color.blocked_out_of_range.color, - - ':hover': { - background: color.blocked_out_of_range.backgroundColor_hover, - border: `1px solid ${color.blocked_out_of_range.borderColor}`, - color: color.blocked_out_of_range.color_active, - }, - - ':active': { - background: color.blocked_out_of_range.backgroundColor_active, - border: `1px solid ${color.blocked_out_of_range.borderColor}`, - color: color.blocked_out_of_range.color_active, - }, - }, - - CalendarDay__selected_start: {}, - CalendarDay__selected_end: {}, - CalendarDay__today: {}, }))(CustomizableCalendarDay); From ccd0d20f3f3549e3218451c06d5836417bd89ee0 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Fri, 19 Jan 2018 14:06:18 -0800 Subject: [PATCH 078/618] Version 16.2.0 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dc1349498..24a0e3cb10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 16.2.0 +- [new] Add `startDateOffset`/`endDateOffset` props to `DayPickerRangeController` ([#884](https://github.com/airbnb/react-dates/pull/884)) +- [fix] Make all styles inline in `CustomizableCalendarDay` ([#964](https://github.com/airbnb/react-dates/pull/964)) + ## 16.1.1 - [fix] Address some small bugs with `CustomizableCalendarDay` ([#962](https://github.com/airbnb/react-dates/pull/962)) diff --git a/package.json b/package.json index 005f3d2749..6f9b923674 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "16.1.1", + "version": "16.2.0", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From 700557ef7243b1eb7d9280dd5632340a3e3b8a50 Mon Sep 17 00:00:00 2001 From: Comron Sattari Date: Wed, 17 Jan 2018 15:39:29 -0800 Subject: [PATCH 079/618] Add keepFocusOnInput prop to maintain input focus even on touch devices --- examples/SingleDatePickerWrapper.jsx | 1 + src/components/DateRangePicker.jsx | 12 +++++-- src/components/SingleDatePicker.jsx | 6 +++- src/shapes/DateRangePickerShape.js | 1 + src/shapes/SingleDatePickerShape.js | 1 + test/components/DateRangePicker_spec.jsx | 44 ++++++++++++++++++++++- test/components/SingleDatePicker_spec.jsx | 41 ++++++++++++++++++++- 7 files changed, 101 insertions(+), 5 deletions(-) diff --git a/examples/SingleDatePickerWrapper.jsx b/examples/SingleDatePickerWrapper.jsx index 4c3d91ec32..9e65e6ef16 100644 --- a/examples/SingleDatePickerWrapper.jsx +++ b/examples/SingleDatePickerWrapper.jsx @@ -42,6 +42,7 @@ const defaultProps = { small: false, regular: false, verticalSpacing: undefined, + keepFocusOnInput: false, // calendar presentation and interaction related props renderMonth: null, diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index b523a2c814..967df454e2 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -65,6 +65,7 @@ const defaultProps = { block: false, small: false, regular: false, + keepFocusOnInput: false, // calendar presentation and interaction related props renderMonth: null, @@ -188,10 +189,17 @@ class DateRangePicker extends React.Component { } onDateRangePickerInputFocus(focusedInput) { - const { onFocusChange, withPortal, withFullScreenPortal } = this.props; + const { + onFocusChange, + withPortal, + withFullScreenPortal, + keepFocusOnInput, + } = this.props; if (focusedInput) { - const moveFocusToDayPicker = withPortal || withFullScreenPortal || this.isTouchDevice; + const withAnyPortal = withPortal || withFullScreenPortal; + const moveFocusToDayPicker = withAnyPortal || (this.isTouchDevice && !keepFocusOnInput); + if (moveFocusToDayPicker) { this.onDayPickerFocus(); } else { diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index 952cfcdcf7..c2f37aad1d 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -62,6 +62,7 @@ const defaultProps = { small: false, regular: false, verticalSpacing: DEFAULT_VERTICAL_SPACING, + keepFocusOnInput: false, // calendar presentation and interaction related props orientation: HORIZONTAL_ORIENTATION, @@ -193,9 +194,12 @@ class SingleDatePicker extends React.Component { onFocusChange, withPortal, withFullScreenPortal, + keepFocusOnInput, } = this.props; - const moveFocusToDayPicker = withPortal || withFullScreenPortal || this.isTouchDevice; + const withAnyPortal = withPortal || withFullScreenPortal; + const moveFocusToDayPicker = withAnyPortal || (this.isTouchDevice && !keepFocusOnInput); + if (moveFocusToDayPicker) { this.onDayPickerFocus(); } else { diff --git a/src/shapes/DateRangePickerShape.js b/src/shapes/DateRangePickerShape.js index 4e713b1fbe..5c7ff7f945 100644 --- a/src/shapes/DateRangePickerShape.js +++ b/src/shapes/DateRangePickerShape.js @@ -42,6 +42,7 @@ export default { block: PropTypes.bool, small: PropTypes.bool, regular: PropTypes.bool, + keepFocusOnInput: PropTypes.bool, // calendar presentation and interaction related props renderMonth: PropTypes.func, diff --git a/src/shapes/SingleDatePickerShape.js b/src/shapes/SingleDatePickerShape.js index 2361ad08eb..2adfb77175 100644 --- a/src/shapes/SingleDatePickerShape.js +++ b/src/shapes/SingleDatePickerShape.js @@ -36,6 +36,7 @@ export default { small: PropTypes.bool, regular: PropTypes.bool, verticalSpacing: nonNegativeInteger, + keepFocusOnInput: PropTypes.bool, // calendar presentation and interaction related props renderMonth: PropTypes.func, diff --git a/test/components/DateRangePicker_spec.jsx b/test/components/DateRangePicker_spec.jsx index 382aabc032..46f813a42b 100644 --- a/test/components/DateRangePicker_spec.jsx +++ b/test/components/DateRangePicker_spec.jsx @@ -263,8 +263,10 @@ describe('DateRangePicker', () => { describe('new focusedInput is truthy', () => { let onDayPickerFocusSpy; + let onDayPickerBlurSpy; beforeEach(() => { onDayPickerFocusSpy = sinon.spy(PureDateRangePicker.prototype, 'onDayPickerFocus'); + onDayPickerBlurSpy = sinon.spy(PureDateRangePicker.prototype, 'onDayPickerBlur'); }); afterEach(() => { @@ -297,8 +299,48 @@ describe('DateRangePicker', () => { expect(onDayPickerFocusSpy.callCount).to.equal(1); }); + it('calls onDayPickerFocus if focusedInput and isTouchDevice', () => { + const wrapper = shallow(( + + )).dive(); + wrapper.instance().isTouchDevice = true; + wrapper.instance().onDateRangePickerInputFocus(START_DATE); + expect(onDayPickerFocusSpy.callCount).to.equal(1); + }); + + it('calls onDayPickerBlur if focusedInput and !withPortal/!withFullScreenPortal and keepFocusOnInput', () => { + const wrapper = shallow(( + + )).dive(); + wrapper.instance().isTouchDevice = true; + wrapper.instance().onDateRangePickerInputFocus(START_DATE); + expect(onDayPickerBlurSpy.callCount).to.equal(1); + }); + + it('calls onDayPickerFocus if focusedInput and withPortal/withFullScreenPortal and keepFocusOnInput', () => { + const wrapper = shallow(( + + )).dive(); + wrapper.instance().onDateRangePickerInputFocus(START_DATE); + expect(onDayPickerFocusSpy.callCount).to.equal(1); + }); + it('calls onDayPickerBlur if focusedInput and !withPortal/!withFullScreenPortal', () => { - const onDayPickerBlurSpy = sinon.spy(PureDateRangePicker.prototype, 'onDayPickerBlur'); const wrapper = shallow(( { describe('#onFocus', () => { let onDayPickerFocusSpy; + let onDayPickerBlurSpy; beforeEach(() => { onDayPickerFocusSpy = sinon.spy(PureSingleDatePicker.prototype, 'onDayPickerFocus'); + onDayPickerBlurSpy = sinon.spy(PureSingleDatePicker.prototype, 'onDayPickerBlur'); }); it('calls props.onFocusChange once', () => { @@ -415,8 +417,45 @@ describe('SingleDatePicker', () => { expect(onDayPickerFocusSpy.callCount).to.equal(1); }); + it('calls onDayPickerFocus if isTouchDevice', () => { + const wrapper = shallow(( + + )).dive(); + wrapper.instance().isTouchDevice = true; + wrapper.instance().onFocus(); + expect(onDayPickerFocusSpy.callCount).to.equal(1); + }); + + it('calls onDayPickerBlur if !withPortal/!withFullScreenPortal and keepFocusOnInput', () => { + const wrapper = shallow(( + + )).dive(); + wrapper.instance().isTouchDevice = true; + wrapper.instance().onFocus(); + expect(onDayPickerBlurSpy.callCount).to.equal(1); + }); + + it('calls onDayPickerFocus if withPortal/withFullScreenPortal and keepFocusOnInput', () => { + const wrapper = shallow(( + + )).dive(); + wrapper.instance().onFocus(); + expect(onDayPickerFocusSpy.callCount).to.equal(1); + }); + it('calls onDayPickerBlur if !withPortal/!withFullScreenPortal', () => { - const onDayPickerBlurSpy = sinon.spy(PureSingleDatePicker.prototype, 'onDayPickerBlur'); const wrapper = shallow(( Date: Mon, 22 Jan 2018 17:39:28 -0800 Subject: [PATCH 080/618] Modify block styling to mean full width on the SingleDatePicker --- src/components/DateInput.jsx | 8 ++++++++ src/components/SingleDatePickerInput.jsx | 1 + 2 files changed, 9 insertions(+) diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index 10bf83f714..6056e05867 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -35,6 +35,7 @@ const propTypes = forbidExtraProps({ showCaret: PropTypes.bool, verticalSpacing: nonNegativeInteger, small: PropTypes.bool, + block: PropTypes.bool, regular: PropTypes.bool, onChange: PropTypes.func, @@ -61,6 +62,7 @@ const defaultProps = { showCaret: false, verticalSpacing: DEFAULT_VERTICAL_SPACING, small: false, + block: false, regular: false, onChange() {}, @@ -181,6 +183,7 @@ class DateInput extends React.Component { verticalSpacing, small, regular, + block, styles, theme: { reactDates }, } = this.props; @@ -197,6 +200,7 @@ class DateInput extends React.Component { {...css( styles.DateInput, small && styles.DateInput__small, + block && styles.DateInput__block, withFang && styles.DateInput__withFang, disabled && styles.DateInput__disabled, withFang && openDirection === OPEN_DOWN && styles.DateInput__openDown, @@ -286,6 +290,10 @@ export default withStyles(({ width: sizing.inputWidth_small, }, + DateInput__block: { + width: '100%', + }, + DateInput__disabled: { background: color.disabled, color: color.textDisabled, diff --git a/src/components/SingleDatePickerInput.jsx b/src/components/SingleDatePickerInput.jsx index f68a371b03..ac62a00e12 100644 --- a/src/components/SingleDatePickerInput.jsx +++ b/src/components/SingleDatePickerInput.jsx @@ -179,6 +179,7 @@ function SingleDatePickerInput({ verticalSpacing={verticalSpacing} small={small} regular={regular} + block={block} /> {showClearDate && ( From fea1be8d4b3f40ecbdf43f88d6c2ca5a3bbcc26b Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Wed, 24 Jan 2018 10:54:45 -0800 Subject: [PATCH 081/618] Version 16.2.1 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24a0e3cb10..45d8629ae1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Change Log +## 16.2.1 +- [fix] SDP `block` styling also makes the input full width ([#972](https://github.com/airbnb/react-dates/pull/972)) + ## 16.2.0 - [new] Add `startDateOffset`/`endDateOffset` props to `DayPickerRangeController` ([#884](https://github.com/airbnb/react-dates/pull/884)) - [fix] Make all styles inline in `CustomizableCalendarDay` ([#964](https://github.com/airbnb/react-dates/pull/964)) diff --git a/package.json b/package.json index 6f9b923674..78252ca51f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "16.2.0", + "version": "16.2.1", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From 0571caa357337a5031bb460b49aecfc96f37ea7a Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Wed, 24 Jan 2018 22:16:30 -0800 Subject: [PATCH 082/618] Fix specificity issues with selected styles on the CustomizableCalendarDay --- src/components/CustomizableCalendarDay.jsx | 10 +++++----- stories/DateRangePicker_day.js | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/components/CustomizableCalendarDay.jsx b/src/components/CustomizableCalendarDay.jsx index 8f19902f7c..2d85acb539 100644 --- a/src/components/CustomizableCalendarDay.jsx +++ b/src/components/CustomizableCalendarDay.jsx @@ -63,9 +63,9 @@ const propTypes = forbidExtraProps({ hoveredSpanStyles: DayStyleShape, selectedSpanStyles: DayStyleShape, lastInRangeStyles: DayStyleShape, + selectedStyles: DayStyleShape, selectedStartStyles: DayStyleShape, selectedEndStyles: DayStyleShape, - selectedStyles: DayStyleShape, afterHoveredStartStyles: DayStyleShape, // internationalization @@ -169,8 +169,6 @@ const defaultProps = { lastInRangeStyles: { borderRight: color.core.primary, }, - selectedStartStyles: {}, - selectedEndStyles: {}, selectedStyles: { background: color.selected.backgroundColor, border: `1px solid ${color.selected.borderColor}`, @@ -182,6 +180,8 @@ const defaultProps = { color: color.selected.color_active, }, }, + selectedStartStyles: {}, + selectedEndStyles: {}, afterHoveredStartStyles: {}, // internationalization @@ -266,9 +266,9 @@ class CustomizableCalendarDay extends React.Component { hoveredSpanStyles: hoveredSpanStylesWithHover, selectedSpanStyles: selectedSpanStylesWithHover, lastInRangeStyles: lastInRangeStylesWithHover, + selectedStyles: selectedStylesWithHover, selectedStartStyles: selectedStartStylesWithHover, selectedEndStyles: selectedEndStylesWithHover, - selectedStyles: selectedStylesWithHover, afterHoveredStartStyles: afterHoveredStartStylesWithHover, } = this.props; @@ -316,9 +316,9 @@ class CustomizableCalendarDay extends React.Component { modifiers.has('after-hovered-start') && afterHoveredStartStyles, modifiers.has('selected-span') && selectedSpanStyles, modifiers.has('last-in-range') && lastInRangeStyles, + selected && selectedStyles, modifiers.has('selected-start') && selectedStartStyles, modifiers.has('selected-end') && selectedEndStyles, - selected && selectedStyles, isOutsideRange && blockedOutOfRangeStyles, )} role="button" // eslint-disable-line jsx-a11y/no-noninteractive-element-to-interactive-role diff --git a/stories/DateRangePicker_day.js b/stories/DateRangePicker_day.js index abf69d78b1..218b10fe6c 100644 --- a/stories/DateRangePicker_day.js +++ b/stories/DateRangePicker_day.js @@ -38,11 +38,26 @@ const hoveredStyles = { color: '#fff', }; +const blockedStyles = { + background: '#fff', + border: '1px double #e4e7e7', + color: '#dce0e0', + + hover: { + background: '#fff', + border: '1px double #e4e7e7', + color: '#dce0e0', + }, +}; + const customDayStyles = { selectedStartStyles: selectedStyles, selectedEndStyles: selectedStyles, hoveredSpanStyles: hoveredStyles, afterHoveredStartStyles: hoveredStyles, + blockedMinNightsStyles: blockedStyles, + blockedCalendarStyles: blockedStyles, + blockedOutOfRangeStyles: blockedStyles, selectedSpanStyles: { background: '#9b32a2', @@ -116,6 +131,7 @@ storiesOf('DRP - Day Props', module) )) .addWithInfo('one-off custom styling', () => ( } autoFocus /> From 625b5d96b8049176065a860b1abd12d324dbe6a3 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Fri, 26 Jan 2018 21:35:57 +0000 Subject: [PATCH 083/618] chore(package): update style-loader to version 0.20.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 78252ca51f..da5cc77ff0 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "sass-loader": "^6.0.6", "sinon": "^4.1.4", "sinon-sandbox": "^1.0.2", - "style-loader": "^0.19.0", + "style-loader": "^0.20.0", "webpack": "^2.6.1" }, "dependencies": { From 36a8998a119153b6e197aa92ea6570e7482fc63a Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Fri, 26 Jan 2018 16:17:48 -0800 Subject: [PATCH 084/618] Add modifiers for firstDayOfWeek and lastDayOfWeek --- src/components/CalendarDay.jsx | 4 +++ src/components/CustomizableCalendarDay.jsx | 10 ++++++ src/components/DayPickerRangeController.jsx | 12 +++++++ .../DayPickerSingleDateController.jsx | 12 +++++++ .../DayPickerRangeController_spec.jsx | 36 +++++++++++++++++++ .../DayPickerSingleDateController_spec.jsx | 36 +++++++++++++++++++ 6 files changed, 110 insertions(+) diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index 5ea590a222..1d55754444 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -129,6 +129,8 @@ class CalendarDay extends React.Component { styles.CalendarDay__default, isOutsideDay && styles.CalendarDay__outside, modifiers.has('today') && styles.CalendarDay__today, + modifiers.has('first-day-of-week') && styles.CalendarDay__firstDayOfWeek, + modifiers.has('last-day-of-week') && styles.CalendarDay__lastDayOfWeek, modifiers.has('hovered-offset') && styles.CalendarDay__hovered_offset, modifiers.has('highlighted-calendar') && styles.CalendarDay__highlighted_calendar, modifiers.has('blocked-minimum-nights') && styles.CalendarDay__blocked_minimum_nights, @@ -331,4 +333,6 @@ export default withStyles(({ reactDates: { color, font } }) => ({ CalendarDay__selected_start: {}, CalendarDay__selected_end: {}, CalendarDay__today: {}, + CalendarDay__firstDayOfWeek: {}, + CalendarDay__lastDayOfWeek: {}, }))(CalendarDay); diff --git a/src/components/CustomizableCalendarDay.jsx b/src/components/CustomizableCalendarDay.jsx index 2d85acb539..690b308b55 100644 --- a/src/components/CustomizableCalendarDay.jsx +++ b/src/components/CustomizableCalendarDay.jsx @@ -56,6 +56,8 @@ const propTypes = forbidExtraProps({ defaultStyles: DayStyleShape, outsideStyles: DayStyleShape, todayStyles: DayStyleShape, + firstDayOfWeekStyles: DayStyleShape, + lastDayOfWeekStyles: DayStyleShape, highlightedCalendarStyles: DayStyleShape, blockedMinNightsStyles: DayStyleShape, blockedCalendarStyles: DayStyleShape, @@ -183,6 +185,8 @@ const defaultProps = { selectedStartStyles: {}, selectedEndStyles: {}, afterHoveredStartStyles: {}, + firstDayOfWeekStyles: {}, + lastDayOfWeekStyles: {}, // internationalization phrases: CalendarDayPhrases, @@ -259,6 +263,8 @@ class CustomizableCalendarDay extends React.Component { defaultStyles: defaultStylesWithHover, outsideStyles: outsideStylesWithHover, todayStyles: todayStylesWithHover, + firstDayOfWeekStyles: firstDayOfWeekStylesWithHover, + lastDayOfWeekStyles: lastDayOfWeekStylesWithHover, highlightedCalendarStyles: highlightedCalendarStylesWithHover, blockedMinNightsStyles: blockedMinNightsStylesWithHover, blockedCalendarStyles: blockedCalendarStylesWithHover, @@ -288,6 +294,8 @@ class CustomizableCalendarDay extends React.Component { const defaultStyles = getStyles(defaultStylesWithHover, isHovered); const outsideStyles = getStyles(outsideStylesWithHover, isHovered); const todayStyles = getStyles(todayStylesWithHover, isHovered); + const firstDayOfWeekStyles = getStyles(firstDayOfWeekStylesWithHover, isHovered); + const lastDayOfWeekStyles = getStyles(lastDayOfWeekStylesWithHover, isHovered); const highlightedCalendarStyles = getStyles(highlightedCalendarStylesWithHover, isHovered); const blockedMinNightsStyles = getStyles(blockedMinNightsStylesWithHover, isHovered); const blockedCalendarStyles = getStyles(blockedCalendarStylesWithHover, isHovered); @@ -309,6 +317,8 @@ class CustomizableCalendarDay extends React.Component { defaultStyles, isOutsideDay && outsideStyles, modifiers.has('today') && todayStyles, + modifiers.has('first-day-of-week') && firstDayOfWeekStyles, + modifiers.has('last-day-of-week') && lastDayOfWeekStyles, modifiers.has('highlighted-calendar') && highlightedCalendarStyles, modifiers.has('blocked-minimum-nights') && blockedMinNightsStyles, modifiers.has('blocked-calendar') && blockedCalendarStyles, diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 4272e36478..a83897a351 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -178,6 +178,8 @@ export default class DayPickerRangeController extends React.Component { 'hovered-span': day => this.isInHoveredSpan(day), 'hovered-offset': day => this.isInHoveredSpan(day), 'after-hovered-start': day => this.isDayAfterHoveredStartDate(day), + 'first-day-of-week': day => this.isFirstDayOfWeek(day), + 'last-day-of-week': day => this.isLastDayOfWeek(day), }; const { currentMonth, visibleDays } = this.getStateForNewMonth(props); @@ -942,6 +944,16 @@ export default class DayPickerRangeController extends React.Component { return isSameDay(day, this.today); } + isFirstDayOfWeek(day) { + const { firstDayOfWeek } = this.props; + return day.day() === (firstDayOfWeek || moment.localeData().firstDayOfWeek()); + } + + isLastDayOfWeek(day) { + const { firstDayOfWeek } = this.props; + return day.day() === ((firstDayOfWeek || moment.localeData().firstDayOfWeek()) + 6) % 7; + } + render() { const { numberOfMonths, diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 0773f89f15..62b4d5eb70 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -148,6 +148,8 @@ export default class DayPickerSingleDateController extends React.Component { valid: day => !this.isBlocked(day), hovered: day => this.isHovered(day), selected: day => this.isSelected(day), + 'first-day-of-week': day => this.isFirstDayOfWeek(day), + 'last-day-of-week': day => this.isLastDayOfWeek(day), }; const { currentMonth, visibleDays } = this.getStateForNewMonth(props); @@ -571,6 +573,16 @@ export default class DayPickerSingleDateController extends React.Component { return isSameDay(day, this.today); } + isFirstDayOfWeek(day) { + const { firstDayOfWeek } = this.props; + return day.day() === (firstDayOfWeek || moment.localeData().firstDayOfWeek()); + } + + isLastDayOfWeek(day) { + const { firstDayOfWeek } = this.props; + return day.day() === ((firstDayOfWeek || moment.localeData().firstDayOfWeek()) + 6) % 7; + } + render() { const { numberOfMonths, diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index 646466db6e..256d6a5e3e 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -3124,5 +3124,41 @@ describe('DayPickerRangeController', () => { expect(wrapper.instance().isToday(moment(today).subtract(1, 'months'))).to.equal(false); }); }); + + describe('#isFirstDayOfWeek', () => { + it('returns true if first day of this week', () => { + const wrapper = shallow(); + expect(wrapper.instance().isFirstDayOfWeek(moment().startOf('week'))).to.equal(true); + }); + + it('returns true if same day as firstDayOfWeek prop', () => { + const firstDayOfWeek = 3; + const wrapper = shallow(); + expect(wrapper.instance().isFirstDayOfWeek(moment().startOf('week').day(firstDayOfWeek))).to.equal(true); + }); + + it('returns false if not the first day of the week', () => { + const wrapper = shallow(); + expect(wrapper.instance().isFirstDayOfWeek(moment().endOf('week'))).to.equal(false); + }); + }); + + describe('#isLastDayOfWeek', () => { + it('returns true if last day of week', () => { + const wrapper = shallow(); + expect(wrapper.instance().isLastDayOfWeek(moment().endOf('week'))).to.equal(true); + }); + + it('returns true if 6 days after firstDayOfWeek prop', () => { + const firstDayOfWeek = 3; + const wrapper = shallow(); + expect(wrapper.instance().isLastDayOfWeek(moment().day(firstDayOfWeek).add(6, 'days'))).to.equal(true); + }); + + it('returns false if not last of week', () => { + const wrapper = shallow(); + expect(wrapper.instance().isLastDayOfWeek(moment().startOf('week').add(1, 'day'))).to.equal(false); + }); + }); }); }); diff --git a/test/components/DayPickerSingleDateController_spec.jsx b/test/components/DayPickerSingleDateController_spec.jsx index 9bbaef9ab5..2fc9b3a067 100644 --- a/test/components/DayPickerSingleDateController_spec.jsx +++ b/test/components/DayPickerSingleDateController_spec.jsx @@ -1344,6 +1344,42 @@ describe('DayPickerSingleDateController', () => { expect(wrapper.instance().isToday(moment(today).subtract(1, 'months'))).to.equal(false); }); }); + + describe('#isFirstDayOfWeek', () => { + it('returns true if first day of this week', () => { + const wrapper = shallow(); + expect(wrapper.instance().isFirstDayOfWeek(moment().startOf('week'))).to.equal(true); + }); + + it('returns true if same day as firstDayOfWeek prop', () => { + const firstDayOfWeek = 3; + const wrapper = shallow(); + expect(wrapper.instance().isFirstDayOfWeek(moment().startOf('week').day(firstDayOfWeek))).to.equal(true); + }); + + it('returns false if not the first day of the week', () => { + const wrapper = shallow(); + expect(wrapper.instance().isFirstDayOfWeek(moment().endOf('week'))).to.equal(false); + }); + }); + + describe('#isLastDayOfWeek', () => { + it('returns true if last day of week', () => { + const wrapper = shallow(); + expect(wrapper.instance().isLastDayOfWeek(moment().endOf('week'))).to.equal(true); + }); + + it('returns true if 6 days after firstDayOfWeek prop', () => { + const firstDayOfWeek = 3; + const wrapper = shallow(); + expect(wrapper.instance().isLastDayOfWeek(moment().day(firstDayOfWeek).add(6, 'days'))).to.equal(true); + }); + + it('returns false if not last of week', () => { + const wrapper = shallow(); + expect(wrapper.instance().isLastDayOfWeek(moment().startOf('week').add(1, 'day'))).to.equal(false); + }); + }); }); describe('initialVisibleMonth', () => { From 71051e77104d4b7aa93e70a49a6d920bcccc3e33 Mon Sep 17 00:00:00 2001 From: Marie P-W Date: Tue, 30 Jan 2018 11:29:37 +0100 Subject: [PATCH 085/618] Add props to make calendar info panel position configurable --- src/components/DateRangePicker.jsx | 7 + src/components/DayPicker.jsx | 215 ++++++++++++------ src/components/DayPickerRangeController.jsx | 9 + .../DayPickerSingleDateController.jsx | 9 + src/components/SingleDatePicker.jsx | 7 + src/constants.js | 5 + src/shapes/CalendarInfoPositionShape.js | 10 + src/shapes/DateRangePickerShape.js | 2 + src/shapes/SingleDatePickerShape.js | 2 + stories/DateRangePicker_calendar.js | 39 +++- stories/SingleDatePicker_calendar.js | 39 +++- 11 files changed, 265 insertions(+), 79 deletions(-) create mode 100644 src/shapes/CalendarInfoPositionShape.js diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 967df454e2..b3784b622d 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -33,6 +33,10 @@ import { OPEN_UP, DAY_SIZE, ICON_BEFORE_POSITION, + INFO_POSITION_TOP, + INFO_POSITION_BOTTOM, + INFO_POSITION_LEFT, + INFO_POSITION_RIGHT, FANG_HEIGHT_PX, DEFAULT_VERTICAL_SPACING, } from '../constants'; @@ -80,6 +84,7 @@ const defaultProps = { keepOpenOnDateSelect: false, reopenPickerOnClearDates: false, renderCalendarInfo: null, + calendarInfoPosition: INFO_POSITION_TOP, hideKeyboardShortcutsPanel: false, daySize: DAY_SIZE, isRTL: false, @@ -329,6 +334,7 @@ class DateRangePicker extends React.Component { renderCalendarDay, renderDayContents, renderCalendarInfo, + calendarInfoPosition, firstDayOfWeek, initialVisibleMonth, hideKeyboardShortcutsPanel, @@ -409,6 +415,7 @@ class DateRangePicker extends React.Component { renderCalendarDay={renderCalendarDay} renderDayContents={renderDayContents} renderCalendarInfo={renderCalendarInfo} + calendarInfoPosition={calendarInfoPosition} isFocused={isDayPickerFocused} showKeyboardShortcuts={showKeyboardShortcuts} onBlur={this.onDayPickerBlur} diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index 2ec875befd..30fd082098 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -21,17 +21,23 @@ import DayPickerKeyboardShortcuts, { } from './DayPickerKeyboardShortcuts'; import getCalendarMonthWidth from '../utils/getCalendarMonthWidth'; +import calculateDimension from '../utils/calculateDimension'; import getActiveElement from '../utils/getActiveElement'; import isDayVisible from '../utils/isDayVisible'; import ScrollableOrientationShape from '../shapes/ScrollableOrientationShape'; import DayOfWeekShape from '../shapes/DayOfWeekShape'; +import CalendarInfoPositionShape from '../shapes/CalendarInfoPositionShape'; import { HORIZONTAL_ORIENTATION, VERTICAL_ORIENTATION, VERTICAL_SCROLLABLE, DAY_SIZE, + INFO_POSITION_TOP, + INFO_POSITION_BOTTOM, + INFO_POSITION_LEFT, + INFO_POSITION_RIGHT, MODIFIER_KEY_NAMES, } from '../constants'; @@ -53,6 +59,7 @@ const propTypes = forbidExtraProps({ initialVisibleMonth: PropTypes.func, firstDayOfWeek: DayOfWeekShape, renderCalendarInfo: PropTypes.func, + calendarInfoPosition: CalendarInfoPositionShape, hideKeyboardShortcutsPanel: PropTypes.bool, daySize: nonNegativeInteger, isRTL: PropTypes.bool, @@ -102,6 +109,7 @@ export const defaultProps = { initialVisibleMonth: () => moment(), firstDayOfWeek: null, renderCalendarInfo: null, + calendarInfoPosition: INFO_POSITION_TOP, hideKeyboardShortcutsPanel: false, daySize: DAY_SIZE, isRTL: false, @@ -183,6 +191,7 @@ class DayPicker extends React.Component { this.openKeyboardShortcutsPanel = this.openKeyboardShortcutsPanel.bind(this); this.closeKeyboardShortcutsPanel = this.closeKeyboardShortcutsPanel.bind(this); + this.setCalendarInfoRef = this.setCalendarInfoRef.bind(this); this.setContainerRef = this.setContainerRef.bind(this); this.setTransitionContainerRef = this.setTransitionContainerRef.bind(this); this.setCalendarMonthHeights = this.setCalendarMonthHeights.bind(this); @@ -449,6 +458,10 @@ class DayPicker extends React.Component { this.container = ref; } + setCalendarInfoRef(ref) { + this.calendarInfo = ref; + } + setTransitionContainerRef(ref) { this.transitionContainer = ref; } @@ -694,6 +707,7 @@ class DayPicker extends React.Component { renderCalendarDay, renderDayContents, renderCalendarInfo, + calendarInfoPosition, hideKeyboardShortcutsPanel, onOutsideClick, monthFormat, @@ -714,18 +728,6 @@ class DayPicker extends React.Component { weekHeaders.push(this.renderWeekHeader(i)); } - const verticalScrollable = orientation === VERTICAL_SCROLLABLE; - const firstVisibleMonthIndex = this.getFirstVisibleIndex(); - const horizontalWidth = (calendarMonthWidth * numberOfMonths) + (2 * DAY_PICKER_PADDING); - - const dayPickerStyle = { - width: this.isHorizontal() && horizontalWidth, - - // These values are to center the datepicker (approximately) on the page - marginLeft: this.isHorizontal() && withPortal && -horizontalWidth / 2, - marginTop: this.isHorizontal() && withPortal && -calendarMonthWidth / 2, - }; - let height; if (this.isHorizontal()) { height = this.calendarMonthGridHeight; @@ -736,7 +738,7 @@ class DayPicker extends React.Component { } const transitionContainerStyle = { - width: this.isHorizontal() && horizontalWidth, + width: this.isHorizontal() && fullHorizontalWidth, height, }; @@ -753,6 +755,48 @@ class DayPicker extends React.Component { const isHorizontalAndAnimating = this.isHorizontal() && isCalendarMonthGridAnimating; + const calendarInfoPositionTop = calendarInfoPosition === INFO_POSITION_TOP; + const calendarInfoPositionBottom = calendarInfoPosition === INFO_POSITION_BOTTOM; + const calendarInfoPositionLeft = calendarInfoPosition === INFO_POSITION_LEFT; + const calendarInfoPositionRight = calendarInfoPosition === INFO_POSITION_RIGHT; + + const calendarInfoStyle = { + minHeight: height + }; + + const calendarInfo = renderCalendarInfo && ( +
    + { renderCalendarInfo() } +
    + ); + + const calendarInfoWith = renderCalendarInfo && (calendarInfoPositionLeft || calendarInfoPositionRight) + ? calculateDimension(this.calendarInfo, 'width', true, true) + : 0; + + const verticalScrollable = orientation === VERTICAL_SCROLLABLE; + const firstVisibleMonthIndex = this.getFirstVisibleIndex(); + const dayPickerWrapperHorizontalWidth = (calendarMonthWidth * numberOfMonths) + (2 * DAY_PICKER_PADDING); + const fullHorizontalWidth = (calendarMonthWidth * numberOfMonths) + (2 * DAY_PICKER_PADDING) + calendarInfoWith; + + const dayPickerWrapperStyle = { + width: dayPickerWrapperHorizontalWidth + }; + + const dayPickerStyle = { + width: this.isHorizontal() && fullHorizontalWidth, + + // These values are to center the datepicker (approximately) on the page + marginLeft: this.isHorizontal() && withPortal && -fullHorizontalWidth / 2, + marginTop: this.isHorizontal() && withPortal && -calendarMonthWidth / 2, + }; + return (
    + {(calendarInfoPositionTop || calendarInfoPositionLeft) && calendarInfo} + - -
    { e.stopPropagation(); }} - onKeyDown={this.onKeyDown} - onMouseUp={() => { this.setState({ withMouseInteractions: true }); }} - role="region" - tabIndex={-1} > - {!verticalScrollable && this.renderNavigation()} + +
    { e.stopPropagation(); }} + onKeyDown={this.onKeyDown} + onMouseUp={() => { this.setState({ withMouseInteractions: true }); }} + role="region" + tabIndex={-1} + > + {!verticalScrollable && this.renderNavigation()} + +
    + + {verticalScrollable && this.renderNavigation()} +
    + + {!isTouch && !hideKeyboardShortcutsPanel && + + }
    - {!isTouch && !hideKeyboardShortcutsPanel && - - }
    - {renderCalendarInfo && renderCalendarInfo()} + {(calendarInfoPositionBottom || calendarInfoPositionRight) && calendarInfo}
    ); @@ -894,6 +949,16 @@ export default withStyles(({ reactDates: { color, font, zIndex } }) => ({ outline: 'none', }, + DayPicker_calendarInfo__horizontal: { + display: 'table-cell', + verticalAlign: 'top', + }, + + DayPicker_wrapper__horizontal: { + display: 'table-cell', + verticalAlign: 'top', + }, + DayPicker_weekHeaders: { position: 'relative', }, diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 4272e36478..41866708ef 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -26,6 +26,7 @@ import toISOMonthString from '../utils/toISOMonthString'; import FocusedInputShape from '../shapes/FocusedInputShape'; import ScrollableOrientationShape from '../shapes/ScrollableOrientationShape'; import DayOfWeekShape from '../shapes/DayOfWeekShape'; +import CalendarInfoPositionShape from '../shapes/CalendarInfoPositionShape'; import { START_DATE, @@ -33,6 +34,10 @@ import { HORIZONTAL_ORIENTATION, VERTICAL_SCROLLABLE, DAY_SIZE, + INFO_POSITION_TOP, + INFO_POSITION_BOTTOM, + INFO_POSITION_LEFT, + INFO_POSITION_RIGHT, } from '../constants'; import DayPicker from './DayPicker'; @@ -74,6 +79,7 @@ const propTypes = forbidExtraProps({ renderCalendarDay: PropTypes.func, renderDayContents: PropTypes.func, renderCalendarInfo: PropTypes.func, + calendarInfoPosition: CalendarInfoPositionShape, firstDayOfWeek: DayOfWeekShape, verticalHeight: nonNegativeInteger, transitionDuration: nonNegativeInteger, @@ -129,6 +135,7 @@ const defaultProps = { renderCalendarDay: undefined, renderDayContents: null, renderCalendarInfo: null, + calendarInfoPosition: INFO_POSITION_TOP, firstDayOfWeek: null, verticalHeight: null, noBorder: false, @@ -960,6 +967,7 @@ export default class DayPickerRangeController extends React.Component { renderCalendarDay, renderDayContents, renderCalendarInfo, + calendarInfoPosition, onBlur, isFocused, showKeyboardShortcuts, @@ -998,6 +1006,7 @@ export default class DayPickerRangeController extends React.Component { renderCalendarDay={renderCalendarDay} renderDayContents={renderDayContents} renderCalendarInfo={renderCalendarInfo} + calendarInfoPosition={calendarInfoPosition} firstDayOfWeek={firstDayOfWeek} hideKeyboardShortcutsPanel={hideKeyboardShortcutsPanel} isFocused={isFocused} diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 0773f89f15..4bf0d9f369 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -20,11 +20,16 @@ import toISOMonthString from '../utils/toISOMonthString'; import ScrollableOrientationShape from '../shapes/ScrollableOrientationShape'; import DayOfWeekShape from '../shapes/DayOfWeekShape'; +import CalendarInfoPositionShape from '../shapes/CalendarInfoPositionShape'; import { HORIZONTAL_ORIENTATION, VERTICAL_SCROLLABLE, DAY_SIZE, + INFO_POSITION_TOP, + INFO_POSITION_BOTTOM, + INFO_POSITION_LEFT, + INFO_POSITION_RIGHT, } from '../constants'; import DayPicker from './DayPicker'; @@ -66,6 +71,7 @@ const propTypes = forbidExtraProps({ renderCalendarDay: PropTypes.func, renderDayContents: PropTypes.func, renderCalendarInfo: PropTypes.func, + calendarInfoPosition: CalendarInfoPositionShape, // accessibility onBlur: PropTypes.func, @@ -118,6 +124,7 @@ const defaultProps = { renderCalendarDay: undefined, renderDayContents: null, renderCalendarInfo: null, + calendarInfoPosition: INFO_POSITION_TOP, // accessibility onBlur() {}, @@ -588,6 +595,7 @@ export default class DayPickerSingleDateController extends React.Component { renderCalendarDay, renderDayContents, renderCalendarInfo, + calendarInfoPosition, isFocused, isRTL, phrases, @@ -626,6 +634,7 @@ export default class DayPickerSingleDateController extends React.Component { renderCalendarDay={renderCalendarDay} renderDayContents={renderDayContents} renderCalendarInfo={renderCalendarInfo} + calendarInfoPosition={calendarInfoPosition} isFocused={isFocused} getFirstFocusableDay={this.getFirstFocusableDay} onBlur={onBlur} diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index c2f37aad1d..85b2a8f5be 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -31,6 +31,10 @@ import { OPEN_UP, DAY_SIZE, ICON_BEFORE_POSITION, + INFO_POSITION_TOP, + INFO_POSITION_BOTTOM, + INFO_POSITION_LEFT, + INFO_POSITION_RIGHT, FANG_HEIGHT_PX, DEFAULT_VERTICAL_SPACING, } from '../constants'; @@ -77,6 +81,7 @@ const defaultProps = { keepOpenOnDateSelect: false, reopenPickerOnClearDate: false, renderCalendarInfo: null, + calendarInfoPosition: INFO_POSITION_TOP, hideKeyboardShortcutsPanel: false, daySize: DAY_SIZE, isRTL: false, @@ -360,6 +365,7 @@ class SingleDatePicker extends React.Component { renderCalendarDay, renderDayContents, renderCalendarInfo, + calendarInfoPosition, hideKeyboardShortcutsPanel, firstDayOfWeek, customCloseIcon, @@ -430,6 +436,7 @@ class SingleDatePicker extends React.Component { renderCalendarDay={renderCalendarDay} renderDayContents={renderDayContents} renderCalendarInfo={renderCalendarInfo} + calendarInfoPosition={calendarInfoPosition} isFocused={isDayPickerFocused} showKeyboardShortcuts={showKeyboardShortcuts} onBlur={this.onDayPickerBlur} diff --git a/src/constants.js b/src/constants.js index 8a6a798116..64c95467e3 100644 --- a/src/constants.js +++ b/src/constants.js @@ -12,6 +12,11 @@ export const VERTICAL_SCROLLABLE = 'verticalScrollable'; export const ICON_BEFORE_POSITION = 'before'; export const ICON_AFTER_POSITION = 'after'; +export const INFO_POSITION_TOP = 'top' +export const INFO_POSITION_BOTTOM = 'bottom' +export const INFO_POSITION_LEFT = 'left' +export const INFO_POSITION_RIGHT = 'right' + export const ANCHOR_LEFT = 'left'; export const ANCHOR_RIGHT = 'right'; diff --git a/src/shapes/CalendarInfoPositionShape.js b/src/shapes/CalendarInfoPositionShape.js new file mode 100644 index 0000000000..42c32e09b6 --- /dev/null +++ b/src/shapes/CalendarInfoPositionShape.js @@ -0,0 +1,10 @@ +import PropTypes from 'prop-types'; + +import { + INFO_POSITION_TOP, + INFO_POSITION_BOTTOM, + INFO_POSITION_LEFT, + INFO_POSITION_RIGHT, +} from '../constants'; + +export default PropTypes.oneOf([INFO_POSITION_TOP, INFO_POSITION_BOTTOM, INFO_POSITION_LEFT, INFO_POSITION_RIGHT]); diff --git a/src/shapes/DateRangePickerShape.js b/src/shapes/DateRangePickerShape.js index 5c7ff7f945..962ce954f4 100644 --- a/src/shapes/DateRangePickerShape.js +++ b/src/shapes/DateRangePickerShape.js @@ -11,6 +11,7 @@ import OrientationShape from '../shapes/OrientationShape'; import anchorDirectionShape from '../shapes/AnchorDirectionShape'; import openDirectionShape from '../shapes/OpenDirectionShape'; import DayOfWeekShape from '../shapes/DayOfWeekShape'; +import CalendarInfoPositionShape from '../shapes/CalendarInfoPositionShape'; export default { // required props for a functional interactive DateRangePicker @@ -60,6 +61,7 @@ export default { keepOpenOnDateSelect: PropTypes.bool, reopenPickerOnClearDates: PropTypes.bool, renderCalendarInfo: PropTypes.func, + calendarInfoPosition: CalendarInfoPositionShape, hideKeyboardShortcutsPanel: PropTypes.bool, verticalHeight: nonNegativeInteger, transitionDuration: nonNegativeInteger, diff --git a/src/shapes/SingleDatePickerShape.js b/src/shapes/SingleDatePickerShape.js index 2adfb77175..c9987584b8 100644 --- a/src/shapes/SingleDatePickerShape.js +++ b/src/shapes/SingleDatePickerShape.js @@ -10,6 +10,7 @@ import OrientationShape from '../shapes/OrientationShape'; import anchorDirectionShape from '../shapes/AnchorDirectionShape'; import openDirectionShape from '../shapes/OpenDirectionShape'; import DayOfWeekShape from '../shapes/DayOfWeekShape'; +import CalendarInfoPositionShape from '../shapes/CalendarInfoPositionShape'; export default { // required props for a functional interactive SingleDatePicker @@ -52,6 +53,7 @@ export default { keepOpenOnDateSelect: PropTypes.bool, reopenPickerOnClearDate: PropTypes.bool, renderCalendarInfo: PropTypes.func, + calendarInfoPosition: CalendarInfoPositionShape, hideKeyboardShortcutsPanel: PropTypes.bool, daySize: nonNegativeInteger, isRTL: PropTypes.bool, diff --git a/stories/DateRangePicker_calendar.js b/stories/DateRangePicker_calendar.js index 4c085c4c52..43796f0a4f 100644 --- a/stories/DateRangePicker_calendar.js +++ b/stories/DateRangePicker_calendar.js @@ -36,7 +36,6 @@ const TestCustomInfoPanel = () => (
    @@ -130,7 +129,7 @@ storiesOf('DRP - Calendar Props', module) autoFocus /> )) - .addWithInfo('with info panel', () => ( + .addWithInfo('with info panel default', () => ( ( @@ -138,6 +137,42 @@ storiesOf('DRP - Calendar Props', module) autoFocus /> )) + .addWithInfo('with info panel left', () => ( + ( + + )} + autoFocus + /> + )) + .addWithInfo('with info panel right', () => ( + ( + + )} + autoFocus + /> + )) + .addWithInfo('with info panel bottom', () => ( + ( + + )} + autoFocus + /> + )) + .addWithInfo('with info panel top', () => ( + ( + + )} + autoFocus + /> + )) .addWithInfo('with keyboard shorcuts panel hidden', () => ( (
    @@ -121,8 +120,44 @@ storiesOf('SDP - Calendar Props', module) autoFocus /> )) - .addWithInfo('with info panel', () => ( + .addWithInfo('with info panel default', () => ( ( + + )} + autoFocus + /> + )) + .addWithInfo('with info panel left', () => ( + ( + + )} + autoFocus + /> + )) + .addWithInfo('with info panel right', () => ( + ( + + )} + autoFocus + /> + )) + .addWithInfo('with info panel bottom', () => ( + ( + + )} + autoFocus + /> + )) + .addWithInfo('with info panel top', () => ( + ( )} From 62ef8c343ff0e5ede3a6a6228be20b9b2aab967a Mon Sep 17 00:00:00 2001 From: Marie P-W Date: Tue, 30 Jan 2018 13:07:15 +0100 Subject: [PATCH 086/618] Fix #989 - Fix linting --- src/components/DateRangePicker.jsx | 3 --- src/components/DayPickerRangeController.jsx | 3 --- src/components/DayPickerSingleDateController.jsx | 3 --- src/components/SingleDatePicker.jsx | 3 --- src/constants.js | 8 ++++---- src/shapes/CalendarInfoPositionShape.js | 7 ++++++- 6 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index b3784b622d..f4959f024d 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -34,9 +34,6 @@ import { DAY_SIZE, ICON_BEFORE_POSITION, INFO_POSITION_TOP, - INFO_POSITION_BOTTOM, - INFO_POSITION_LEFT, - INFO_POSITION_RIGHT, FANG_HEIGHT_PX, DEFAULT_VERTICAL_SPACING, } from '../constants'; diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 41866708ef..ffb21b1a74 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -35,9 +35,6 @@ import { VERTICAL_SCROLLABLE, DAY_SIZE, INFO_POSITION_TOP, - INFO_POSITION_BOTTOM, - INFO_POSITION_LEFT, - INFO_POSITION_RIGHT, } from '../constants'; import DayPicker from './DayPicker'; diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 4bf0d9f369..4510f0b1ca 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -27,9 +27,6 @@ import { VERTICAL_SCROLLABLE, DAY_SIZE, INFO_POSITION_TOP, - INFO_POSITION_BOTTOM, - INFO_POSITION_LEFT, - INFO_POSITION_RIGHT, } from '../constants'; import DayPicker from './DayPicker'; diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index 85b2a8f5be..f6f78ddaf2 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -32,9 +32,6 @@ import { DAY_SIZE, ICON_BEFORE_POSITION, INFO_POSITION_TOP, - INFO_POSITION_BOTTOM, - INFO_POSITION_LEFT, - INFO_POSITION_RIGHT, FANG_HEIGHT_PX, DEFAULT_VERTICAL_SPACING, } from '../constants'; diff --git a/src/constants.js b/src/constants.js index 64c95467e3..273c8074f8 100644 --- a/src/constants.js +++ b/src/constants.js @@ -12,10 +12,10 @@ export const VERTICAL_SCROLLABLE = 'verticalScrollable'; export const ICON_BEFORE_POSITION = 'before'; export const ICON_AFTER_POSITION = 'after'; -export const INFO_POSITION_TOP = 'top' -export const INFO_POSITION_BOTTOM = 'bottom' -export const INFO_POSITION_LEFT = 'left' -export const INFO_POSITION_RIGHT = 'right' +export const INFO_POSITION_TOP = 'top'; +export const INFO_POSITION_BOTTOM = 'bottom'; +export const INFO_POSITION_LEFT = 'left'; +export const INFO_POSITION_RIGHT = 'right'; export const ANCHOR_LEFT = 'left'; export const ANCHOR_RIGHT = 'right'; diff --git a/src/shapes/CalendarInfoPositionShape.js b/src/shapes/CalendarInfoPositionShape.js index 42c32e09b6..c2f433ecb4 100644 --- a/src/shapes/CalendarInfoPositionShape.js +++ b/src/shapes/CalendarInfoPositionShape.js @@ -7,4 +7,9 @@ import { INFO_POSITION_RIGHT, } from '../constants'; -export default PropTypes.oneOf([INFO_POSITION_TOP, INFO_POSITION_BOTTOM, INFO_POSITION_LEFT, INFO_POSITION_RIGHT]); +export default PropTypes.oneOf([ + INFO_POSITION_TOP, + INFO_POSITION_BOTTOM, + INFO_POSITION_LEFT, + INFO_POSITION_RIGHT, +]); From f24ef36cdf2f87b9812f5b828aa62f8c5ae14e46 Mon Sep 17 00:00:00 2001 From: Marie P-W Date: Tue, 30 Jan 2018 13:34:43 +0100 Subject: [PATCH 087/618] Fix #989 - Fix linting --- src/components/DayPicker.jsx | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index 30fd082098..fbeb23c621 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -759,34 +759,28 @@ class DayPicker extends React.Component { const calendarInfoPositionBottom = calendarInfoPosition === INFO_POSITION_BOTTOM; const calendarInfoPositionLeft = calendarInfoPosition === INFO_POSITION_LEFT; const calendarInfoPositionRight = calendarInfoPosition === INFO_POSITION_RIGHT; - - const calendarInfoStyle = { - minHeight: height - }; + const calendarInfoIsInline = calendarInfoPositionLeft || calendarInfoPositionRight; const calendarInfo = renderCalendarInfo && (
    { renderCalendarInfo() }
    ); - const calendarInfoWith = renderCalendarInfo && (calendarInfoPositionLeft || calendarInfoPositionRight) + const calendarInfoWith = renderCalendarInfo && calendarInfoIsInline ? calculateDimension(this.calendarInfo, 'width', true, true) : 0; const verticalScrollable = orientation === VERTICAL_SCROLLABLE; const firstVisibleMonthIndex = this.getFirstVisibleIndex(); - const dayPickerWrapperHorizontalWidth = (calendarMonthWidth * numberOfMonths) + (2 * DAY_PICKER_PADDING); - const fullHorizontalWidth = (calendarMonthWidth * numberOfMonths) + (2 * DAY_PICKER_PADDING) + calendarInfoWith; + const wrapperHorizontalWidth = (calendarMonthWidth * numberOfMonths) + (2 * DAY_PICKER_PADDING); + const fullHorizontalWidth = wrapperHorizontalWidth + calendarInfoWith; const dayPickerWrapperStyle = { - width: dayPickerWrapperHorizontalWidth + width: wrapperHorizontalWidth, }; const dayPickerStyle = { @@ -819,7 +813,7 @@ class DayPicker extends React.Component {
    From efcd0f5b33c695ebd6e74d7d30ed6186b245009f Mon Sep 17 00:00:00 2001 From: Marie P-W Date: Tue, 30 Jan 2018 13:43:33 +0100 Subject: [PATCH 088/618] Fix #989 - Fix linting --- src/components/DayPicker.jsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index fbeb23c621..43c7842852 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -728,6 +728,7 @@ class DayPicker extends React.Component { weekHeaders.push(this.renderWeekHeader(i)); } + const verticalScrollable = orientation === VERTICAL_SCROLLABLE; let height; if (this.isHorizontal()) { height = this.calendarMonthGridHeight; @@ -737,11 +738,6 @@ class DayPicker extends React.Component { height = verticalHeight || 1.75 * calendarMonthWidth; } - const transitionContainerStyle = { - width: this.isHorizontal() && fullHorizontalWidth, - height, - }; - const isCalendarMonthGridAnimating = monthTransition !== null; const transformType = this.isVertical() ? 'translateY' : 'translateX'; const transformValue = `${transformType}(${translationValue}px)`; @@ -774,11 +770,15 @@ class DayPicker extends React.Component { ? calculateDimension(this.calendarInfo, 'width', true, true) : 0; - const verticalScrollable = orientation === VERTICAL_SCROLLABLE; const firstVisibleMonthIndex = this.getFirstVisibleIndex(); const wrapperHorizontalWidth = (calendarMonthWidth * numberOfMonths) + (2 * DAY_PICKER_PADDING); const fullHorizontalWidth = wrapperHorizontalWidth + calendarInfoWith; + const transitionContainerStyle = { + width: this.isHorizontal() && wrapperHorizontalWidth, + height, + }; + const dayPickerWrapperStyle = { width: wrapperHorizontalWidth, }; From 51ea6e07d997cc6f5d4e472c1d39d97fe55b9573 Mon Sep 17 00:00:00 2001 From: LiranCohen Date: Tue, 30 Jan 2018 16:39:00 +0200 Subject: [PATCH 089/618] avoid pitfall of executing the callback before state finished update. --- src/components/DayPickerSingleDateController.jsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 62b4d5eb70..94ea461544 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -362,9 +362,10 @@ export default class DayPickerSingleDateController extends React.Component { ...newVisibleDays, ...this.getModifiers(prevMonthVisibleDays), }, + },()=>{ + onPrevMonthClick(prevMonth.clone()); }); - onPrevMonthClick(prevMonth.clone()); } onNextMonthClick() { @@ -386,9 +387,9 @@ export default class DayPickerSingleDateController extends React.Component { ...newVisibleDays, ...this.getModifiers(nextMonthVisibleDays), }, + },() => { + onNextMonthClick(newCurrentMonth.clone()); }); - - onNextMonthClick(newCurrentMonth.clone()); } From 1d05379c36133e0a19f03690825c9652a80ecbe3 Mon Sep 17 00:00:00 2001 From: LiranCohen Date: Tue, 30 Jan 2018 17:04:03 +0200 Subject: [PATCH 090/618] style fixes. --- src/components/DayPickerSingleDateController.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 94ea461544..893dea5efe 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -362,10 +362,9 @@ export default class DayPickerSingleDateController extends React.Component { ...newVisibleDays, ...this.getModifiers(prevMonthVisibleDays), }, - },()=>{ + }, () => { onPrevMonthClick(prevMonth.clone()); }); - } onNextMonthClick() { @@ -387,7 +386,7 @@ export default class DayPickerSingleDateController extends React.Component { ...newVisibleDays, ...this.getModifiers(nextMonthVisibleDays), }, - },() => { + }, () => { onNextMonthClick(newCurrentMonth.clone()); }); } From ddbb26bebdbc4b20fa786accf202eafce21d86b4 Mon Sep 17 00:00:00 2001 From: Marie P-W Date: Wed, 31 Jan 2018 11:22:59 +0100 Subject: [PATCH 091/618] Fix jsx curly braces | #989 --- src/components/DayPicker.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index 43c7842852..3ffa212570 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -762,7 +762,7 @@ class DayPicker extends React.Component { ref={this.setCalendarInfoRef} {...css((calendarInfoIsInline) && styles.DayPicker_calendarInfo__horizontal)} > - { renderCalendarInfo() } + {renderCalendarInfo()}
    ); From 3e4847807ae75d55157c7717c0e4dfcab603c7a4 Mon Sep 17 00:00:00 2001 From: Marie P-W Date: Wed, 31 Jan 2018 11:46:49 +0100 Subject: [PATCH 092/618] Fix - cache in a variable value of `this.horizontal()` instead of multiple calls | #989 --- src/components/DayPicker.jsx | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index 3ffa212570..f18777fd01 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -722,6 +722,8 @@ class DayPicker extends React.Component { transitionDuration, } = this.props; + const isHorizontal = this.isHorizontal(); + const numOfWeekHeaders = this.isVertical() ? 1 : numberOfMonths; const weekHeaders = []; for (let i = 0; i < numOfWeekHeaders; i += 1) { @@ -730,7 +732,7 @@ class DayPicker extends React.Component { const verticalScrollable = orientation === VERTICAL_SCROLLABLE; let height; - if (this.isHorizontal()) { + if (isHorizontal) { height = this.calendarMonthGridHeight; } else if (this.isVertical() && !verticalScrollable && !withPortal) { // If the user doesn't set a desired height, @@ -749,7 +751,7 @@ class DayPicker extends React.Component { keyboardShortcutButtonLocation = withPortal ? TOP_LEFT : TOP_RIGHT; } - const isHorizontalAndAnimating = this.isHorizontal() && isCalendarMonthGridAnimating; + const isHorizontalAndAnimating = isHorizontal && isCalendarMonthGridAnimating; const calendarInfoPositionTop = calendarInfoPosition === INFO_POSITION_TOP; const calendarInfoPositionBottom = calendarInfoPosition === INFO_POSITION_BOTTOM; @@ -775,7 +777,7 @@ class DayPicker extends React.Component { const fullHorizontalWidth = wrapperHorizontalWidth + calendarInfoWith; const transitionContainerStyle = { - width: this.isHorizontal() && wrapperHorizontalWidth, + width: isHorizontal && wrapperHorizontalWidth, height, }; @@ -784,11 +786,11 @@ class DayPicker extends React.Component { }; const dayPickerStyle = { - width: this.isHorizontal() && fullHorizontalWidth, + width: isHorizontal && fullHorizontalWidth, // These values are to center the datepicker (approximately) on the page - marginLeft: this.isHorizontal() && withPortal && -fullHorizontalWidth / 2, - marginTop: this.isHorizontal() && withPortal && -calendarMonthWidth / 2, + marginLeft: isHorizontal && withPortal && -fullHorizontalWidth / 2, + marginTop: isHorizontal && withPortal && -calendarMonthWidth / 2, }; return ( @@ -797,10 +799,10 @@ class DayPicker extends React.Component { aria-label={phrases.calendarLabel} {...css( styles.DayPicker, - this.isHorizontal() && styles.DayPicker__horizontal, + isHorizontal && styles.DayPicker__horizontal, this.isVertical() && styles.DayPicker__vertical, verticalScrollable && styles.DayPicker__verticalScrollable, - this.isHorizontal() && withPortal && styles.DayPicker_portal__horizontal, + isHorizontal && withPortal && styles.DayPicker_portal__horizontal, this.isVertical() && withPortal && styles.DayPicker_portal__vertical, dayPickerStyle, !hasSetHeight && styles.DayPicker__hidden, @@ -820,7 +822,7 @@ class DayPicker extends React.Component { } - {datePickerIsAfterEndInput || children} + {isStartDateFocused || children} - {datePickerIsAfterEndInput && children} + {isEndDateFocused && children} {showClearDates && (
    ); } diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index f1d477da24..cbb500efd1 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -576,15 +576,12 @@ class SingleDatePicker extends React.PureComponent { block && styles.SingleDatePicker__block, )} > - { - enableOutsideClick - ? ( - - {input} - - ) - : input - } + {enableOutsideClick && ( + + {input} + + )} + { enableOutsideClick || input}
    ); } From 37d1b838f482c9ead0e268a222a745d2b3dd133b Mon Sep 17 00:00:00 2001 From: Bailey Stoner Date: Mon, 7 Jan 2019 16:16:33 -0800 Subject: [PATCH 338/618] Fix wrong comparison :) --- src/components/DateRangePickerInput.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DateRangePickerInput.jsx b/src/components/DateRangePickerInput.jsx index eff3d33ae8..4ade394ae9 100644 --- a/src/components/DateRangePickerInput.jsx +++ b/src/components/DateRangePickerInput.jsx @@ -245,7 +245,7 @@ function DateRangePickerInput({
    } - {isStartDateFocused || children} + {isStartDateFocused && children} Date: Mon, 7 Jan 2019 17:33:47 -0800 Subject: [PATCH 339/618] Update src/components/SingleDatePicker.jsx Co-Authored-By: monokrome --- src/components/SingleDatePicker.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index cbb500efd1..a796923e71 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -581,7 +581,7 @@ class SingleDatePicker extends React.PureComponent { {input} )} - { enableOutsideClick || input} + {enableOutsideClick || input}
    ); } From 450e79403917321c1b2d6ab279ef6215bcb848ed Mon Sep 17 00:00:00 2001 From: Bailey Stoner Date: Mon, 7 Jan 2019 17:41:26 -0800 Subject: [PATCH 340/618] Remove unnecessary `children` prop. --- src/components/DateRangePickerInputController.jsx | 9 +-------- src/components/SingleDatePickerInput.jsx | 5 ----- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/components/DateRangePickerInputController.jsx b/src/components/DateRangePickerInputController.jsx index 673702b3b8..6466c6d9ff 100644 --- a/src/components/DateRangePickerInputController.jsx +++ b/src/components/DateRangePickerInputController.jsx @@ -28,8 +28,6 @@ import { } from '../constants'; const propTypes = forbidExtraProps({ - children: PropTypes.node, - startDate: momentPropTypes.momentObj, startDateId: PropTypes.string, startDatePlaceholderText: PropTypes.string, @@ -82,8 +80,6 @@ const propTypes = forbidExtraProps({ }); const defaultProps = { - children: null, - startDate: null, startDateId: START_DATE, startDatePlaceholderText: 'Start Date', @@ -265,7 +261,6 @@ export default class DateRangePickerInputController extends React.PureComponent render() { const { - children, startDate, startDateId, startDatePlaceholderText, @@ -340,9 +335,7 @@ export default class DateRangePickerInputController extends React.PureComponent small={small} regular={regular} verticalSpacing={verticalSpacing} - > - {children} - + /> ); } } diff --git a/src/components/SingleDatePickerInput.jsx b/src/components/SingleDatePickerInput.jsx index a877f52c7b..4c48654b95 100644 --- a/src/components/SingleDatePickerInput.jsx +++ b/src/components/SingleDatePickerInput.jsx @@ -19,7 +19,6 @@ import { ICON_BEFORE_POSITION, ICON_AFTER_POSITION, OPEN_DOWN } from '../constan const propTypes = forbidExtraProps({ ...withStylesPropTypes, id: PropTypes.string.isRequired, - children: PropTypes.node, placeholder: PropTypes.string, // also used as label displayValue: PropTypes.string, screenReaderMessage: PropTypes.string, @@ -55,7 +54,6 @@ const propTypes = forbidExtraProps({ }); const defaultProps = { - children: null, placeholder: 'Select Date', displayValue: '', screenReaderMessage: '', @@ -92,7 +90,6 @@ const defaultProps = { function SingleDatePickerInput({ id, - children, placeholder, displayValue, focused, @@ -186,8 +183,6 @@ function SingleDatePickerInput({ block={block} /> - {children} - {showClearDate && (
    -
    {(calendarInfoPositionBottom || calendarInfoPositionAfter) && calendarInfo} diff --git a/src/components/SingleDatePickerInputController.jsx b/src/components/SingleDatePickerInputController.jsx index 7bb8071ced..6f07f263a5 100644 --- a/src/components/SingleDatePickerInputController.jsx +++ b/src/components/SingleDatePickerInputController.jsx @@ -25,8 +25,6 @@ import { } from '../constants'; const propTypes = forbidExtraProps({ - children: PropTypes.node, - date: momentPropTypes.momentObj, onDateChange: PropTypes.func.isRequired, @@ -72,8 +70,6 @@ const propTypes = forbidExtraProps({ }); const defaultProps = { - children: null, - date: null, focused: false, @@ -195,7 +191,6 @@ export default class SingleDatePickerInputController extends React.PureComponent render() { const { id, - children, placeholder, disabled, focused, @@ -255,9 +250,7 @@ export default class SingleDatePickerInputController extends React.PureComponent small={small} regular={regular} verticalSpacing={verticalSpacing} - > - {children} - + /> ); } } From a20d6232d2e027b4aaafd3d10e89022d6cb961d2 Mon Sep 17 00:00:00 2001 From: Bailey Stoner Date: Fri, 11 Jan 2019 11:53:47 -0800 Subject: [PATCH 345/618] Fix a number of linting issues --- .tern-port | 1 + src/components/.tern-port | 1 + src/components/DateRangePickerInput.jsx | 1 + src/components/DayPickerRangeController.jsx | 12 +++++++++--- src/components/SingleDatePicker.jsx | 1 + src/components/SingleDatePickerInputController.jsx | 7 ++++++- stories/.tern-port | 1 + test/components/SingleDatePicker_spec.jsx | 1 - 8 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 .tern-port create mode 100644 src/components/.tern-port create mode 100644 stories/.tern-port diff --git a/.tern-port b/.tern-port new file mode 100644 index 0000000000..dcb26926b3 --- /dev/null +++ b/.tern-port @@ -0,0 +1 @@ +56258 \ No newline at end of file diff --git a/src/components/.tern-port b/src/components/.tern-port new file mode 100644 index 0000000000..2dd3385e3f --- /dev/null +++ b/src/components/.tern-port @@ -0,0 +1 @@ +56254 \ No newline at end of file diff --git a/src/components/DateRangePickerInput.jsx b/src/components/DateRangePickerInput.jsx index 4ade394ae9..a1a54ec983 100644 --- a/src/components/DateRangePickerInput.jsx +++ b/src/components/DateRangePickerInput.jsx @@ -263,6 +263,7 @@ function DateRangePickerInput({ onFocus={onEndDateFocus} onKeyDownArrowDown={onKeyDownArrowDown} onKeyDownQuestionMark={onKeyDownQuestionMark} + onKeyDownTab={onEndDateTab} verticalSpacing={verticalSpacing} small={small} regular={regular} diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index e4708d27d6..9345022dae 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -547,7 +547,13 @@ export default class DayPickerRangeController extends React.PureComponent { startDateOffset, endDateOffset, } = this.props; - const { hoverDate, visibleDays } = this.state; + + const { + hoverDate, + visibleDays, + dateOffset: currentDateOffset, + } = this.state; + let dateOffset = null; if (focusedInput) { @@ -564,8 +570,8 @@ export default class DayPickerRangeController extends React.PureComponent { }; // eslint-disable-next-line react/destructuring-assignment - if (this.state.dateOffset && this.state.dateOffset.start && this.state.dateOffset.end) { - modifiers = this.deleteModifierFromRange(modifiers, this.state.dateOffset.start, this.state.dateOffset.end, 'hovered-offset'); + if (this.state.dateOffset && currentDateOffset.start && currentDateOffset.end) { + modifiers = this.deleteModifierFromRange(modifiers, currentDateOffset.start, currentDateOffset.end, 'hovered-offset'); } modifiers = this.addModifierToRange(modifiers, start, end, 'hovered-offset'); } diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index a796923e71..e0110159b8 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -197,6 +197,7 @@ class SingleDatePicker extends React.PureComponent { appendToBody, } = this.props; if (!focused) return; + if (!this.dayPickerContainer) return; if (appendToBody && this.dayPickerContainer.contains(event.target)) return; this.setState({ diff --git a/src/components/SingleDatePickerInputController.jsx b/src/components/SingleDatePickerInputController.jsx index 6f07f263a5..600ecfa403 100644 --- a/src/components/SingleDatePickerInputController.jsx +++ b/src/components/SingleDatePickerInputController.jsx @@ -25,6 +25,8 @@ import { } from '../constants'; const propTypes = forbidExtraProps({ + children: PropTypes.node, + date: momentPropTypes.momentObj, onDateChange: PropTypes.func.isRequired, @@ -190,6 +192,7 @@ export default class SingleDatePickerInputController extends React.PureComponent render() { const { + children, id, placeholder, disabled, @@ -250,7 +253,9 @@ export default class SingleDatePickerInputController extends React.PureComponent small={small} regular={regular} verticalSpacing={verticalSpacing} - /> + > + {children} + ); } } diff --git a/stories/.tern-port b/stories/.tern-port new file mode 100644 index 0000000000..24f778ea99 --- /dev/null +++ b/stories/.tern-port @@ -0,0 +1 @@ +56465 \ No newline at end of file diff --git a/test/components/SingleDatePicker_spec.jsx b/test/components/SingleDatePicker_spec.jsx index a38bdcd018..1c02c0688f 100644 --- a/test/components/SingleDatePicker_spec.jsx +++ b/test/components/SingleDatePicker_spec.jsx @@ -8,7 +8,6 @@ import CloseButton from '../../src/components/CloseButton'; import DayPickerSingleDateController from '../../src/components/DayPickerSingleDateController'; import SingleDatePickerInputController from '../../src/components/SingleDatePickerInputController'; import SingleDatePicker, { PureSingleDatePicker } from '../../src/components/SingleDatePicker'; -import DayPickerRangeController from "../../src/components/DayPickerRangeController"; const describeIfWindow = typeof document === 'undefined' ? describe.skip : describe; From 591e8befc4a4ae23ab6465c35e10a929f1f53422 Mon Sep 17 00:00:00 2001 From: Bailey Stoner Date: Fri, 11 Jan 2019 12:12:44 -0800 Subject: [PATCH 346/618] Fix specs. --- .tern-port | 1 - src/components/.tern-port | 1 - src/components/DateRangePickerInputController.jsx | 1 - src/components/SingleDatePicker.jsx | 2 +- stories/.tern-port | 1 - test/components/.tern-port | 1 + test/components/DateRangePickerInput_spec.jsx | 9 --------- test/components/SingleDatePicker_spec.jsx | 4 +--- 8 files changed, 3 insertions(+), 17 deletions(-) delete mode 100644 .tern-port delete mode 100644 src/components/.tern-port delete mode 100644 stories/.tern-port create mode 100644 test/components/.tern-port diff --git a/.tern-port b/.tern-port deleted file mode 100644 index dcb26926b3..0000000000 --- a/.tern-port +++ /dev/null @@ -1 +0,0 @@ -56258 \ No newline at end of file diff --git a/src/components/.tern-port b/src/components/.tern-port deleted file mode 100644 index 2dd3385e3f..0000000000 --- a/src/components/.tern-port +++ /dev/null @@ -1 +0,0 @@ -56254 \ No newline at end of file diff --git a/src/components/DateRangePickerInputController.jsx b/src/components/DateRangePickerInputController.jsx index 673702b3b8..97026260d5 100644 --- a/src/components/DateRangePickerInputController.jsx +++ b/src/components/DateRangePickerInputController.jsx @@ -328,7 +328,6 @@ export default class DateRangePickerInputController extends React.PureComponent onStartDateShiftTab={this.onClearFocus} onEndDateChange={this.onEndDateChange} onEndDateFocus={this.onEndDateFocus} - onEndDateTab={this.onClearFocus} showClearDates={showClearDates} onClearDates={this.clearDates} screenReaderMessage={screenReaderMessage} diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index e0110159b8..b30d14db45 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -196,8 +196,8 @@ class SingleDatePicker extends React.PureComponent { endDate, appendToBody, } = this.props; + if (!focused) return; - if (!this.dayPickerContainer) return; if (appendToBody && this.dayPickerContainer.contains(event.target)) return; this.setState({ diff --git a/stories/.tern-port b/stories/.tern-port deleted file mode 100644 index 24f778ea99..0000000000 --- a/stories/.tern-port +++ /dev/null @@ -1 +0,0 @@ -56465 \ No newline at end of file diff --git a/test/components/.tern-port b/test/components/.tern-port new file mode 100644 index 0000000000..11cb151f51 --- /dev/null +++ b/test/components/.tern-port @@ -0,0 +1 @@ +56872 \ No newline at end of file diff --git a/test/components/DateRangePickerInput_spec.jsx b/test/components/DateRangePickerInput_spec.jsx index 63d71aba48..1e8ca505b6 100644 --- a/test/components/DateRangePickerInput_spec.jsx +++ b/test/components/DateRangePickerInput_spec.jsx @@ -49,15 +49,6 @@ describe('DateRangePickerInput', () => { }); }); }); - - describe('props.children', () => { - it('should render children when provided', () => { - const Child = () =>
    CHILD
    ; - - const wrapper = shallow().dive(); - expect(wrapper.find('Child')).to.have.lengthOf(1); - }); - }); }); describe('props.customArrowIcon', () => { diff --git a/test/components/SingleDatePicker_spec.jsx b/test/components/SingleDatePicker_spec.jsx index 1c02c0688f..cbe0d3749c 100644 --- a/test/components/SingleDatePicker_spec.jsx +++ b/test/components/SingleDatePicker_spec.jsx @@ -413,9 +413,7 @@ describe('SingleDatePicker', () => { focused /> )).dive(); - wrapper.setState({ - isDayPickerFocused: true, - }); + wrapper.setState({ isDayPickerFocused: true }); wrapper.instance().onOutsideClick(); expect(wrapper.state().isDayPickerFocused).to.equal(false); }); From 69622f885a13804436099381a50ac1a0ac6eb81c Mon Sep 17 00:00:00 2001 From: Bailey Stoner Date: Fri, 11 Jan 2019 12:36:35 -0800 Subject: [PATCH 347/618] Fix issue caused when relatedTarget is sometimes null. --- src/components/DateRangePicker.jsx | 6 +++--- src/components/SingleDatePicker.jsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index f36e4a380f..ae03feea86 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -241,9 +241,9 @@ class DateRangePicker extends React.PureComponent { }); } - onDayPickerFocusOut(e) { - if (this.dayPickerContainer.contains(e.relatedTarget)) return; - this.onOutsideClick(e); + onDayPickerFocusOut(event) { + if (this.dayPickerContainer.contains(event.target || event.relatedTarget)) return; + this.onOutsideClick(event); } onDayPickerBlur() { diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index b30d14db45..ddd112e36f 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -252,7 +252,7 @@ class SingleDatePicker extends React.PureComponent { } onFocusOut(e) { - if (!this.container.contains(e.relatedTarget)) { + if (!this.container.contains(e.target || e.relatedTarget)) { const { onFocusChange } = this.props; onFocusChange({ focused: false }); } From bb22f7a5f6cc041583a2dc56f04cc28f1d0846af Mon Sep 17 00:00:00 2001 From: Bailey Stoner Date: Fri, 11 Jan 2019 12:47:01 -0800 Subject: [PATCH 348/618] Add comment explaining why we are using focusout here. --- src/components/DateRangePicker.jsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index ae03feea86..17be917c7b 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -266,6 +266,12 @@ class DateRangePicker extends React.PureComponent { } addDayPickerEventListeners() { + // NOTE: We are using a manual event listener here, because React doesn't + // provide FocusOut, while blur and keydown don't provide the information + // needed in order to know whether we have left focus or not. + // + // For reference, this issue is further described here: + // - https://github.com/facebook/react/issues/6410 this.removeDayPickerFocusOut = addEventListener( this.dayPickerContainer, 'focusout', From bb6391af2b48d5db76f6b7f0f694c3e87389e5ad Mon Sep 17 00:00:00 2001 From: Bailey Stoner Date: Fri, 11 Jan 2019 14:49:20 -0800 Subject: [PATCH 349/618] Fix issues with inconsistencies handling events between date pickers. --- src/components/DateRangePicker.jsx | 7 +++-- src/components/SingleDatePicker.jsx | 41 ++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 17be917c7b..c23fcfd141 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -242,7 +242,7 @@ class DateRangePicker extends React.PureComponent { } onDayPickerFocusOut(event) { - if (this.dayPickerContainer.contains(event.target || event.relatedTarget)) return; + if (this.dayPickerContainer.contains(event.relatedTarget || event.target)) return; this.onOutsideClick(event); } @@ -255,9 +255,12 @@ class DateRangePicker extends React.PureComponent { } setDayPickerContainerRef(ref) { + if (ref === this.dayPickerContainer) return; if (this.dayPickerContainer) this.removeDayPickerEventListeners(); - if (!ref) return; + this.dayPickerContainer = ref; + if (!ref) return; + this.addDayPickerEventListeners(); } diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index ddd112e36f..47633a7b2d 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -144,12 +144,13 @@ class SingleDatePicker extends React.PureComponent { /* istanbul ignore next */ componentDidMount() { - this.removeEventListener = addEventListener( + this.removeResizeEventListener = addEventListener( window, 'resize', this.responsivizePickerPosition, { passive: true }, ); + this.responsivizePickerPosition(); this.disableScroll(); @@ -162,11 +163,6 @@ class SingleDatePicker extends React.PureComponent { } this.isTouchDevice = isTouchDevice(); - - // We manually set event because React has not implemented onFocusIn/onFocusOut. - // Keep an eye on https://github.com/facebook/react/issues/6410 for updates - // We use "blur w/ useCapture param" vs "onfocusout" for FF browser support - this.container.addEventListener('blur', this.onFocusOut, true); } componentDidUpdate(prevProps) { @@ -181,10 +177,9 @@ class SingleDatePicker extends React.PureComponent { /* istanbul ignore next */ componentWillUnmount() { - if (this.removeEventListener) this.removeEventListener(); + if (this.removeResizeEventListener) this.removeResizeEventListener(); + if (this.removeFocusOutEventListener) this.removeFocusOutEventListener(); if (this.enableScroll) this.enableScroll(); - - this.container.removeEventListener('blur', this.onFocusOut, true); } onOutsideClick(event) { @@ -252,10 +247,9 @@ class SingleDatePicker extends React.PureComponent { } onFocusOut(e) { - if (!this.container.contains(e.target || e.relatedTarget)) { - const { onFocusChange } = this.props; - onFocusChange({ focused: false }); - } + const { onFocusChange } = this.props; + if (this.container.contains(e.relatedTarget || e.target)) return; + onFocusChange({ focused: false }); } setDayPickerContainerRef(ref) { @@ -263,7 +257,28 @@ class SingleDatePicker extends React.PureComponent { } setContainerRef(ref) { + if (ref === this.container) return; + this.removeEventListeners(); + this.container = ref; + if (!ref) return; + + this.addEventListeners(); + } + + addEventListeners() { + // We manually set event because React has not implemented onFocusIn/onFocusOut. + // Keep an eye on https://github.com/facebook/react/issues/6410 for updates + // We use "blur w/ useCapture param" vs "onfocusout" for FF browser support + this.removeFocusOutEventListener = addEventListener( + this.container, + 'focusout', + this.onFocusOut, + ); + } + + removeEventListeners() { + if (this.removeFocusOutEventListener) this.removeFocusOutEventListener(); } disableScroll() { From 7f35ad46b3964dd72f6a58592061b30308e66df2 Mon Sep 17 00:00:00 2001 From: Bailey Stoner Date: Mon, 14 Jan 2019 11:44:11 -0800 Subject: [PATCH 350/618] Fix issue where children had no default props. --- src/components/SingleDatePickerInputController.jsx | 4 +++- test/components/.tern-port | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) delete mode 100644 test/components/.tern-port diff --git a/src/components/SingleDatePickerInputController.jsx b/src/components/SingleDatePickerInputController.jsx index 600ecfa403..4982de4014 100644 --- a/src/components/SingleDatePickerInputController.jsx +++ b/src/components/SingleDatePickerInputController.jsx @@ -72,6 +72,8 @@ const propTypes = forbidExtraProps({ }); const defaultProps = { + children: null, + date: null, focused: false, @@ -254,7 +256,7 @@ export default class SingleDatePickerInputController extends React.PureComponent regular={regular} verticalSpacing={verticalSpacing} > - {children} + {children && children} ); } diff --git a/test/components/.tern-port b/test/components/.tern-port deleted file mode 100644 index 11cb151f51..0000000000 --- a/test/components/.tern-port +++ /dev/null @@ -1 +0,0 @@ -56872 \ No newline at end of file From b1d2abaac2580262858aaa1ead496442004bca11 Mon Sep 17 00:00:00 2001 From: Bailey Stoner Date: Mon, 14 Jan 2019 13:15:15 -0800 Subject: [PATCH 351/618] Add tests ensuring that DateRangePicker focusout works as expected. --- test/components/DateRangePicker_spec.jsx | 30 ++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/components/DateRangePicker_spec.jsx b/test/components/DateRangePicker_spec.jsx index bdcd013b12..f84462b8e1 100644 --- a/test/components/DateRangePicker_spec.jsx +++ b/test/components/DateRangePicker_spec.jsx @@ -567,6 +567,36 @@ describe('DateRangePicker', () => { wrapper.instance().onDayPickerBlur(); expect(wrapper.state().showKeyboardShortcuts).to.equal(false); }); + + it('tabbing out with keyboard behaves as an outside click', () => { + const target = sinon.stub(); + const onOutsideClick = sinon.stub(); + const dayPickerContainer = { + addEventListener: sinon.stub(), + contains: sinon.stub().returns(false), + }; + const wrapper = shallow(()).dive(); + wrapper.instance().setDayPickerContainerRef(dayPickerContainer); + wrapper.instance().onOutsideClick = onOutsideClick; + expect(onOutsideClick.callCount).to.equal(0); + wrapper.instance().onDayPickerFocusOut({ key: 'Tab', shiftKey: false, target }); + expect(onOutsideClick.callCount).to.equal(1); + }); + + + it('tabbing within itself does not behave as an outside click', () => { + const target = sinon.stub(); + const onOutsideClick = sinon.stub(); + const dayPickerContainer = { + addEventListener: sinon.stub(), + contains: sinon.stub().returns(true), + }; + const wrapper = shallow(()).dive(); + wrapper.instance().setDayPickerContainerRef(dayPickerContainer); + wrapper.instance().onOutsideClick = onOutsideClick; + wrapper.instance().onDayPickerFocusOut({ key: 'Tab', shiftKey: false, target }); + expect(onOutsideClick.callCount).to.equal(0); + }); }); describe('#showKeyboardShortcutsPanel', () => { From 19bdce9d926793b11fd0eeba61556b42f330f7ef Mon Sep 17 00:00:00 2001 From: Bailey Stoner Date: Tue, 22 Jan 2019 14:56:17 -0800 Subject: [PATCH 352/618] Add a comment explaining what is happening here. --- src/components/DateRangePicker.jsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index c23fcfd141..4ae3545767 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -242,6 +242,12 @@ class DateRangePicker extends React.PureComponent { } onDayPickerFocusOut(event) { + // In cases where **relatedTarget** is not null, it points to the right + // element here. However, in cases where it is null (such as clicking on a + // specific day), the appropriate value is **event.target**. + // + // We handle both situations here by using the ` || ` operator to fallback + // to *event.target** when **relatedTarget** is not provided. if (this.dayPickerContainer.contains(event.relatedTarget || event.target)) return; this.onOutsideClick(event); } From 84177a30400a1799400bae54723876d9d5a9d81e Mon Sep 17 00:00:00 2001 From: Bailey Stoner Date: Tue, 22 Jan 2019 15:05:55 -0800 Subject: [PATCH 353/618] Rename to `currentDateOffset` I'm worried that maybe this feels a bit redundant since anything in `this.state` should be "current", but it does help to solve the issue w/ naming being weird?... --- src/components/DayPickerRangeController.jsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 9345022dae..ec84c217d2 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -551,10 +551,10 @@ export default class DayPickerRangeController extends React.PureComponent { const { hoverDate, visibleDays, - dateOffset: currentDateOffset, + currentDateOffset, } = this.state; - let dateOffset = null; + let nextDateOffset = null; if (focusedInput) { const hasOffset = startDateOffset || endDateOffset; @@ -564,13 +564,13 @@ export default class DayPickerRangeController extends React.PureComponent { const start = getSelectedDateOffset(startDateOffset, day); const end = getSelectedDateOffset(endDateOffset, day, rangeDay => rangeDay.add(1, 'day')); - dateOffset = { + nextDateOffset = { start, end, }; // eslint-disable-next-line react/destructuring-assignment - if (this.state.dateOffset && currentDateOffset.start && currentDateOffset.end) { + if (this.state.nextDateOffset && currentDateOffset.start && currentDateOffset.end) { modifiers = this.deleteModifierFromRange(modifiers, currentDateOffset.start, currentDateOffset.end, 'hovered-offset'); } modifiers = this.addModifierToRange(modifiers, start, end, 'hovered-offset'); @@ -622,7 +622,7 @@ export default class DayPickerRangeController extends React.PureComponent { this.setState({ hoverDate: day, - dateOffset, + currentDateOffset: nextDateOffset, visibleDays: { ...visibleDays, ...modifiers, @@ -633,14 +633,14 @@ export default class DayPickerRangeController extends React.PureComponent { onDayMouseLeave(day) { const { startDate, endDate, minimumNights } = this.props; - const { hoverDate, visibleDays, dateOffset } = this.state; + const { hoverDate, visibleDays, currentDateOffset } = this.state; if (this.isTouchDevice || !hoverDate) return; let modifiers = {}; modifiers = this.deleteModifier(modifiers, hoverDate, 'hovered'); - if (dateOffset) { - modifiers = this.deleteModifierFromRange(modifiers, dateOffset.start, dateOffset.end, 'hovered-offset'); + if (currentDateOffset) { + modifiers = this.deleteModifierFromRange(modifiers, currentDateOffset.start, currentDateOffset.end, 'hovered-offset'); } if (startDate && !endDate && isAfterDay(hoverDate, startDate)) { From 2411c3c67207de647abde90ffaa5f6b51da539aa Mon Sep 17 00:00:00 2001 From: Bailey Stoner Date: Tue, 22 Jan 2019 15:06:42 -0800 Subject: [PATCH 354/618] Prevent redundancy by renaming local instead of state. --- src/components/DayPickerRangeController.jsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index ec84c217d2..2ba4192283 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -551,7 +551,7 @@ export default class DayPickerRangeController extends React.PureComponent { const { hoverDate, visibleDays, - currentDateOffset, + dateOffset, } = this.state; let nextDateOffset = null; @@ -570,8 +570,8 @@ export default class DayPickerRangeController extends React.PureComponent { }; // eslint-disable-next-line react/destructuring-assignment - if (this.state.nextDateOffset && currentDateOffset.start && currentDateOffset.end) { - modifiers = this.deleteModifierFromRange(modifiers, currentDateOffset.start, currentDateOffset.end, 'hovered-offset'); + if (this.state.nextDateOffset && dateOffset.start && dateOffset.end) { + modifiers = this.deleteModifierFromRange(modifiers, dateOffset.start, dateOffset.end, 'hovered-offset'); } modifiers = this.addModifierToRange(modifiers, start, end, 'hovered-offset'); } @@ -622,7 +622,7 @@ export default class DayPickerRangeController extends React.PureComponent { this.setState({ hoverDate: day, - currentDateOffset: nextDateOffset, + dateOffset: nextDateOffset, visibleDays: { ...visibleDays, ...modifiers, @@ -633,14 +633,14 @@ export default class DayPickerRangeController extends React.PureComponent { onDayMouseLeave(day) { const { startDate, endDate, minimumNights } = this.props; - const { hoverDate, visibleDays, currentDateOffset } = this.state; + const { hoverDate, visibleDays, dateOffset } = this.state; if (this.isTouchDevice || !hoverDate) return; let modifiers = {}; modifiers = this.deleteModifier(modifiers, hoverDate, 'hovered'); - if (currentDateOffset) { - modifiers = this.deleteModifierFromRange(modifiers, currentDateOffset.start, currentDateOffset.end, 'hovered-offset'); + if (dateOffset) { + modifiers = this.deleteModifierFromRange(modifiers, dateOffset.start, dateOffset.end, 'hovered-offset'); } if (startDate && !endDate && isAfterDay(hoverDate, startDate)) { From 98409065101a6140e3168daf4a6c2a2e4f7f9ae6 Mon Sep 17 00:00:00 2001 From: Bailey Stoner Date: Tue, 22 Jan 2019 15:08:44 -0800 Subject: [PATCH 355/618] Only show children if focused. Using `children &&` was odd. --- src/components/SingleDatePickerInputController.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SingleDatePickerInputController.jsx b/src/components/SingleDatePickerInputController.jsx index 4982de4014..dd2b31eba4 100644 --- a/src/components/SingleDatePickerInputController.jsx +++ b/src/components/SingleDatePickerInputController.jsx @@ -256,7 +256,7 @@ export default class SingleDatePickerInputController extends React.PureComponent regular={regular} verticalSpacing={verticalSpacing} > - {children && children} + {focused && children} ); } From dd9d6b27b300dc8e31d89fcd982d525845e9c698 Mon Sep 17 00:00:00 2001 From: Bailey Stoner Date: Tue, 22 Jan 2019 15:26:23 -0800 Subject: [PATCH 356/618] Using `focused &&` broke a test, but plain `{children}` should work. --- src/components/SingleDatePickerInputController.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SingleDatePickerInputController.jsx b/src/components/SingleDatePickerInputController.jsx index dd2b31eba4..cf3c28d320 100644 --- a/src/components/SingleDatePickerInputController.jsx +++ b/src/components/SingleDatePickerInputController.jsx @@ -256,7 +256,7 @@ export default class SingleDatePickerInputController extends React.PureComponent regular={regular} verticalSpacing={verticalSpacing} > - {focused && children} + {children} ); } From e137cea7eefd7f5eb14a526abe790d85ea364f7e Mon Sep 17 00:00:00 2001 From: Bailey Stoner Date: Tue, 22 Jan 2019 15:56:32 -0800 Subject: [PATCH 357/618] Fix issue using `this.state` as opposed to deconstructed value. --- src/components/DayPickerRangeController.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 2ba4192283..4a0fbd4c41 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -570,7 +570,7 @@ export default class DayPickerRangeController extends React.PureComponent { }; // eslint-disable-next-line react/destructuring-assignment - if (this.state.nextDateOffset && dateOffset.start && dateOffset.end) { + if (dateOffset && dateOffset.start && dateOffset.end) { modifiers = this.deleteModifierFromRange(modifiers, dateOffset.start, dateOffset.end, 'hovered-offset'); } modifiers = this.addModifierToRange(modifiers, start, end, 'hovered-offset'); From e8b71d2b121213a2f695fd370be50e717abb5194 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Wed, 23 Jan 2019 10:12:55 -0800 Subject: [PATCH 358/618] Version 18.4.1 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10adc22d12..4556eacb87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## 18.4.1 +- [fix] Make DRP and SDP calendars tabbable from the inputs ([#1499](https://github.com/airbnb/react-dates/pull/1499)) + ## 18.4.0 - [new] Clarify VoiceOver text for dates selected as start-date and end-date ([#1501](https://github.com/airbnb/react-dates/pull/1501)) diff --git a/package.json b/package.json index c45640d683..30c2e3b85a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "18.4.0", + "version": "18.4.1", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From 5789ad95c53c72829adf4ec344a2f9e54bbd8fa8 Mon Sep 17 00:00:00 2001 From: Nicole Kinser Date: Wed, 30 Jan 2019 11:26:53 -0800 Subject: [PATCH 359/618] Add aria-disabled prop to CalendarDay and CustomizableCalendarDay --- src/components/CalendarDay.jsx | 1 + src/components/CustomizableCalendarDay.jsx | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index 4832e7c07a..276bb5ef3d 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -142,6 +142,7 @@ class CalendarDay extends React.PureComponent { )} role="button" // eslint-disable-line jsx-a11y/no-noninteractive-element-to-interactive-role ref={this.setButtonRef} + aria-disabled={modifiers.has('blocked')} aria-label={ariaLabel} onMouseEnter={(e) => { this.onDayMouseEnter(day, e); }} onMouseLeave={(e) => { this.onDayMouseLeave(day, e); }} diff --git a/src/components/CustomizableCalendarDay.jsx b/src/components/CustomizableCalendarDay.jsx index 4d23e9a69a..fa864a83ff 100644 --- a/src/components/CustomizableCalendarDay.jsx +++ b/src/components/CustomizableCalendarDay.jsx @@ -335,6 +335,7 @@ class CustomizableCalendarDay extends React.PureComponent { )} role="button" // eslint-disable-line jsx-a11y/no-noninteractive-element-to-interactive-role ref={this.setButtonRef} + aria-disabled={modifiers.has('blocked')} aria-label={ariaLabel} onMouseEnter={(e) => { this.onDayMouseEnter(day, e); }} onMouseLeave={(e) => { this.onDayMouseLeave(day, e); }} From 60f503695b63616810949a23ee85fe71856c12f7 Mon Sep 17 00:00:00 2001 From: Marcos Avila Date: Fri, 1 Feb 2019 15:39:00 -0300 Subject: [PATCH 360/618] Fixes the focus out event in IE11 --- src/components/DateRangePicker.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 4ae3545767..8ada755948 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -248,7 +248,7 @@ class DateRangePicker extends React.PureComponent { // // We handle both situations here by using the ` || ` operator to fallback // to *event.target** when **relatedTarget** is not provided. - if (this.dayPickerContainer.contains(event.relatedTarget || event.target)) return; + if (this.dayPickerContainer.contains(event.relatedTarget || event.target) || this.dayPickerContainer.contains(event.target)) return; this.onOutsideClick(event); } From 82b6752b595d32b3bf95f6c44412623707421e33 Mon Sep 17 00:00:00 2001 From: Marcos Avila Date: Fri, 1 Feb 2019 16:00:53 -0300 Subject: [PATCH 361/618] Fixes the focus out event in IE11 (#2) --- src/components/DateRangePicker.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 8ada755948..b46dedeaf0 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -248,7 +248,8 @@ class DateRangePicker extends React.PureComponent { // // We handle both situations here by using the ` || ` operator to fallback // to *event.target** when **relatedTarget** is not provided. - if (this.dayPickerContainer.contains(event.relatedTarget || event.target) || this.dayPickerContainer.contains(event.target)) return; + if (this.dayPickerContainer.contains(event.relatedTarget || event.target) + || this.dayPickerContainer.contains(event.target)) return; this.onOutsideClick(event); } From 3b23397c5de370135eb521f8bae42545eeeaeb16 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 3 Feb 2019 14:23:02 -0800 Subject: [PATCH 362/618] [Dev Deps] update `why-did-you-update` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 30c2e3b85a..9a2e8e4182 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "sinon-sandbox": "^2.0.0", "style-loader": "^0.20.3", "webpack": "^4.23.1", - "why-did-you-update": "^0.1.1" + "why-did-you-update": "^1.0.6" }, "dependencies": { "airbnb-prop-types": "^2.10.0", From 23556987c43228d7cb89c6506c18fa3571ae3adf Mon Sep 17 00:00:00 2001 From: Jake Clements Date: Tue, 11 Sep 2018 13:17:11 +1000 Subject: [PATCH 363/618] Add date offset functionality to DateRangePicker --- src/components/DateRangePicker.jsx | 6 +++ src/components/DayPickerRangeController.jsx | 4 ++ src/shapes/DateRangePickerShape.js | 2 + stories/DateRangePicker_calendar.js | 6 +++ test/components/DateRangePicker_spec.jsx | 42 +++++++++++++++++++ .../DayPickerRangeController_spec.jsx | 29 +++++++++++++ 6 files changed, 89 insertions(+) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 4ae3545767..95bfbeafc6 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -51,6 +51,8 @@ const defaultProps = { // input related props startDatePlaceholderText: 'Start Date', endDatePlaceholderText: 'End Date', + startDateOffset: undefined, + endDateOffset: undefined, disabled: false, required: false, readOnly: false, @@ -402,7 +404,9 @@ class DateRangePicker extends React.PureComponent { enableOutsideDays, focusedInput, startDate, + startDateOffset, endDate, + endDateOffset, minimumNights, keepOpenOnDateSelect, renderCalendarDay, @@ -479,7 +483,9 @@ class DateRangePicker extends React.PureComponent { onClose={onClose} focusedInput={focusedInput} startDate={startDate} + startDateOffset={startDateOffset} endDate={endDate} + endDateOffset={endDateOffset} monthFormat={monthFormat} renderMonthText={renderMonthText} withPortal={withAnyPortal} diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 4a0fbd4c41..039b63b4c2 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -491,6 +491,10 @@ export default class DayPickerRangeController extends React.PureComponent { startDate = getSelectedDateOffset(startDateOffset, day); endDate = getSelectedDateOffset(endDateOffset, day); + if (this.isBlocked(startDate) || this.isBlocked(endDate)) { + return; + } + if (!keepOpenOnDateSelect) { onFocusChange(null); onClose({ startDate, endDate }); diff --git a/src/shapes/DateRangePickerShape.js b/src/shapes/DateRangePickerShape.js index 5c73cfa4d9..05454c153e 100644 --- a/src/shapes/DateRangePickerShape.js +++ b/src/shapes/DateRangePickerShape.js @@ -28,6 +28,8 @@ export default { // input related props startDateId: PropTypes.string.isRequired, startDatePlaceholderText: PropTypes.string, + startDateOffset: PropTypes.func, + endDateOffset: PropTypes.func, endDateId: PropTypes.string.isRequired, endDatePlaceholderText: PropTypes.string, disabled: DisabledShape, diff --git a/stories/DateRangePicker_calendar.js b/stories/DateRangePicker_calendar.js index e5de2142fe..c465937f70 100644 --- a/stories/DateRangePicker_calendar.js +++ b/stories/DateRangePicker_calendar.js @@ -59,6 +59,12 @@ storiesOf('DRP - Calendar Props', module) .add('3 months', withInfo()(() => ( ))) + .add('with 7 days range selection', withInfo()(() => ( + day.subtract(3, 'days')} + endDateOffset={day => day.add(3, 'days')} + /> + ))) .add('with custom day size', withInfo()(() => ( ))) diff --git a/test/components/DateRangePicker_spec.jsx b/test/components/DateRangePicker_spec.jsx index f84462b8e1..62413f260f 100644 --- a/test/components/DateRangePicker_spec.jsx +++ b/test/components/DateRangePicker_spec.jsx @@ -699,4 +699,46 @@ describe('DateRangePicker', () => { }); }); }); + + describe('dateOffsets', () => { + describe('startDateOffset is passed in', () => { + it('Should pass startDateOffset to DayPickerRangeController', () => { + const startDate = moment('2018-10-17'); + const onDatesChangeStub = sinon.stub(); + const wrapper = shallow(( + date.subtract(5, 'days')} + onDatesChange={onDatesChangeStub} + focusedInput={START_DATE} + /> + )).dive(); + + const dayPicker = wrapper.find(DayPickerRangeController); + const dayPickerStartDateOffset = dayPicker.props().startDateOffset(startDate); + + expect(dayPickerStartDateOffset.format()).to.equal(startDate.format()); + }); + }); + + describe('endDateOffset is passed in', () => { + it('Should pass endDateOffset to DayPickerRangeController', () => { + const endDate = moment('2018-10-17', 'YYYY-MM-DD'); + const onDatesChangeStub = sinon.stub(); + const wrapper = shallow(( + date.subtract(5, 'days')} + onDatesChange={onDatesChangeStub} + focusedInput={START_DATE} + /> + )).dive(); + + const dayPicker = wrapper.find(DayPickerRangeController); + const dayPickerEndDateOffset = dayPicker.props().endDateOffset(endDate); + + expect(dayPickerEndDateOffset.format()).to.equal(endDate.format()); + }); + }); + }); }); diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index b57f1c9354..bf82146820 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -1563,6 +1563,35 @@ describe('DayPickerRangeController', () => { expect(args.endDate.format()).to.equal(clickDate.clone().add(4, 'days').format()); }); + it('does not call props.onDatesChange with startDate === startDateOffset(date) and endDate === endDateOffset(date)', () => { + const clickDate = moment(today).clone().add(2, 'days'); + const onDatesChangeStub = sinon.spy(); + const wrapper = shallow(( + day.subtract(2, 'days')} + endDateOffset={day => day.add(4, 'days')} + isOutsideRange={day => day.isAfter(moment(today))} + /> + )); + wrapper.instance().onDayClick(clickDate); + expect(onDatesChangeStub).to.have.property('callCount', 0); + }); + + it('does not call props.onDatesChange when dateOffset isOutsideRange', () => { + const clickDate = moment(today); + const onDatesChangeStub = sinon.stub(); + const wrapper = shallow(( + day.add(5, 'days')} + isOutsideRange={day => day.isAfter(moment(today).clone().add(1, 'days'))} + /> + )); + wrapper.instance().onDayClick(clickDate); + expect(onDatesChangeStub).to.have.property('callCount', 0); + }); + it('calls props.onDatesChange with startDate === startDateOffset(date) and endDate === selectedDate when endDateOffset not provided', () => { const clickDate = moment(today).clone().add(2, 'days'); const onDatesChangeStub = sinon.stub(); From 4076cc5a7a51b0f5e28c11a4ba9e6c4da119cfab Mon Sep 17 00:00:00 2001 From: Nicole Kinser Date: Tue, 5 Feb 2019 13:29:39 -0800 Subject: [PATCH 364/618] Add tests to demonstrate how logic of onDatesChange can affect onFocusChange --- .../DayPickerRangeController_spec.jsx | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index bf82146820..c5de000418 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -1622,6 +1622,57 @@ describe('DayPickerRangeController', () => { expect(args.endDate.format()).to.equal(clickDate.clone().add(12, 'days').format()); }); }); + + describe('logic in props.onDatesChange affects props.onFocusChange', () => { + let preventFocusChange; + let focusedInput; + let onDatesChange; + let onFocusChange; + beforeEach(() => { + preventFocusChange = false; + focusedInput = START_DATE; + onDatesChange = ({ startDate }) => { + if (isSameDay(startDate, today)) preventFocusChange = true; + }; + onFocusChange = (input) => { + if (!preventFocusChange) { + focusedInput = input; + } else { + preventFocusChange = false; + } + }; + }); + + it('calls onDayClick with a day that prevents a focus change', () => { + const clickDate = moment(today); + const wrapper = shallow(( + + )); + // The first day click sets preventFocusChange to true, but it doesn't take effect until the + // second day click because onFocusChange is called before onDatesChange + wrapper.instance().onDayClick(clickDate); + expect(focusedInput).to.equal(END_DATE); + wrapper.instance().onDayClick(clickDate.clone().add(1, 'days')); + expect(focusedInput).to.equal(END_DATE); + }); + + it('calls onDayClick with a day that does not prevent a focus change', () => { + const clickDate = moment(today).clone().add(2, 'days'); + const wrapper = shallow(( + + )); + wrapper.instance().onDayClick(clickDate); + expect(focusedInput).to.equal(END_DATE); + }); + }); }); describe('#onDayMouseEnter', () => { From fe1d97cfb8c02d5b8023801233f733cefb7b46ab Mon Sep 17 00:00:00 2001 From: Nicole Kinser Date: Mon, 4 Feb 2019 15:10:34 -0800 Subject: [PATCH 365/618] Move the call to onDatesChange before the call to onFocusChange --- src/components/DayPickerRangeController.jsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 039b63b4c2..e2c538081b 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -490,6 +490,7 @@ export default class DayPickerRangeController extends React.PureComponent { if (startDateOffset || endDateOffset) { startDate = getSelectedDateOffset(startDateOffset, day); endDate = getSelectedDateOffset(endDateOffset, day); + onDatesChange({ startDate, endDate }); if (this.isBlocked(startDate) || this.isBlocked(endDate)) { return; @@ -512,6 +513,8 @@ export default class DayPickerRangeController extends React.PureComponent { } } + onDatesChange({ startDate, endDate }); + if (isEndDateDisabled && !isStartDateAfterEndDate) { onFocusChange(null); onClose({ startDate, endDate }); @@ -523,9 +526,11 @@ export default class DayPickerRangeController extends React.PureComponent { if (!startDate) { endDate = day; + onDatesChange({ startDate, endDate }); onFocusChange(START_DATE); } else if (isInclusivelyAfterDay(day, firstAllowedEndDate)) { endDate = day; + onDatesChange({ startDate, endDate }); if (!keepOpenOnDateSelect) { onFocusChange(null); onClose({ startDate, endDate }); @@ -533,10 +538,14 @@ export default class DayPickerRangeController extends React.PureComponent { } else if (disabled !== START_DATE) { startDate = day; endDate = null; + onDatesChange({ startDate, endDate }); + } else { + onDatesChange({ startDate, endDate }); } + } else { + onDatesChange({ startDate, endDate }); } - onDatesChange({ startDate, endDate }); onBlur(); } From c89cc3c0ce4bd040a3ef5e5197bd0aaa32c0ab4a Mon Sep 17 00:00:00 2001 From: Nicole Kinser Date: Mon, 4 Feb 2019 16:47:07 -0800 Subject: [PATCH 366/618] resolved rebased --- src/components/DayPickerRangeController.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index e2c538081b..13b67e2807 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -490,12 +490,13 @@ export default class DayPickerRangeController extends React.PureComponent { if (startDateOffset || endDateOffset) { startDate = getSelectedDateOffset(startDateOffset, day); endDate = getSelectedDateOffset(endDateOffset, day); - onDatesChange({ startDate, endDate }); if (this.isBlocked(startDate) || this.isBlocked(endDate)) { return; } + onDatesChange({ startDate, endDate }); + if (!keepOpenOnDateSelect) { onFocusChange(null); onClose({ startDate, endDate }); From aaf2ab645f9c614be0cec563f951c0a64850d727 Mon Sep 17 00:00:00 2001 From: Nicole Kinser Date: Tue, 5 Feb 2019 13:15:05 -0800 Subject: [PATCH 367/618] added tests to verify onDatesChange only called once --- .../DayPickerRangeController_spec.jsx | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index c5de000418..27773020b4 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -1623,6 +1623,133 @@ describe('DayPickerRangeController', () => { }); }); + describe('props.onDatesChange only called once in onDayClick', () => { + it('calls props.onDatesChange once when focusedInput === START_DATE', () => { + const clickDate = moment(today); + const onDatesChangeStub = sinon.stub(); + const wrapper = shallow(( + + )); + wrapper.instance().onDayClick(clickDate); + expect(onDatesChangeStub.callCount).to.equal(1); + const args = onDatesChangeStub.getCall(0).args[0]; + expect(args.startDate.format()).to.equal(clickDate.clone().format()); + expect(args.endDate).to.equal(null); + }); + + it('calls props.onDatesChange once when focusedInput === END_DATE and there is no startDate', () => { + const clickDate = moment(today); + const onDatesChangeStub = sinon.stub(); + const wrapper = shallow(( + + )); + wrapper.instance().onDayClick(clickDate); + expect(onDatesChangeStub.callCount).to.equal(1); + const args = onDatesChangeStub.getCall(0).args[0]; + expect(args.startDate).to.equal(null); + expect(args.endDate.format()).to.equal(clickDate.clone().format()); + }); + + it('calls props.onDatesChange once when focusedInput === END_DATE and the day is a valid endDate', () => { + const clickDate = moment(today); + const startDate = clickDate.clone().subtract(2, 'days'); + const onDatesChangeStub = sinon.stub(); + const wrapper = shallow(( + + )); + wrapper.instance().onDayClick(clickDate); + expect(onDatesChangeStub.callCount).to.equal(1); + const args = onDatesChangeStub.getCall(0).args[0]; + expect(args.startDate.format()).to.equal(startDate.clone().format()); + expect(args.endDate.format()).to.equal(clickDate.clone().format()); + }); + + it('calls props.onDatesChange once when focusedInput === END_DATE, the day is an invalid endDate, and disabled !== START_DATE', () => { + const clickDate = moment(today); + const onDatesChangeStub = sinon.stub(); + const wrapper = shallow(( + + )); + wrapper.instance().onDayClick(clickDate); + expect(onDatesChangeStub.callCount).to.equal(1); + const args = onDatesChangeStub.getCall(0).args[0]; + expect(args.startDate.format()).to.equal(clickDate.clone().format()); + expect(args.endDate).to.equal(null); + }); + + it('calls props.onDatesChange once when focusedInput === END_DATE and the day is an invalid endDate', () => { + const clickDate = moment(today); + const startDate = clickDate.clone().add(1, 'days'); + const onDatesChangeStub = sinon.stub(); + const wrapper = shallow(( + + )); + wrapper.instance().onDayClick(clickDate); + expect(onDatesChangeStub.callCount).to.equal(1); + const args = onDatesChangeStub.getCall(0).args[0]; + expect(args.startDate.format()).to.equal(startDate.clone().format()); + expect(args.endDate).to.equal(null); + }); + + it('calls props.onDatesChange once when there is a startDateOffset', () => { + const clickDate = moment(today); + const onDatesChangeStub = sinon.stub(); + const wrapper = shallow(( + day.subtract(2, 'days')} + /> + )); + wrapper.instance().onDayClick(clickDate); + expect(onDatesChangeStub.callCount).to.equal(1); + const args = onDatesChangeStub.getCall(0).args[0]; + expect(args.startDate.format()).to.equal(clickDate.clone().subtract(2, 'days').format()); + expect(args.endDate.format()).to.equal(clickDate.clone().format()); + }); + + it('calls props.onDatesChange once when there is a endDateOffset', () => { + const clickDate = moment(today); + const onDatesChangeStub = sinon.stub(); + const wrapper = shallow(( + day.add(4, 'days')} + /> + )); + wrapper.instance().onDayClick(clickDate); + expect(onDatesChangeStub.callCount).to.equal(1); + const args = onDatesChangeStub.getCall(0).args[0]; + expect(args.startDate.format()).to.equal(clickDate.clone().format()); + expect(args.endDate.format()).to.equal(clickDate.clone().add(4, 'days').format()); + }); + }); + describe('logic in props.onDatesChange affects props.onFocusChange', () => { let preventFocusChange; let focusedInput; From 21e9bb5ef79ecc9f9e1f9c05129d3308937afd09 Mon Sep 17 00:00:00 2001 From: Nicole Kinser Date: Tue, 5 Feb 2019 13:33:03 -0800 Subject: [PATCH 368/618] changed way I retrieve callCount --- test/components/DayPickerRangeController_spec.jsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index 27773020b4..851a54a138 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -1635,7 +1635,7 @@ describe('DayPickerRangeController', () => { /> )); wrapper.instance().onDayClick(clickDate); - expect(onDatesChangeStub.callCount).to.equal(1); + expect(onDatesChangeStub).to.have.property('callCount', 1); const args = onDatesChangeStub.getCall(0).args[0]; expect(args.startDate.format()).to.equal(clickDate.clone().format()); expect(args.endDate).to.equal(null); @@ -1652,7 +1652,7 @@ describe('DayPickerRangeController', () => { /> )); wrapper.instance().onDayClick(clickDate); - expect(onDatesChangeStub.callCount).to.equal(1); + expect(onDatesChangeStub).to.have.property('callCount', 1); const args = onDatesChangeStub.getCall(0).args[0]; expect(args.startDate).to.equal(null); expect(args.endDate.format()).to.equal(clickDate.clone().format()); @@ -1671,7 +1671,7 @@ describe('DayPickerRangeController', () => { /> )); wrapper.instance().onDayClick(clickDate); - expect(onDatesChangeStub.callCount).to.equal(1); + expect(onDatesChangeStub).to.have.property('callCount', 1); const args = onDatesChangeStub.getCall(0).args[0]; expect(args.startDate.format()).to.equal(startDate.clone().format()); expect(args.endDate.format()).to.equal(clickDate.clone().format()); @@ -1690,7 +1690,7 @@ describe('DayPickerRangeController', () => { /> )); wrapper.instance().onDayClick(clickDate); - expect(onDatesChangeStub.callCount).to.equal(1); + expect(onDatesChangeStub).to.have.property('callCount', 1); const args = onDatesChangeStub.getCall(0).args[0]; expect(args.startDate.format()).to.equal(clickDate.clone().format()); expect(args.endDate).to.equal(null); @@ -1711,7 +1711,7 @@ describe('DayPickerRangeController', () => { /> )); wrapper.instance().onDayClick(clickDate); - expect(onDatesChangeStub.callCount).to.equal(1); + expect(onDatesChangeStub).to.have.property('callCount', 1); const args = onDatesChangeStub.getCall(0).args[0]; expect(args.startDate.format()).to.equal(startDate.clone().format()); expect(args.endDate).to.equal(null); @@ -1727,7 +1727,7 @@ describe('DayPickerRangeController', () => { /> )); wrapper.instance().onDayClick(clickDate); - expect(onDatesChangeStub.callCount).to.equal(1); + expect(onDatesChangeStub).to.have.property('callCount', 1); const args = onDatesChangeStub.getCall(0).args[0]; expect(args.startDate.format()).to.equal(clickDate.clone().subtract(2, 'days').format()); expect(args.endDate.format()).to.equal(clickDate.clone().format()); @@ -1743,7 +1743,7 @@ describe('DayPickerRangeController', () => { /> )); wrapper.instance().onDayClick(clickDate); - expect(onDatesChangeStub.callCount).to.equal(1); + expect(onDatesChangeStub).to.have.property('callCount', 1); const args = onDatesChangeStub.getCall(0).args[0]; expect(args.startDate.format()).to.equal(clickDate.clone().format()); expect(args.endDate.format()).to.equal(clickDate.clone().add(4, 'days').format()); From 95ee6367a99affec66bb524a8310a5490a2462f7 Mon Sep 17 00:00:00 2001 From: Nicole Kinser Date: Tue, 5 Feb 2019 14:19:55 -0800 Subject: [PATCH 369/618] adjust test --- test/components/DayPickerRangeController_spec.jsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index 851a54a138..40e7282fef 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -1779,10 +1779,8 @@ describe('DayPickerRangeController', () => { focusedInput={START_DATE} /> )); - // The first day click sets preventFocusChange to true, but it doesn't take effect until the - // second day click because onFocusChange is called before onDatesChange wrapper.instance().onDayClick(clickDate); - expect(focusedInput).to.equal(END_DATE); + expect(focusedInput).to.equal(START_DATE); wrapper.instance().onDayClick(clickDate.clone().add(1, 'days')); expect(focusedInput).to.equal(END_DATE); }); From 2d3e390e3ab747d0694288f527db41ee2954db93 Mon Sep 17 00:00:00 2001 From: Marcos Avila Date: Wed, 6 Feb 2019 15:26:15 -0300 Subject: [PATCH 370/618] Requested changes #1524 --- src/components/DateRangePicker.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 0917b956a0..9cef6e4205 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -250,8 +250,9 @@ class DateRangePicker extends React.PureComponent { // // We handle both situations here by using the ` || ` operator to fallback // to *event.target** when **relatedTarget** is not provided. - if (this.dayPickerContainer.contains(event.relatedTarget || event.target) - || this.dayPickerContainer.contains(event.target)) return; + const { contains } = this.dayPickerContainer; + const { relatedTarget, target } = event; + if (contains(relatedTarget) || contains(target)) return; this.onOutsideClick(event); } From e66ae14bb29f09433a847dfa24d00bfccdb2183e Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Wed, 6 Feb 2019 14:01:00 -0800 Subject: [PATCH 371/618] Version 18.5.0 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4556eacb87..dc476745e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## 18.5.0 +- [fix] Add `aria-disabled` attribute to the (Customizable)CalendarDay ([#1521](https://github.com/airbnb/react-dates/pull/1521)) +- [new] Add `startDateOffset` and `endDateOffset` props to the DRP ([#1252](https://github.com/airbnb/react-dates/pull/1252)) + ## 18.4.1 - [fix] Make DRP and SDP calendars tabbable from the inputs ([#1499](https://github.com/airbnb/react-dates/pull/1499)) diff --git a/package.json b/package.json index 9a2e8e4182..b66f5903d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "18.4.1", + "version": "18.5.0", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From 64f0be97843d3627593260e3a73ff6a568ca8a54 Mon Sep 17 00:00:00 2001 From: Marcos Avila Date: Wed, 6 Feb 2019 19:12:43 -0300 Subject: [PATCH 372/618] Requested changes #1524 --- src/components/DateRangePicker.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 9cef6e4205..d3119ae6e0 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -250,9 +250,8 @@ class DateRangePicker extends React.PureComponent { // // We handle both situations here by using the ` || ` operator to fallback // to *event.target** when **relatedTarget** is not provided. - const { contains } = this.dayPickerContainer; - const { relatedTarget, target } = event; - if (contains(relatedTarget) || contains(target)) return; + const relatedTarget = event.relatedTarget === document.body ? event.target : event.relatedTarget || event.target; + if (this.dayPickerContainer.contains(relatedTarget)) return; this.onOutsideClick(event); } From 188b4a99c88482ef21e6923b4f7dfe7029234ff8 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Wed, 6 Feb 2019 20:07:23 -0300 Subject: [PATCH 373/618] Update src/components/DateRangePicker.jsx Co-Authored-By: marcos0x --- src/components/DateRangePicker.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index d3119ae6e0..481fed5142 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -250,7 +250,7 @@ class DateRangePicker extends React.PureComponent { // // We handle both situations here by using the ` || ` operator to fallback // to *event.target** when **relatedTarget** is not provided. - const relatedTarget = event.relatedTarget === document.body ? event.target : event.relatedTarget || event.target; + const relatedTarget = event.relatedTarget === document.body ? event.target : (event.relatedTarget || event.target); if (this.dayPickerContainer.contains(relatedTarget)) return; this.onOutsideClick(event); } From 143119d6d5971d6c043214e0699e100121e75d8b Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Wed, 6 Feb 2019 15:26:11 -0800 Subject: [PATCH 374/618] Version 19.0.0 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc476745e6..5ccdeb31a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## 19.0.0 +- [breaking] Call `onDatesChange` before `onFocusChange` in the DRP ([#1525](https://github.com/airbnb/react-dates/pull/1525)) + ## 18.5.0 - [fix] Add `aria-disabled` attribute to the (Customizable)CalendarDay ([#1521](https://github.com/airbnb/react-dates/pull/1521)) - [new] Add `startDateOffset` and `endDateOffset` props to the DRP ([#1252](https://github.com/airbnb/react-dates/pull/1252)) diff --git a/package.json b/package.json index b66f5903d7..5a32662935 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "18.5.0", + "version": "19.0.0", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From 5392e33c8c4b7c3994a1d08bd3d79c7380097b3d Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 6 Feb 2019 21:40:24 -0300 Subject: [PATCH 375/618] Updates the comment --- src/components/DateRangePicker.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 481fed5142..e2aad78f89 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -246,7 +246,7 @@ class DateRangePicker extends React.PureComponent { onDayPickerFocusOut(event) { // In cases where **relatedTarget** is not null, it points to the right // element here. However, in cases where it is null (such as clicking on a - // specific day), the appropriate value is **event.target**. + // specific day) or it is **document.body** (IE11), the appropriate value is **event.target**. // // We handle both situations here by using the ` || ` operator to fallback // to *event.target** when **relatedTarget** is not provided. From 1191688d2a21e2efa772eff410a7c0dc3be10c83 Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 6 Feb 2019 21:42:37 -0300 Subject: [PATCH 376/618] Updates the comment --- src/components/DateRangePicker.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index e2aad78f89..db58b1a366 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -250,7 +250,9 @@ class DateRangePicker extends React.PureComponent { // // We handle both situations here by using the ` || ` operator to fallback // to *event.target** when **relatedTarget** is not provided. - const relatedTarget = event.relatedTarget === document.body ? event.target : (event.relatedTarget || event.target); + const relatedTarget = event.relatedTarget === document.body + ? event.target + : (event.relatedTarget || event.target); if (this.dayPickerContainer.contains(relatedTarget)) return; this.onOutsideClick(event); } From 60f5f3bd7beb25f8570045d87afe58a66173f383 Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 6 Feb 2019 22:20:50 -0300 Subject: [PATCH 377/618] Fix test --- test/components/DateRangePicker_spec.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/components/DateRangePicker_spec.jsx b/test/components/DateRangePicker_spec.jsx index 62413f260f..e573a0cf90 100644 --- a/test/components/DateRangePicker_spec.jsx +++ b/test/components/DateRangePicker_spec.jsx @@ -522,7 +522,7 @@ describe('DateRangePicker', () => { }); }); - describe('#onDayPickerBlur', () => { + describeIfWindow('#onDayPickerBlur', () => { it('sets state.isDateRangePickerInputFocused to true', () => { const wrapper = shallow(( Date: Wed, 6 Feb 2019 17:28:00 -0800 Subject: [PATCH 378/618] Fix single date picker not responding to input. --- src/components/SingleDatePicker.jsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index 47633a7b2d..2e0e2fb2b6 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -253,25 +253,25 @@ class SingleDatePicker extends React.PureComponent { } setDayPickerContainerRef(ref) { - this.dayPickerContainer = ref; - } - - setContainerRef(ref) { - if (ref === this.container) return; + if (ref === this.dayPickerContainer) return; this.removeEventListeners(); - this.container = ref; + this.dayPickerContainer = ref; if (!ref) return; this.addEventListeners(); } + setContainerRef(ref) { + this.container = ref; + } + addEventListeners() { // We manually set event because React has not implemented onFocusIn/onFocusOut. // Keep an eye on https://github.com/facebook/react/issues/6410 for updates // We use "blur w/ useCapture param" vs "onfocusout" for FF browser support this.removeFocusOutEventListener = addEventListener( - this.container, + this.dayPickerContainer, 'focusout', this.onFocusOut, ); @@ -307,6 +307,7 @@ class SingleDatePicker extends React.PureComponent { appendToBody, focused, } = this.props; + const { dayPickerContainerStyles } = this.state; if (!focused) { From bb23531fa5cc5602f4ee300335072c86386ff0e5 Mon Sep 17 00:00:00 2001 From: Nicole Kinser Date: Thu, 7 Feb 2019 16:53:04 -0800 Subject: [PATCH 379/618] Version 19.0.1 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ccdeb31a3..8a5c453eb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## 19.0.1 +- [fix] Fix single date picker not responding to input ([#1533](https://github.com/airbnb/react-dates/pull/1533)) +- [fix] Fixes the focus out event in IE11 ([#1524](https://github.com/airbnb/react-dates/pull/1524)) + ## 19.0.0 - [breaking] Call `onDatesChange` before `onFocusChange` in the DRP ([#1525](https://github.com/airbnb/react-dates/pull/1525)) diff --git a/package.json b/package.json index 5a32662935..22a056bced 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "19.0.0", + "version": "19.0.1", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From 7c287db8fd013f45232c67e0456c530a1184279f Mon Sep 17 00:00:00 2001 From: Nicole Kinser Date: Fri, 8 Feb 2019 13:31:44 -0800 Subject: [PATCH 380/618] Version 19.0.2 --- CHANGELOG.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a5c453eb0..df0d035bd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ --> ## 19.0.1 -- [fix] Fix single date picker not responding to input ([#1533](https://github.com/airbnb/react-dates/pull/1533)) +- [fix] Fix single date picker not responding to input ([#1533](https://github.com/airbnb/react-dates/pull/1533)) - [fix] Fixes the focus out event in IE11 ([#1524](https://github.com/airbnb/react-dates/pull/1524)) ## 19.0.0 diff --git a/package.json b/package.json index 22a056bced..6333ba20d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "19.0.1", + "version": "19.0.2", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From d4a6eeb0e4a9ee868a878cfc97bc369182f3fb94 Mon Sep 17 00:00:00 2001 From: Dane David Date: Mon, 11 Feb 2019 00:35:45 +0530 Subject: [PATCH 381/618] Fix date selection issue with withPortal or appendToBody. Fixes #1535. --- src/components/SingleDatePicker.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index 2e0e2fb2b6..0d631faf97 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -248,7 +248,7 @@ class SingleDatePicker extends React.PureComponent { onFocusOut(e) { const { onFocusChange } = this.props; - if (this.container.contains(e.relatedTarget || e.target)) return; + if (this.dayPickerContainer.contains(e.relatedTarget || e.target)) return; onFocusChange({ focused: false }); } From 13f5486c7cb72712e6c9527c832abc6a5cceead0 Mon Sep 17 00:00:00 2001 From: Dane David Date: Mon, 11 Feb 2019 01:52:52 +0530 Subject: [PATCH 382/618] Add check for ref definition inside onFocusOut. --- src/components/SingleDatePicker.jsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index 0d631faf97..d9720c6123 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -248,7 +248,12 @@ class SingleDatePicker extends React.PureComponent { onFocusOut(e) { const { onFocusChange } = this.props; - if (this.dayPickerContainer.contains(e.relatedTarget || e.target)) return; + if ( + this.dayPickerContainer + && this.dayPickerContainer.contains(e.relatedTarget || e.target) + ) { + return; + } onFocusChange({ focused: false }); } From 69962a6615495aff28ec0c93a45ac74544007738 Mon Sep 17 00:00:00 2001 From: Dane David Date: Mon, 11 Feb 2019 21:46:59 +0530 Subject: [PATCH 383/618] Revert "Add check for ref definition inside onFocusOut." This reverts commit 13f5486c7cb72712e6c9527c832abc6a5cceead0. --- src/components/SingleDatePicker.jsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index d9720c6123..0d631faf97 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -248,12 +248,7 @@ class SingleDatePicker extends React.PureComponent { onFocusOut(e) { const { onFocusChange } = this.props; - if ( - this.dayPickerContainer - && this.dayPickerContainer.contains(e.relatedTarget || e.target) - ) { - return; - } + if (this.dayPickerContainer.contains(e.relatedTarget || e.target)) return; onFocusChange({ focused: false }); } From 0351ccb87b206f48d586e45acd4fe1386e415223 Mon Sep 17 00:00:00 2001 From: Dane David Date: Mon, 11 Feb 2019 21:51:20 +0530 Subject: [PATCH 384/618] Update SingleDatePicker test to use proper instance ref. --- test/components/SingleDatePicker_spec.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/components/SingleDatePicker_spec.jsx b/test/components/SingleDatePicker_spec.jsx index cbe0d3749c..a0094df441 100644 --- a/test/components/SingleDatePicker_spec.jsx +++ b/test/components/SingleDatePicker_spec.jsx @@ -602,7 +602,7 @@ describe('SingleDatePicker', () => { )).dive(); - wrapper.instance().container = { + wrapper.instance().dayPickerContainer = { contains: () => false, }; From 1a55d687284ae9998a7241a303ba44739b7243b9 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Mon, 11 Feb 2019 17:20:24 -0800 Subject: [PATCH 385/618] Version 19.0.3 --- CHANGELOG.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df0d035bd9..946a6e6041 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## 19.0.3 +- [fix] Fix date selection in the SDP ([#1540](https://github.com/airbnb/react-dates/pull/1540)) + +## 19.0.2 +- no changes; extraneous publish + ## 19.0.1 - [fix] Fix single date picker not responding to input ([#1533](https://github.com/airbnb/react-dates/pull/1533)) - [fix] Fixes the focus out event in IE11 ([#1524](https://github.com/airbnb/react-dates/pull/1524)) diff --git a/package.json b/package.json index 6333ba20d3..33624142fd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "19.0.2", + "version": "19.0.3", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From f4e97e62f1433ad05c0cbccf73368f2773a4173e Mon Sep 17 00:00:00 2001 From: Kris Salvador Date: Fri, 8 Feb 2019 15:48:14 -0500 Subject: [PATCH 386/618] Remove last-in-range style modifiers --- src/components/CalendarDay.jsx | 13 +------------ src/components/CustomizableCalendarDay.jsx | 12 ------------ src/components/DayPickerRangeController.jsx | 1 - test/components/DayPickerRangeController_spec.jsx | 12 ------------ 4 files changed, 1 insertion(+), 37 deletions(-) diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index 276bb5ef3d..bf6807e551 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -79,9 +79,7 @@ class CalendarDay extends React.PureComponent { } onKeyDown(day, e) { - const { - onDayClick, - } = this.props; + const { onDayClick } = this.props; const { key } = e; if (key === 'Enter' || key === ' ') { @@ -133,7 +131,6 @@ class CalendarDay extends React.PureComponent { modifiers.has('blocked-calendar') && styles.CalendarDay__blocked_calendar, hoveredSpan && styles.CalendarDay__hovered_span, modifiers.has('selected-span') && styles.CalendarDay__selected_span, - modifiers.has('last-in-range') && styles.CalendarDay__last_in_range, modifiers.has('selected-start') && styles.CalendarDay__selected_start, modifiers.has('selected-end') && styles.CalendarDay__selected_end, selected && styles.CalendarDay__selected, @@ -254,14 +251,6 @@ export default withStyles(({ reactDates: { color, font } }) => ({ }, }, - CalendarDay__last_in_range: { - borderStyle: 'solid', - - ':hover': { - borderStyle: 'solid', - }, - }, - CalendarDay__selected: { background: color.selected.backgroundColor, border: `1px double ${color.selected.borderColor}`, diff --git a/src/components/CustomizableCalendarDay.jsx b/src/components/CustomizableCalendarDay.jsx index fa864a83ff..5145a26502 100644 --- a/src/components/CustomizableCalendarDay.jsx +++ b/src/components/CustomizableCalendarDay.jsx @@ -63,7 +63,6 @@ const propTypes = forbidExtraProps({ blockedOutOfRangeStyles: DayStyleShape, hoveredSpanStyles: DayStyleShape, selectedSpanStyles: DayStyleShape, - lastInRangeStyles: DayStyleShape, selectedStyles: DayStyleShape, selectedStartStyles: DayStyleShape, selectedEndStyles: DayStyleShape, @@ -160,14 +159,6 @@ export const selectedSpanStyles = { }, }; -export const lastInRangeStyles = { - borderStyle: 'solid', - - hover: { - borderStyle: 'solid', - }, -}; - export const selectedStyles = { background: color.selected.backgroundColor, border: `1px double ${color.selected.borderColor}`, @@ -203,7 +194,6 @@ const defaultProps = { blockedOutOfRangeStyles, hoveredSpanStyles, selectedSpanStyles, - lastInRangeStyles, selectedStyles, selectedStartStyles: {}, selectedEndStyles: {}, @@ -290,7 +280,6 @@ class CustomizableCalendarDay extends React.PureComponent { blockedOutOfRangeStyles: blockedOutOfRangeStylesWithHover, hoveredSpanStyles: hoveredSpanStylesWithHover, selectedSpanStyles: selectedSpanStylesWithHover, - lastInRangeStyles: lastInRangeStylesWithHover, selectedStyles: selectedStylesWithHover, selectedStartStyles: selectedStartStylesWithHover, selectedEndStyles: selectedEndStylesWithHover, @@ -327,7 +316,6 @@ class CustomizableCalendarDay extends React.PureComponent { hoveredSpan && getStyles(hoveredSpanStylesWithHover, isHovered), modifiers.has('after-hovered-start') && getStyles(afterHoveredStartStylesWithHover, isHovered), modifiers.has('selected-span') && getStyles(selectedSpanStylesWithHover, isHovered), - modifiers.has('last-in-range') && getStyles(lastInRangeStylesWithHover, isHovered), selected && getStyles(selectedStylesWithHover, isHovered), modifiers.has('selected-start') && getStyles(selectedStartStylesWithHover, isHovered), modifiers.has('selected-end') && getStyles(selectedEndStylesWithHover, isHovered), diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 13b67e2807..e5c271d054 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -198,7 +198,6 @@ export default class DayPickerRangeController extends React.PureComponent { 'selected-end': day => this.isEndDate(day), 'blocked-minimum-nights': day => this.doesNotMeetMinimumNights(day), 'selected-span': day => this.isInSelectedSpan(day), - 'last-in-range': day => this.isLastInRange(day), hovered: day => this.isHovered(day), 'hovered-span': day => this.isInHoveredSpan(day), 'hovered-offset': day => this.isInHoveredSpan(day), diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index 40e7282fef..d89334d341 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -2698,18 +2698,6 @@ describe('DayPickerRangeController', () => { expect(modifiers.has('selected-span')).to.equal(true); }); - it('contains `last-in-range` if this.isLastInRange returns true', () => { - sinon.stub(DayPickerRangeController.prototype, 'isLastInRange').returns(true); - const wrapper = shallow(( - - )); - const modifiers = wrapper.instance().getModifiersForDay(moment()); - expect(modifiers.has('last-in-range')).to.equal(true); - }); - it('contains `hovered` if this.isHovered returns true', () => { sinon.stub(DayPickerRangeController.prototype, 'isHovered').returns(true); const wrapper = shallow( Date: Sun, 10 Feb 2019 16:50:30 -0500 Subject: [PATCH 387/618] Keep last-in-range modifier and related test for DayPickerRangeController --- src/components/DayPickerRangeController.jsx | 1 + test/components/DayPickerRangeController_spec.jsx | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index e5c271d054..13b67e2807 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -198,6 +198,7 @@ export default class DayPickerRangeController extends React.PureComponent { 'selected-end': day => this.isEndDate(day), 'blocked-minimum-nights': day => this.doesNotMeetMinimumNights(day), 'selected-span': day => this.isInSelectedSpan(day), + 'last-in-range': day => this.isLastInRange(day), hovered: day => this.isHovered(day), 'hovered-span': day => this.isInHoveredSpan(day), 'hovered-offset': day => this.isInHoveredSpan(day), diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index d89334d341..40e7282fef 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -2698,6 +2698,18 @@ describe('DayPickerRangeController', () => { expect(modifiers.has('selected-span')).to.equal(true); }); + it('contains `last-in-range` if this.isLastInRange returns true', () => { + sinon.stub(DayPickerRangeController.prototype, 'isLastInRange').returns(true); + const wrapper = shallow(( + + )); + const modifiers = wrapper.instance().getModifiersForDay(moment()); + expect(modifiers.has('last-in-range')).to.equal(true); + }); + it('contains `hovered` if this.isHovered returns true', () => { sinon.stub(DayPickerRangeController.prototype, 'isHovered').returns(true); const wrapper = shallow( Date: Mon, 11 Feb 2019 21:57:20 -0500 Subject: [PATCH 388/618] Keep lastInRangeStyles prop and set default to empty object --- src/components/CustomizableCalendarDay.jsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/CustomizableCalendarDay.jsx b/src/components/CustomizableCalendarDay.jsx index 5145a26502..c22049c163 100644 --- a/src/components/CustomizableCalendarDay.jsx +++ b/src/components/CustomizableCalendarDay.jsx @@ -63,6 +63,7 @@ const propTypes = forbidExtraProps({ blockedOutOfRangeStyles: DayStyleShape, hoveredSpanStyles: DayStyleShape, selectedSpanStyles: DayStyleShape, + lastInRangeStyles: DayStyleShape, selectedStyles: DayStyleShape, selectedStartStyles: DayStyleShape, selectedEndStyles: DayStyleShape, @@ -159,6 +160,8 @@ export const selectedSpanStyles = { }, }; +export const lastInRangeStyles = {}; + export const selectedStyles = { background: color.selected.backgroundColor, border: `1px double ${color.selected.borderColor}`, @@ -194,6 +197,7 @@ const defaultProps = { blockedOutOfRangeStyles, hoveredSpanStyles, selectedSpanStyles, + lastInRangeStyles, selectedStyles, selectedStartStyles: {}, selectedEndStyles: {}, @@ -280,6 +284,7 @@ class CustomizableCalendarDay extends React.PureComponent { blockedOutOfRangeStyles: blockedOutOfRangeStylesWithHover, hoveredSpanStyles: hoveredSpanStylesWithHover, selectedSpanStyles: selectedSpanStylesWithHover, + lastInRangeStyles: lastInRangeStylesWithHover, selectedStyles: selectedStylesWithHover, selectedStartStyles: selectedStartStylesWithHover, selectedEndStyles: selectedEndStylesWithHover, @@ -316,6 +321,7 @@ class CustomizableCalendarDay extends React.PureComponent { hoveredSpan && getStyles(hoveredSpanStylesWithHover, isHovered), modifiers.has('after-hovered-start') && getStyles(afterHoveredStartStylesWithHover, isHovered), modifiers.has('selected-span') && getStyles(selectedSpanStylesWithHover, isHovered), + modifiers.has('last-in-range') && getStyles(lastInRangeStylesWithHover, isHovered), selected && getStyles(selectedStylesWithHover, isHovered), modifiers.has('selected-start') && getStyles(selectedStartStylesWithHover, isHovered), modifiers.has('selected-end') && getStyles(selectedEndStylesWithHover, isHovered), From 6a1b5643e0ac0dc55c711f4ab0aefad1c281dd28 Mon Sep 17 00:00:00 2001 From: GfxKai Date: Tue, 19 Feb 2019 14:59:10 +0000 Subject: [PATCH 389/618] [Fix] `DateRangePickerInput`/`SingleDatePickerInput`: Stop calendar blinking on focus switch (fixes #1523) Using `isStartDateFocused` and `isEndDateFocused` to conditionally render `children` causes a re-render whenever the focus switches from the start date input to end date input, during which the calendar disappears and reappears. This pr adopts same approach as dd9d6b27b300dc8e31d89fcd982d525845e9c698 by skipping the focus check and just rendering the children. --- src/components/DateRangePickerInput.jsx | 15 +++++++++------ src/components/SingleDatePickerInput.jsx | 2 +- src/defaultPhrases.js | 19 ++++++++++++------- test/components/DateRangePickerInput_spec.jsx | 9 +++++++++ 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/components/DateRangePickerInput.jsx b/src/components/DateRangePickerInput.jsx index 9bebea109a..2389595852 100644 --- a/src/components/DateRangePickerInput.jsx +++ b/src/components/DateRangePickerInput.jsx @@ -190,7 +190,11 @@ function DateRangePickerInput({ /> ); - const screenReaderText = screenReaderMessage || phrases.keyboardNavigationInstructions; + const screenReaderStartDateText = screenReaderMessage + || phrases.keyboardForwardNavigationInstructions; + const screenReaderEndDateText = screenReaderMessage + || phrases.keyboardBackwardNavigationInstructions; + const inputIcon = (showDefaultInputIcon || customInputIcon !== null) && (
    diff --git a/src/components/DayPickerKeyboardShortcuts.jsx b/src/components/DayPickerKeyboardShortcuts.jsx index 8f8775db6f..959a5525ac 100644 --- a/src/components/DayPickerKeyboardShortcuts.jsx +++ b/src/components/DayPickerKeyboardShortcuts.jsx @@ -22,6 +22,7 @@ const propTypes = forbidExtraProps({ openKeyboardShortcutsPanel: PropTypes.func, closeKeyboardShortcutsPanel: PropTypes.func, phrases: PropTypes.shape(getPhrasePropTypes(DayPickerKeyboardShortcutsPhrases)), + renderKeyboardShortcutsButton: PropTypes.func, }); const defaultProps = { @@ -31,6 +32,7 @@ const defaultProps = { openKeyboardShortcutsPanel() {}, closeKeyboardShortcutsPanel() {}, phrases: DayPickerKeyboardShortcutsPhrases, + renderKeyboardShortcutsButton: undefined, }; function getKeyboardShortcuts(phrases) { @@ -164,6 +166,7 @@ class DayPickerKeyboardShortcuts extends React.PureComponent { closeKeyboardShortcutsPanel, styles, phrases, + renderKeyboardShortcutsButton, } = this.props; const toggleButtonText = showKeyboardShortcutsPanel @@ -176,33 +179,36 @@ class DayPickerKeyboardShortcuts extends React.PureComponent { return (
    - + + + )} {showKeyboardShortcutsPanel && (
    Date: Thu, 7 Mar 2019 15:57:30 -0800 Subject: [PATCH 409/618] small null check fix --- src/components/DayPickerKeyboardShortcuts.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DayPickerKeyboardShortcuts.jsx b/src/components/DayPickerKeyboardShortcuts.jsx index 959a5525ac..16680ba012 100644 --- a/src/components/DayPickerKeyboardShortcuts.jsx +++ b/src/components/DayPickerKeyboardShortcuts.jsx @@ -179,7 +179,7 @@ class DayPickerKeyboardShortcuts extends React.PureComponent { return (
    - { typeof renderKeyboardShortcutsButton === 'function' ? renderKeyboardShortcutsButton() + { renderKeyboardShortcutsButton ? renderKeyboardShortcutsButton() : (
    {!isTouch && !hideKeyboardShortcutsPanel && ( - + )}
    diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 319d291606..82a8f2bd56 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -84,7 +84,7 @@ const propTypes = forbidExtraProps({ onOutsideClick: PropTypes.func, renderCalendarDay: PropTypes.func, renderDayContents: PropTypes.func, - renderKeyboardShorcutsButton: PropTypes.func, + renderKeyboardShortcutsButton: PropTypes.func, calendarInfoPosition: CalendarInfoPositionShape, firstDayOfWeek: DayOfWeekShape, verticalHeight: nonNegativeInteger, @@ -147,7 +147,6 @@ const defaultProps = { renderCalendarDay: undefined, renderDayContents: null, - renderCalendarInfo: null, renderMonthElement: null, renderKeyboardShortcutsButton: undefined, calendarInfoPosition: INFO_POSITION_BOTTOM, From 22d6a4b5bb7c759ed590d539abab476b0d370b58 Mon Sep 17 00:00:00 2001 From: Kevin Pan Date: Thu, 7 Mar 2019 18:43:58 -0800 Subject: [PATCH 411/618] adding props at render to button --- src/components/DayPickerKeyboardShortcuts.jsx | 11 +++++++++-- test/components/DayPickerKeyboardShortcuts_spec.jsx | 10 ++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/components/DayPickerKeyboardShortcuts.jsx b/src/components/DayPickerKeyboardShortcuts.jsx index 16680ba012..042b1a7700 100644 --- a/src/components/DayPickerKeyboardShortcuts.jsx +++ b/src/components/DayPickerKeyboardShortcuts.jsx @@ -179,8 +179,15 @@ class DayPickerKeyboardShortcuts extends React.PureComponent { return (
    - { renderKeyboardShortcutsButton ? renderKeyboardShortcutsButton() - : ( + { renderKeyboardShortcutsButton + ? React.cloneElement( + renderKeyboardShortcutsButton(), + { + // can't pass context-specific callbacks from passed-in button + ref: this.setShowKeyboardShortcutsButtonRef, + onClick: this.onShowKeyboardShortcutsButtonClick, + }, + ) : ( ) }; + const wrapper = shallow().dive(); + const buttonWrapper = wrapper.children().find('button'); + expect(buttonWrapper.text()).to.equal('Success!'); + expect(buttonWrapper.props()).to.contain.keys(['ref', 'onClick']); + }); + }); }); describe('#DayPickerKeyboardShortcuts_panel', () => { From 92052b5ac40d778715da485872972c8867c37cdf Mon Sep 17 00:00:00 2001 From: Kevin Pan Date: Fri, 8 Mar 2019 10:33:27 -0800 Subject: [PATCH 412/618] replace deleted renderCalendarInfo --- src/components/DayPickerRangeController.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 82a8f2bd56..2bc4b65075 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -84,6 +84,7 @@ const propTypes = forbidExtraProps({ onOutsideClick: PropTypes.func, renderCalendarDay: PropTypes.func, renderDayContents: PropTypes.func, + renderCalendarInfo: PropTypes.func, renderKeyboardShortcutsButton: PropTypes.func, calendarInfoPosition: CalendarInfoPositionShape, firstDayOfWeek: DayOfWeekShape, @@ -147,6 +148,7 @@ const defaultProps = { renderCalendarDay: undefined, renderDayContents: null, + renderCalendarInfo: null, renderMonthElement: null, renderKeyboardShortcutsButton: undefined, calendarInfoPosition: INFO_POSITION_BOTTOM, From 8b62e7b4772a471195080280559503b5f690f894 Mon Sep 17 00:00:00 2001 From: Kevin Pan Date: Fri, 8 Mar 2019 11:11:11 -0800 Subject: [PATCH 413/618] removing need to clone button --- src/components/DayPickerKeyboardShortcuts.jsx | 9 +-------- test/components/DayPickerKeyboardShortcuts_spec.jsx | 1 - 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/components/DayPickerKeyboardShortcuts.jsx b/src/components/DayPickerKeyboardShortcuts.jsx index 042b1a7700..8e63d35107 100644 --- a/src/components/DayPickerKeyboardShortcuts.jsx +++ b/src/components/DayPickerKeyboardShortcuts.jsx @@ -180,14 +180,7 @@ class DayPickerKeyboardShortcuts extends React.PureComponent { return (
    { renderKeyboardShortcutsButton - ? React.cloneElement( - renderKeyboardShortcutsButton(), - { - // can't pass context-specific callbacks from passed-in button - ref: this.setShowKeyboardShortcutsButtonRef, - onClick: this.onShowKeyboardShortcutsButtonClick, - }, - ) : ( + ? renderKeyboardShortcutsButton() : ( + + )} - {showKeyboardShortcutsPanel && (
    { describe('renderKeyboardShortcutsButton', () => { it('renders the provided button', () => { - const props = { renderKeyboardShortcutsButton: () => () }; + function Button() { + return (); + } + const props = { renderKeyboardShortcutsButton: () => (
    ); +function renderKeyboardShortcutsButton(buttonProps) { + const { ref, onClick, ariaLabel } = buttonProps || {}; + + const buttonStyle = { + backgroundColor: '#914669', + border: 0, + borderRadius: 0, + color: 'inherit', + font: 'inherit', + lineHeight: 'normal', + overflow: 'visible', + padding: 0, + cursor: 'pointer', + width: 26, + height: 26, + position: 'absolute', + bottom: 0, + right: 0, + }; + + const spanStyle = { + color: 'white', + position: 'absolute', + bottom: 5, + right: 9, + }; + + return ( + + ); +} + const datesList = [ moment(), moment().add(1, 'days'), @@ -417,4 +460,12 @@ storiesOf('DayPickerRangeController', module) getMinNightsForHoverDate={() => 2} isDayBlocked={day1 => datesList.some(day2 => isSameDay(day1, day2))} /> + ))) + .add('with custom keyboard shortcuts button', withInfo()(() => ( + ))); From ba0b6768d48a1078f0a873e8fbedcc30c003d9d3 Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Wed, 11 Sep 2019 13:20:47 -0700 Subject: [PATCH 416/618] [fix][deps] Update react-with-styles 4.0.0 -> 4.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6db4f3d781..2fc3fe07cc 100644 --- a/package.json +++ b/package.json @@ -127,7 +127,7 @@ "react-outside-click-handler": "^1.2.4", "react-portal": "^4.2.0", "react-with-direction": "^1.3.1", - "react-with-styles": "^4.0.0", + "react-with-styles": "^4.0.1", "react-with-styles-interface-css": "^6.0.0" }, "peerDependencies": { From 26246e7ae1aa0526519049b3f10568a3e680c0be Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Wed, 11 Sep 2019 16:02:52 -0700 Subject: [PATCH 417/618] [dev][ci] Allow failures -- TODO: debug this issue --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 57583f8c53..40af3ca812 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,3 +37,6 @@ matrix: env: COVERAGE=true TEST=false - node_js: "lts/*" env: KARMA=true TEST=false + allow_failures: + - node_js: "10" + env: REACT=16 From 2657568d0997220ef005870c7fb98951423edc8a Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Wed, 11 Sep 2019 17:00:13 -0700 Subject: [PATCH 418/618] Version 21.0.1 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9d88ab127..937e26553e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ ## Unreleased +## 21.0.1 + +- [fix] [deps] Update react-with-styles ^4.0.0 -> ^4.0.1 ([#1781](https://github.com/airbnb/react-dates/pull/1781)) + ## 21.0.0 - [breaking] [deps] Update react-with-styles and other deps ([#1761](https://github.com/airbnb/react-dates/pull/1761) diff --git a/package.json b/package.json index 2fc3fe07cc..d5c606043d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "21.0.0", + "version": "21.0.1", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From c588a9d5ece36c4a0633d7fa8d0910f51223a52b Mon Sep 17 00:00:00 2001 From: WarrickPardoe Date: Mon, 11 Mar 2019 18:52:09 +0000 Subject: [PATCH 419/618] =?UTF-8?q?[Fix]=20`DayPicker`:=20week=20headers:?= =?UTF-8?q?=20use=20the=20passed-in=20moment=20object=E2=80=99s=20instance?= =?UTF-8?q?,=20to=20support=20locale?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #1575. --- src/components/DayPicker.jsx | 30 +++++++++++++++++++----------- test/components/DayPicker_spec.jsx | 14 ++++++++++++-- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index 3f7e6b41fe..24f8a53298 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -582,6 +582,19 @@ class DayPicker extends React.PureComponent { return firstDayOfWeek; } + getWeekHeaders() { + const { weekDayFormat } = this.props; + const { currentMonth } = this.state; + const firstDayOfWeek = this.getFirstDayOfWeek(); + + const weekHeaders = []; + for (let i = 0; i < 7; i += 1) { + weekHeaders.push(currentMonth.day((i + firstDayOfWeek) % 7).format(weekDayFormat)); + } + + return weekHeaders; + } + getFirstVisibleIndex() { const { orientation } = this.props; const { monthTransition } = this.state; @@ -854,7 +867,6 @@ class DayPicker extends React.PureComponent { daySize, horizontalMonthPadding, orientation, - weekDayFormat, styles, css, } = this.props; @@ -874,16 +886,12 @@ class DayPicker extends React.PureComponent { weekHeaderStyle = verticalStyle; } - const firstDayOfWeek = this.getFirstDayOfWeek(); - - const header = []; - for (let i = 0; i < 7; i += 1) { - header.push(( -
  • - {moment().day((i + firstDayOfWeek) % 7).format(weekDayFormat)} -
  • - )); - } + const weekHeaders = this.getWeekHeaders(); + const header = weekHeaders.map((day) => ( +
  • + {day} +
  • + )); return (
    { @@ -805,6 +806,15 @@ describe('DayPicker', () => { }); }); + describe('#weekHeaderNames', () => { + it('returns weekheaders in fr', () => { + const INITIAL_MONTH = moment().locale('fr'); + const wrapper = shallow( INITIAL_MONTH} />).dive(); + const instance = wrapper.instance(); + expect(instance.getWeekHeaders()).to.be.eql(INITIAL_MONTH.localeData().weekdaysMin()); + }); + }); + describe.skip('life cycle methods', () => { let adjustDayPickerHeightSpy; beforeEach(() => { From 116aacf0c8572c1c6c247828b5eb8bf2803b6b6c Mon Sep 17 00:00:00 2001 From: Kevin Pan Date: Mon, 11 Mar 2019 12:49:18 -0700 Subject: [PATCH 420/618] PR fixes: syntax issues, adding specs, misc fixes --- src/components/DayPickerKeyboardShortcuts.jsx | 5 ++--- stories/DayPickerRangeController.js | 15 +++++++-------- .../components/DayPickerRangeController_spec.jsx | 16 ++++++++++++++++ test/components/DayPicker_spec.jsx | 12 ++++++++++++ 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/components/DayPickerKeyboardShortcuts.jsx b/src/components/DayPickerKeyboardShortcuts.jsx index e0ffb5220d..ffb8e83a8f 100644 --- a/src/components/DayPickerKeyboardShortcuts.jsx +++ b/src/components/DayPickerKeyboardShortcuts.jsx @@ -186,8 +186,7 @@ class DayPickerKeyboardShortcuts extends React.PureComponent { onClick: this.onShowKeyboardShortcutsButtonClick, ariaLabel: toggleButtonText, })} - {renderKeyboardShortcutsButton - || ( + {renderKeyboardShortcutsButton || ( - )} + )} {showKeyboardShortcutsPanel && (
    ( ); function renderKeyboardShortcutsButton(buttonProps) { - const { ref, onClick, ariaLabel } = buttonProps || {}; + const { ref, onClick, ariaLabel } = buttonProps; const buttonStyle = { backgroundColor: '#914669', @@ -262,9 +262,9 @@ storiesOf('DayPickerRangeController', module) orientation={VERTICAL_SCROLLABLE} numberOfMonths={3} verticalHeight={300} - navNext={ + navNext={(
    - Show More Months -
    - } +
    +)} />
    ))) @@ -352,9 +352,8 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - isOutsideRange={day => - !isInclusivelyAfterDay(day, moment()) || - isInclusivelyAfterDay(day, moment().add(2, 'weeks')) + isOutsideRange={day => !isInclusivelyAfterDay(day, moment()) + || isInclusivelyAfterDay(day, moment().add(2, 'weeks')) } /> ))) diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index 20efaea9d4..a62ef71fac 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -4426,5 +4426,21 @@ describe('DayPickerRangeController', () => { expect(wrapper.find(DayPickerNavigation)).to.have.lengthOf(0); }); }); + + describe('renderKeyboardShortcutsButton prop', () => { + it('pass down custom button render function', () => { + const testRenderKeyboardShortcutsButton = () => {}; + const wrapper = shallow( + , + ); + const dayPicker = wrapper.find(DayPicker); + expect(dayPicker).to.have.lengthOf(1); + expect(dayPicker.prop('renderKeyboardShortcutsButton')) + .to + .eql(testRenderKeyboardShortcutsButton); + }); + }); }); }); diff --git a/test/components/DayPicker_spec.jsx b/test/components/DayPicker_spec.jsx index 8bb60a48dc..389bc1f0f7 100644 --- a/test/components/DayPicker_spec.jsx +++ b/test/components/DayPicker_spec.jsx @@ -105,6 +105,18 @@ describe('DayPicker', () => { const wrapper = shallow().dive(); expect(wrapper.find(DayPickerKeyboardShortcuts)).to.have.lengthOf(0); }); + + it('component exists with custom button render function if renderKeyboardShortcutsButton is passed down', () => { + const testRenderKeyboardShortcutsButton = () => {}; + const wrapper = shallow( + , + ).dive(); + const dayPickerKeyboardShortcuts = wrapper.find(DayPickerKeyboardShortcuts); + expect(dayPickerKeyboardShortcuts).to.have.lengthOf(1); + expect(dayPickerKeyboardShortcuts.prop('renderKeyboardShortcutsButton')) + .to + .eql(testRenderKeyboardShortcutsButton); + }); }); }); From 98fe1763c87945370ecedc4ff5f6df31ce8f7636 Mon Sep 17 00:00:00 2001 From: Kevin Pan Date: Mon, 11 Mar 2019 14:14:02 -0700 Subject: [PATCH 421/618] linting issues --- stories/DayPickerRangeController.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index 9071abbfbb..8ff8f15c2f 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -264,7 +264,7 @@ storiesOf('DayPickerRangeController', module) verticalHeight={300} navNext={(
    - Show More Months -
    -)} +
    + )} />
    ))) From 32b5f3dc4ccbd5484fe03b0d2b253c11089c700b Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Tue, 12 Mar 2019 10:37:10 -0700 Subject: [PATCH 422/618] Version 20.1.0 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 243ef2a681..76d2921dbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## 20.1.0 +- [new] Add `renderKeyboardShortcutsButton` prop ([#1576](https://github.com/airbnb/react-dates/pull/1576)) + ## 20.0.0 - [breaking] Omit tabindex prop from calendar navigation buttons when custom buttons are supplied ([#1563](https://github.com/airbnb/react-dates/pull/1563)) - [new] Add hovered-start-blocked-minimun-nights and hovered-start-first-possible-end modifiers ([#1547](https://github.com/airbnb/react-dates/pull/1547)) diff --git a/package.json b/package.json index 356a6b20c2..3b3a9c4d2e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "20.0.0", + "version": "20.1.0", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From a9fdd4b92f52a31c7a2576533291472e475e50a8 Mon Sep 17 00:00:00 2001 From: Samantha Date: Wed, 13 Mar 2019 11:51:37 +0100 Subject: [PATCH 423/618] issue #1530 - Fix date selection in the SDP --- CHANGELOG.md | 4 ++++ src/components/SingleDatePicker.jsx | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76d2921dbd..cd629140eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## [Unreleased] +- [fix] Fix date selection in the SDP ([#1530]) + + ## 20.1.0 - [new] Add `renderKeyboardShortcutsButton` prop ([#1576](https://github.com/airbnb/react-dates/pull/1576)) diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index a5732d6ff1..931f498ab2 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -249,7 +249,7 @@ class SingleDatePicker extends React.PureComponent { onFocusOut(e) { const { onFocusChange } = this.props; - if (this.dayPickerContainer.contains(e.relatedTarget || e.target)) return; + if (this.dayPickerContainer.contains(e.relatedTarget || e.target) || this.dayPickerContainer.contains(e.target)) return; onFocusChange({ focused: false }); } From a969e8e977b02b5b721f4ec47cc51fc72a5e09cd Mon Sep 17 00:00:00 2001 From: Ryan Lewis Date: Thu, 21 Mar 2019 10:53:51 -0600 Subject: [PATCH 424/618] explicitly set aria-label --- README.md | 3 +++ src/components/DateInput.jsx | 7 +++++-- src/components/DateRangePicker.jsx | 6 ++++++ src/components/DateRangePickerInput.jsx | 8 ++++++++ src/components/DateRangePickerInputController.jsx | 8 ++++++++ src/components/SingleDatePicker.jsx | 3 +++ src/components/SingleDatePickerInput.jsx | 8 ++++++-- src/components/SingleDatePickerInputController.jsx | 6 +++++- src/shapes/DateRangePickerShape.js | 2 ++ src/shapes/SingleDatePickerShape.js | 1 + test/components/DateInput_spec.jsx | 8 ++++---- 11 files changed, 51 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3b40fb2deb..6ca688041e 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,8 @@ The following is a list of other *OPTIONAL* props you may provide to the `DateRa // input related props startDatePlaceholderText: PropTypes.string, endDatePlaceholderText: PropTypes.string, +startDateAriaLabel: PropTypes.string, +endDateAriaLabel: PropTypes.string, disabled: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf([START_DATE, END_DATE])]), required: PropTypes.bool, readOnly: PropTypes.bool, @@ -221,6 +223,7 @@ The following is a list of other *OPTIONAL* props you may provide to the `Single ```js // input related props placeholder: PropTypes.string, +ariaLabel: PropTypes.string, disabled: PropTypes.bool, required: PropTypes.bool, readOnly: PropTypes.bool, diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index e5f3cee4c0..1bfea2f474 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -26,8 +26,9 @@ const FANG_STROKE_BOTTOM = `M0,0 ${FANG_WIDTH_PX / 2},${FANG_HEIGHT_PX} ${FANG_W const propTypes = forbidExtraProps({ ...withStylesPropTypes, id: PropTypes.string.isRequired, - placeholder: PropTypes.string, // also used as label + placeholder: PropTypes.string, displayValue: PropTypes.string, + ariaLabel: PropTypes.string, screenReaderMessage: PropTypes.string, focused: PropTypes.bool, disabled: PropTypes.bool, @@ -55,6 +56,7 @@ const propTypes = forbidExtraProps({ const defaultProps = { placeholder: 'Select Date', displayValue: '', + ariaLabel: null, screenReaderMessage: '', focused: false, disabled: false, @@ -172,6 +174,7 @@ class DateInput extends React.PureComponent { const { id, placeholder, + ariaLabel, displayValue, screenReaderMessage, focused, @@ -217,7 +220,7 @@ class DateInput extends React.PureComponent { focused && styles.DateInput_input__focused, disabled && styles.DateInput_input__disabled, )} - aria-label={placeholder} + aria-label={ariaLabel} type="text" id={id} name={id} diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index db58b1a366..adc9ceef3f 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -51,6 +51,8 @@ const defaultProps = { // input related props startDatePlaceholderText: 'Start Date', endDatePlaceholderText: 'End Date', + startDateAriaLabel: null, + endDateAriaLabel: null, startDateOffset: undefined, endDateOffset: undefined, disabled: false, @@ -540,9 +542,11 @@ class DateRangePicker extends React.PureComponent { startDate, startDateId, startDatePlaceholderText, + startDateAriaLabel, endDate, endDateId, endDatePlaceholderText, + endDateAriaLabel, focusedInput, screenReaderInputMessage, showClearDates, @@ -586,10 +590,12 @@ class DateRangePicker extends React.PureComponent { startDateId={startDateId} startDatePlaceholderText={startDatePlaceholderText} isStartDateFocused={focusedInput === START_DATE} + startDateAriaLabel={startDateAriaLabel} endDate={endDate} endDateId={endDateId} endDatePlaceholderText={endDatePlaceholderText} isEndDateFocused={focusedInput === END_DATE} + endDateAriaLabel={endDateAriaLabel} displayFormat={displayFormat} showClearDates={showClearDates} showCaret={!withPortal && !withFullScreenPortal && !hideFang} diff --git a/src/components/DateRangePickerInput.jsx b/src/components/DateRangePickerInput.jsx index a1a54ec983..1cd0f34b5c 100644 --- a/src/components/DateRangePickerInput.jsx +++ b/src/components/DateRangePickerInput.jsx @@ -32,10 +32,12 @@ const propTypes = forbidExtraProps({ startDateId: PropTypes.string, startDatePlaceholderText: PropTypes.string, + startDateAriaLabel: PropTypes.string, screenReaderMessage: PropTypes.string, endDateId: PropTypes.string, endDatePlaceholderText: PropTypes.string, + endDateAriaLabel: PropTypes.string, onStartDateFocus: PropTypes.func, onEndDateFocus: PropTypes.func, @@ -84,6 +86,8 @@ const defaultProps = { endDateId: END_DATE, startDatePlaceholderText: 'Start Date', endDatePlaceholderText: 'End Date', + startDateAriaLabel: null, + endDateAriaLabel: null, screenReaderMessage: '', onStartDateFocus() {}, onEndDateFocus() {}, @@ -136,6 +140,7 @@ function DateRangePickerInput({ onStartDateChange, onStartDateFocus, onStartDateShiftTab, + startDateAriaLabel, endDate, endDateId, endDatePlaceholderText, @@ -143,6 +148,7 @@ function DateRangePickerInput({ onEndDateChange, onEndDateFocus, onEndDateTab, + endDateAriaLabel, onKeyDownArrowDown, onKeyDownQuestionMark, onClearDates, @@ -216,6 +222,7 @@ function DateRangePickerInput({ { describe('#render', () => { describe('input', () => { - it('has props.placeholder as an aria-label if prop is passed in', () => { - const placeholder = 'placeholder_foo'; - const wrapper = shallow().dive(); - expect(wrapper.find('input').props()['aria-label']).to.equal(placeholder); + it('has props.ariaLabel as an aria-label if prop is passed in', () => { + const ariaLabel = 'ariaLabelExample'; + const wrapper = shallow().dive(); + expect(wrapper.find('input').props()['aria-label']).to.equal(ariaLabel); }); it('has value === props.displayValue', () => { From a5e0f299c23ea46e9e3c56643cf044943ab48401 Mon Sep 17 00:00:00 2001 From: Ryan Lewis Date: Thu, 21 Mar 2019 14:52:26 -0600 Subject: [PATCH 425/618] undoing breaking change and adding test --- src/components/DateInput.jsx | 4 ++-- src/components/DateRangePicker.jsx | 4 ++-- src/components/DateRangePickerInput.jsx | 4 ++-- src/components/DateRangePickerInputController.jsx | 4 ++-- src/components/SingleDatePicker.jsx | 2 +- src/components/SingleDatePickerInput.jsx | 2 +- src/components/SingleDatePickerInputController.jsx | 2 +- test/components/DateInput_spec.jsx | 11 +++++++++++ 8 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index 1bfea2f474..d50ecd9411 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -56,7 +56,7 @@ const propTypes = forbidExtraProps({ const defaultProps = { placeholder: 'Select Date', displayValue: '', - ariaLabel: null, + ariaLabel: undefined, screenReaderMessage: '', focused: false, disabled: false, @@ -220,7 +220,7 @@ class DateInput extends React.PureComponent { focused && styles.DateInput_input__focused, disabled && styles.DateInput_input__disabled, )} - aria-label={ariaLabel} + aria-label={ariaLabel === undefined ? placeholder : ariaLabel} type="text" id={id} name={id} diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index adc9ceef3f..fa9868cc5a 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -51,8 +51,8 @@ const defaultProps = { // input related props startDatePlaceholderText: 'Start Date', endDatePlaceholderText: 'End Date', - startDateAriaLabel: null, - endDateAriaLabel: null, + startDateAriaLabel: undefined, + endDateAriaLabel: undefined, startDateOffset: undefined, endDateOffset: undefined, disabled: false, diff --git a/src/components/DateRangePickerInput.jsx b/src/components/DateRangePickerInput.jsx index 1cd0f34b5c..9bebea109a 100644 --- a/src/components/DateRangePickerInput.jsx +++ b/src/components/DateRangePickerInput.jsx @@ -86,8 +86,8 @@ const defaultProps = { endDateId: END_DATE, startDatePlaceholderText: 'Start Date', endDatePlaceholderText: 'End Date', - startDateAriaLabel: null, - endDateAriaLabel: null, + startDateAriaLabel: undefined, + endDateAriaLabel: undefined, screenReaderMessage: '', onStartDateFocus() {}, onEndDateFocus() {}, diff --git a/src/components/DateRangePickerInputController.jsx b/src/components/DateRangePickerInputController.jsx index b3218180cf..a504e3cc74 100644 --- a/src/components/DateRangePickerInputController.jsx +++ b/src/components/DateRangePickerInputController.jsx @@ -90,13 +90,13 @@ const defaultProps = { startDateId: START_DATE, startDatePlaceholderText: 'Start Date', isStartDateFocused: false, - startDateAriaLabel: null, + startDateAriaLabel: undefined, endDate: null, endDateId: END_DATE, endDatePlaceholderText: 'End Date', isEndDateFocused: false, - endDateAriaLabel: null, + endDateAriaLabel: undefined, screenReaderMessage: '', showClearDates: false, diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index ac9b6c6190..a5732d6ff1 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -48,7 +48,7 @@ const defaultProps = { // input related props id: 'date', placeholder: 'Date', - ariaLabel: null, + ariaLabel: undefined, disabled: false, required: false, readOnly: false, diff --git a/src/components/SingleDatePickerInput.jsx b/src/components/SingleDatePickerInput.jsx index 60df68040f..3ca8dc46e5 100644 --- a/src/components/SingleDatePickerInput.jsx +++ b/src/components/SingleDatePickerInput.jsx @@ -58,7 +58,7 @@ const propTypes = forbidExtraProps({ const defaultProps = { children: null, placeholder: 'Select Date', - ariaLabel: null, + ariaLabel: undefined, displayValue: '', screenReaderMessage: '', focused: false, diff --git a/src/components/SingleDatePickerInputController.jsx b/src/components/SingleDatePickerInputController.jsx index d2e7a9a3ee..5777068f1a 100644 --- a/src/components/SingleDatePickerInputController.jsx +++ b/src/components/SingleDatePickerInputController.jsx @@ -79,7 +79,7 @@ const defaultProps = { focused: false, placeholder: '', - ariaLabel: null, + ariaLabel: undefined, screenReaderMessage: 'Date', showClearDate: false, showCaret: false, diff --git a/test/components/DateInput_spec.jsx b/test/components/DateInput_spec.jsx index 21dec9d83d..1f7f16906c 100644 --- a/test/components/DateInput_spec.jsx +++ b/test/components/DateInput_spec.jsx @@ -16,6 +16,17 @@ describe('DateInput', () => { expect(wrapper.find('input').props()['aria-label']).to.equal(ariaLabel); }); + it('has no aria-label if props.ariaLabel is null', () => { + const wrapper = shallow().dive(); + expect(wrapper.find('input').props()['aria-label']).to.equal(null); + }); + + it('has props.ariaLabel as an aria-label if prop is passed in', () => { + const placeholder = 'placeholder foo'; + const wrapper = shallow().dive(); + expect(wrapper.find('input').props()['aria-label']).to.equal(placeholder); + }); + it('has value === props.displayValue', () => { const DISPLAY_VALUE = 'foobar'; const wrapper = shallow().dive(); From f34f22768afd172e55cd0fc3742af096f5929b96 Mon Sep 17 00:00:00 2001 From: Ryan Lewis Date: Thu, 21 Mar 2019 15:24:02 -0600 Subject: [PATCH 426/618] fixing test description --- test/components/DateInput_spec.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/components/DateInput_spec.jsx b/test/components/DateInput_spec.jsx index 1f7f16906c..2e7886144b 100644 --- a/test/components/DateInput_spec.jsx +++ b/test/components/DateInput_spec.jsx @@ -10,7 +10,7 @@ const event = { preventDefault() {}, stopPropagation() {} }; describe('DateInput', () => { describe('#render', () => { describe('input', () => { - it('has props.ariaLabel as an aria-label if prop is passed in', () => { + it('has props.ariaLabel as an aria-label if ariaLabel is passed in', () => { const ariaLabel = 'ariaLabelExample'; const wrapper = shallow().dive(); expect(wrapper.find('input').props()['aria-label']).to.equal(ariaLabel); @@ -21,7 +21,7 @@ describe('DateInput', () => { expect(wrapper.find('input').props()['aria-label']).to.equal(null); }); - it('has props.ariaLabel as an aria-label if prop is passed in', () => { + it('has props.placeholder as an aria-label if ariaLabel is not passed in', () => { const placeholder = 'placeholder foo'; const wrapper = shallow().dive(); expect(wrapper.find('input').props()['aria-label']).to.equal(placeholder); From 0ae047835088a8744c39018d361b6f25fdebfef7 Mon Sep 17 00:00:00 2001 From: Kris Salvador Date: Sun, 24 Mar 2019 23:54:29 -0400 Subject: [PATCH 427/618] Add after hovered start class --- src/components/CalendarDay.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index 7256cd0710..982f18bcbd 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -132,6 +132,7 @@ class CalendarDay extends React.PureComponent { modifiers.has('blocked-minimum-nights') && styles.CalendarDay__blocked_minimum_nights, modifiers.has('blocked-calendar') && styles.CalendarDay__blocked_calendar, hoveredSpan && styles.CalendarDay__hovered_span, + modifiers.has('after-hovered-start') && styles.CalendarDay__after_hovered_start, modifiers.has('selected-span') && styles.CalendarDay__selected_span, modifiers.has('selected-start') && styles.CalendarDay__selected_start, modifiers.has('selected-end') && styles.CalendarDay__selected_end, @@ -340,4 +341,5 @@ export default withStyles(({ reactDates: { color, font } }) => ({ CalendarDay__today: {}, CalendarDay__firstDayOfWeek: {}, CalendarDay__lastDayOfWeek: {}, + CalendarDay__after_hovered_start: {} }), { pureComponent: typeof React.PureComponent !== 'undefined' })(CalendarDay); From 6f99bb487fd962be7257d505c07f538cea8314b4 Mon Sep 17 00:00:00 2001 From: Kris Salvador Date: Mon, 25 Mar 2019 17:01:12 -0400 Subject: [PATCH 428/618] Add no-selected-start-before-selected-end and before-hovered-end --- src/components/CalendarDay.jsx | 6 +- src/components/DayPickerRangeController.jsx | 61 +++++++++++++++++++++ src/utils/getCalendarDaySettings.js | 2 +- src/utils/isPreviousDay.js | 9 +++ test/utils/isPreviousDay_spec.js | 27 +++++++++ 5 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 src/utils/isPreviousDay.js create mode 100644 test/utils/isPreviousDay_spec.js diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index 982f18bcbd..8f58a05236 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -137,6 +137,8 @@ class CalendarDay extends React.PureComponent { modifiers.has('selected-start') && styles.CalendarDay__selected_start, modifiers.has('selected-end') && styles.CalendarDay__selected_end, selected && !modifiers.has('selected-span') && styles.CalendarDay__selected, + modifiers.has('before-hovered-end') && styles.CalendarDay__before_hovered_end, + modifiers.has('no-selected-start-before-selected-end') && styles.CalendarDay__no_selected_start_before_selected_end, isOutsideRange && styles.CalendarDay__blocked_out_of_range, daySizeStyles, )} @@ -341,5 +343,7 @@ export default withStyles(({ reactDates: { color, font } }) => ({ CalendarDay__today: {}, CalendarDay__firstDayOfWeek: {}, CalendarDay__lastDayOfWeek: {}, - CalendarDay__after_hovered_start: {} + CalendarDay__after_hovered_start: {}, + CalendarDay__before_hovered_end: {}, + CalendarDay__no_selected_start_before_selected_end: {} }), { pureComponent: typeof React.PureComponent !== 'undefined' })(CalendarDay); diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 2bc4b65075..d2535cedbd 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -14,6 +14,7 @@ import isNextDay from '../utils/isNextDay'; import isSameDay from '../utils/isSameDay'; import isAfterDay from '../utils/isAfterDay'; import isBeforeDay from '../utils/isBeforeDay'; +import isPreviousDay from '../utils/isPreviousDay'; import getVisibleDays from '../utils/getVisibleDays'; import isDayVisible from '../utils/isDayVisible'; @@ -211,6 +212,8 @@ export default class DayPickerRangeController extends React.PureComponent { 'last-day-of-week': day => this.isLastDayOfWeek(day), 'hovered-start-first-possible-end': (day, hoverDate) => this.isFirstPossibleEndDateForHoveredStartDate(day, hoverDate), 'hovered-start-blocked-minimum-nights': (day, hoverDate) => this.doesNotMeetMinNightsForHoveredStartDate(day, hoverDate), + 'before-hovered-end': day => this.isDayBeforeHoveredEndDate(day), + 'no-selected-start-before-selected-end': (day) => { return this.beforeSelectedEnd(day) && !props.startDate } }; const { currentMonth, visibleDays } = this.getStateForNewMonth(props); @@ -330,6 +333,18 @@ export default class DayPickerRangeController extends React.PureComponent { const startSpan = prevStartDate.clone().add(1, 'day'); const endSpan = prevStartDate.clone().add(prevMinimumNights + 1, 'days'); modifiers = this.deleteModifierFromRange(modifiers, startSpan, endSpan, 'after-hovered-start'); + + if (!startDate && endDate) { + values(visibleDays).forEach((days) => { + Object.keys(days).forEach((day) => { + const momentObj = moment(day); + + if (isBeforeDay(momentObj, endDate)) { + modifiers = this.addModifier(modifiers, momentObj, 'no-selected-start-before-selected-end') + } + }); + }); + } } } @@ -371,6 +386,12 @@ export default class DayPickerRangeController extends React.PureComponent { modifiers = this.addModifierToRange(modifiers, startSpan, endSpan, 'after-hovered-start'); } + if (!this.isTouchDevice && didEndDateChange && !startDate && endDate) { + const startSpan = endDate.clone().subtract(minimumNights, 'days'); + const endSpan = endDate.clone().add(1, 'day'); + modifiers = this.addModifierToRange(modifiers, startSpan, endSpan, 'before-hovered-end'); + } + if (prevMinimumNights > 0) { if (didFocusChange || didStartDateChange || minimumNights !== prevMinimumNights) { const startSpan = prevStartDate || this.today; @@ -675,6 +696,23 @@ export default class DayPickerRangeController extends React.PureComponent { } } + if (endDate) { + const startSpan = endDate.clone().subtract(minimumNights, 'days'); + const endSpan = endDate.clone().add(1, 'day'); + modifiers = this.deleteModifierFromRange(modifiers, startSpan, endSpan, 'before-hovered-end'); + + if (isSameDay(day, endDate)) { + const newStartSpan = endDate.clone().subtract(minimumNights, 'days'); + const newEndSpan = endDate.clone().add(1, 'day'); + modifiers = this.addModifierToRange( + modifiers, + newStartSpan, + newEndSpan, + 'before-hovered-end', + ); + } + } + if (hoverDate && !this.isBlocked(hoverDate)) { const minNightsForPrevHoverDate = getMinNightsForHoverDate(hoverDate); if (minNightsForPrevHoverDate > 0 && focusedInput === START_DATE) { @@ -756,6 +794,12 @@ export default class DayPickerRangeController extends React.PureComponent { modifiers = this.deleteModifierFromRange(modifiers, startSpan, endSpan, 'after-hovered-start'); } + if (endDate && isSameDay(day, endDate)) { + const startSpan = endDate.clone().subtract(minimumNights, 'days'); + const endSpan = endDate.clone().add(1, 'day'); + modifiers = this.deleteModifierFromRange(modifiers, startSpan, endSpan, 'before-hovered-end'); + } + if (!this.isBlocked(hoverDate)) { const minNightsForHoverDate = getMinNightsForHoverDate(hoverDate); if (minNightsForHoverDate > 0 && focusedInput === START_DATE) { @@ -1223,6 +1267,23 @@ export default class DayPickerRangeController extends React.PureComponent { return isSameDay(day, firstAvailableEndDate); } + beforeSelectedEnd(day) { + const { endDate } = this.props; + return isBeforeDay(day, endDate); + } + + isDayBeforeHoveredEndDate(day) { + const { startDate, endDate, minimumNights } = this.props; + const { hoverDate } = this.state || {}; + + return !!endDate + && !startDate + && !this.isBlocked(day) + && isPreviousDay(hoverDate, day) + && minimumNights > 0 + && isSameDay(hoverDate, endDate); + } + render() { const { numberOfMonths, diff --git a/src/utils/getCalendarDaySettings.js b/src/utils/getCalendarDaySettings.js index 33c530ec04..5ef662c294 100644 --- a/src/utils/getCalendarDaySettings.js +++ b/src/utils/getCalendarDaySettings.js @@ -16,7 +16,7 @@ function shouldUseDefaultCursor(modifiers) { function isHoveredSpan(modifiers) { if (isSelected(modifiers)) return false; - return modifiers.has('hovered-span') || modifiers.has('after-hovered-start'); + return modifiers.has('hovered-span') || modifiers.has('after-hovered-start') || modifiers.has('before-hovered-end'); } function getAriaLabel(phrases, modifiers, day, ariaLabelFormat) { diff --git a/src/utils/isPreviousDay.js b/src/utils/isPreviousDay.js new file mode 100644 index 0000000000..f7355c6a29 --- /dev/null +++ b/src/utils/isPreviousDay.js @@ -0,0 +1,9 @@ +import moment from 'moment'; + +import isSameDay from './isSameDay'; + +export default function isPreviousDay(a, b) { + if (!moment.isMoment(a) || !moment.isMoment(b)) return false; + const dayBefore = moment(a).subtract(1, 'day'); + return isSameDay(dayBefore, b); +} diff --git a/test/utils/isPreviousDay_spec.js b/test/utils/isPreviousDay_spec.js new file mode 100644 index 0000000000..0c1b5d4947 --- /dev/null +++ b/test/utils/isPreviousDay_spec.js @@ -0,0 +1,27 @@ +import moment from 'moment'; +import { expect } from 'chai'; + +import isPreviousDay from '../../src/utils/isPreviousDay'; + +const today = moment(); +const yesterday = moment().subtract(1, 'days'); + +describe('isPreviousDay', () => { + it('returns true if second argument is the day immediately before the first', () => { + expect(isPreviousDay(today, yesterday)).to.equal(true); + }); + + it('returns false if the second arg is not the day immediately before the first', () => { + expect(isPreviousDay(yesterday, today)).to.equal(false); + }); + + describe('non-moment arguments', () => { + it('is false if first argument is not a moment object', () => { + expect(isPreviousDay(null, today)).to.equal(false); + }); + + it('is false if second argument is not a moment object', () => { + expect(isPreviousDay(today, 'foo')).to.equal(false); + }); + }); +}); From 98fd20b5cfad1ba18c1a07926ffae5d89c70105d Mon Sep 17 00:00:00 2001 From: Kris Salvador Date: Sun, 31 Mar 2019 22:35:50 -0400 Subject: [PATCH 429/618] Add selected-start-no-selected-end and selected-end-no-selected-start --- src/components/CalendarDay.jsx | 10 ++- src/components/DayPickerRangeController.jsx | 87 +++++++++++++++------ 2 files changed, 73 insertions(+), 24 deletions(-) diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index 8f58a05236..deb49d71db 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -139,6 +139,10 @@ class CalendarDay extends React.PureComponent { selected && !modifiers.has('selected-span') && styles.CalendarDay__selected, modifiers.has('before-hovered-end') && styles.CalendarDay__before_hovered_end, modifiers.has('no-selected-start-before-selected-end') && styles.CalendarDay__no_selected_start_before_selected_end, + modifiers.has('selected-start-in-hovered-span') && styles.CalendarDay__selected_start_in_hovered_span, + modifiers.has('selected-end-in-hovered-span') && styles.CalendarDay__selected_end_in_hovered_span, + modifiers.has('selected-start-no-selected-end') && styles.CalendarDay__selected_start_no_selected_end, + modifiers.has('selected-end-no-selected-start') && styles.CalendarDay__selected_end_no_selected_start, isOutsideRange && styles.CalendarDay__blocked_out_of_range, daySizeStyles, )} @@ -345,5 +349,9 @@ export default withStyles(({ reactDates: { color, font } }) => ({ CalendarDay__lastDayOfWeek: {}, CalendarDay__after_hovered_start: {}, CalendarDay__before_hovered_end: {}, - CalendarDay__no_selected_start_before_selected_end: {} + CalendarDay__no_selected_start_before_selected_end: {}, + CalendarDay__selected_start_in_hovered_span: {}, + CalendarDay__selected_end_in_hovered_span: {}, + CalendarDay__selected_start_no_selected_end: {}, + CalendarDay__selected_end_no_selected_start: {} }), { pureComponent: typeof React.PureComponent !== 'undefined' })(CalendarDay); diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index d2535cedbd..c265c7e836 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -113,21 +113,21 @@ const defaultProps = { endDate: undefined, // TODO: use null minDate: null, maxDate: null, - onDatesChange() {}, + onDatesChange() { }, startDateOffset: undefined, endDateOffset: undefined, focusedInput: null, - onFocusChange() {}, - onClose() {}, + onFocusChange() { }, + onClose() { }, keepOpenOnDateSelect: false, minimumNights: 1, disabled: false, - isOutsideRange() {}, - isDayBlocked() {}, - isDayHighlighted() {}, - getMinNightsForHoverDate() {}, + isOutsideRange() { }, + isDayBlocked() { }, + isDayHighlighted() { }, + getMinNightsForHoverDate() { }, // DayPicker props renderMonthText: null, @@ -143,9 +143,9 @@ const defaultProps = { navNext: null, noNavButtons: false, - onPrevMonthClick() {}, - onNextMonthClick() {}, - onOutsideClick() {}, + onPrevMonthClick() { }, + onNextMonthClick() { }, + onOutsideClick() { }, renderCalendarDay: undefined, renderDayContents: null, @@ -161,11 +161,11 @@ const defaultProps = { horizontalMonthPadding: 13, // accessibility - onBlur() {}, + onBlur() { }, isFocused: false, showKeyboardShortcuts: false, - onTab() {}, - onShiftTab() {}, + onTab() { }, + onShiftTab() { }, // i18n monthFormat: 'MMMM YYYY', @@ -213,7 +213,10 @@ export default class DayPickerRangeController extends React.PureComponent { 'hovered-start-first-possible-end': (day, hoverDate) => this.isFirstPossibleEndDateForHoveredStartDate(day, hoverDate), 'hovered-start-blocked-minimum-nights': (day, hoverDate) => this.doesNotMeetMinNightsForHoveredStartDate(day, hoverDate), 'before-hovered-end': day => this.isDayBeforeHoveredEndDate(day), - 'no-selected-start-before-selected-end': (day) => { return this.beforeSelectedEnd(day) && !props.startDate } + 'no-selected-start-before-selected-end': (day) => this.beforeSelectedEnd(day) && !props.startDate, + 'selected-start-in-hovered-span': (day, hoverDate) => this.isStartDate(day) && isAfterDay(hoverDate, day), + 'selected-start-no-selected-end': (day) => this.isStartDate(day) && !props.endDate, + 'selected-end-no-selected-start': (day) => this.isEndDate(day) && !props.startDate }; const { currentMonth, visibleDays } = this.getStateForNewMonth(props); @@ -334,6 +337,10 @@ export default class DayPickerRangeController extends React.PureComponent { const endSpan = prevStartDate.clone().add(prevMinimumNights + 1, 'days'); modifiers = this.deleteModifierFromRange(modifiers, startSpan, endSpan, 'after-hovered-start'); + if (!endDate || !prevEndDate) { + modifiers = this.deleteModifier(modifiers, prevStartDate, 'selected-start-no-selected-end'); + } + if (!startDate && endDate) { values(visibleDays).forEach((days) => { Object.keys(days).forEach((day) => { @@ -351,6 +358,10 @@ export default class DayPickerRangeController extends React.PureComponent { if (didEndDateChange) { modifiers = this.deleteModifier(modifiers, prevEndDate, 'selected-end'); modifiers = this.addModifier(modifiers, endDate, 'selected-end'); + + if (prevEndDate && (!startDate || !prevStartDate)) { + modifiers = this.deleteModifier(modifiers, prevEndDate, 'selected-end-no-start-date'); + } } if (didStartDateChange || didEndDateChange) { @@ -378,6 +389,14 @@ export default class DayPickerRangeController extends React.PureComponent { 'selected-span', ); } + + if (startDate && !endDate) { + modifiers = this.addModifier(modifiers, startDate, 'selected-start-no-selected-end'); + } + + if (endDate && !startDate) { + modifiers = this.addModifier(modifiers, endDate, 'selected-end-no-selected-start'); + } } if (!this.isTouchDevice && didStartDateChange && startDate && !endDate) { @@ -388,7 +407,7 @@ export default class DayPickerRangeController extends React.PureComponent { if (!this.isTouchDevice && didEndDateChange && !startDate && endDate) { const startSpan = endDate.clone().subtract(minimumNights, 'days'); - const endSpan = endDate.clone().add(1, 'day'); + const endSpan = endDate.clone(); modifiers = this.addModifierToRange(modifiers, startSpan, endSpan, 'before-hovered-end'); } @@ -663,9 +682,14 @@ export default class DayPickerRangeController extends React.PureComponent { modifiers = this.deleteModifierFromRange(modifiers, startDate, endSpan, 'hovered-span'); } + if (isBeforeDay(day, startDate) || isSameDay(day, startDate)) { + modifiers = this.deleteModifier(modifiers, startDate, 'selected-start-in-hovered-span'); + } + if (!this.isBlocked(day) && isAfterDay(day, startDate)) { const endSpan = day.clone().add(1, 'day'); modifiers = this.addModifierToRange(modifiers, startDate, endSpan, 'hovered-span'); + modifiers = this.addModifier(modifiers, startDate, 'selected-start-in-hovered-span'); } } @@ -674,8 +698,13 @@ export default class DayPickerRangeController extends React.PureComponent { modifiers = this.deleteModifierFromRange(modifiers, hoverDate, endDate, 'hovered-span'); } + if (isAfterDay(day, endDate) || isSameDay(day, endDate)) { + modifiers = this.deleteModifier(modifiers, endDate, 'selected-end-in-hovered-span'); + } + if (!this.isBlocked(day) && isBeforeDay(day, endDate)) { modifiers = this.addModifierToRange(modifiers, day, endDate, 'hovered-span'); + modifiers = this.addModifier(modifiers, endDate, 'selected-end-in-hovered-span'); } } @@ -698,12 +727,12 @@ export default class DayPickerRangeController extends React.PureComponent { if (endDate) { const startSpan = endDate.clone().subtract(minimumNights, 'days'); - const endSpan = endDate.clone().add(1, 'day'); + const endSpan = endDate.clone(); modifiers = this.deleteModifierFromRange(modifiers, startSpan, endSpan, 'before-hovered-end'); if (isSameDay(day, endDate)) { const newStartSpan = endDate.clone().subtract(minimumNights, 'days'); - const newEndSpan = endDate.clone().add(1, 'day'); + const newEndSpan = endDate.clone(); modifiers = this.addModifierToRange( modifiers, newStartSpan, @@ -779,13 +808,25 @@ export default class DayPickerRangeController extends React.PureComponent { modifiers = this.deleteModifierFromRange(modifiers, dateOffset.start, dateOffset.end, 'hovered-offset'); } - if (startDate && !endDate && isAfterDay(hoverDate, startDate)) { - const endSpan = hoverDate.clone().add(1, 'day'); - modifiers = this.deleteModifierFromRange(modifiers, startDate, endSpan, 'hovered-span'); + if (startDate && !endDate) { + if (isAfterDay(hoverDate, startDate)) { + const endSpan = hoverDate.clone().add(1, 'day'); + modifiers = this.deleteModifierFromRange(modifiers, startDate, endSpan, 'hovered-span'); + } + + if (isAfterDay(day, startDate)) { + modifiers = this.deleteModifier(modifiers, startDate, 'selected-start-in-hovered-span'); + } } - if (!startDate && endDate && isAfterDay(endDate, hoverDate)) { - modifiers = this.deleteModifierFromRange(modifiers, hoverDate, endDate, 'hovered-span'); + if (!startDate && endDate) { + if (isAfterDay(endDate, hoverDate)) { + modifiers = this.deleteModifierFromRange(modifiers, hoverDate, endDate, 'hovered-span'); + } + + if (isBeforeDay(day, endDate)) { + modifiers = this.deleteModifier(modifiers, endDate, 'selected-end-in-hovered-span'); + } } if (startDate && isSameDay(day, startDate)) { @@ -796,7 +837,7 @@ export default class DayPickerRangeController extends React.PureComponent { if (endDate && isSameDay(day, endDate)) { const startSpan = endDate.clone().subtract(minimumNights, 'days'); - const endSpan = endDate.clone().add(1, 'day'); + const endSpan = endDate.clone(); modifiers = this.deleteModifierFromRange(modifiers, startSpan, endSpan, 'before-hovered-end'); } From f58954b50433b5fbef62947964fd46941bc45f1c Mon Sep 17 00:00:00 2001 From: Jacob Parish Date: Tue, 2 Apr 2019 17:04:27 -0500 Subject: [PATCH 430/618] test(SingleDatePicker): add tests for `onFocusOut` event --- karma.conf.js | 2 +- package.json | 1 - src/components/SingleDatePicker.jsx | 11 +++- test/components/SingleDatePicker_spec.jsx | 62 ++++++++++++++++++++--- 4 files changed, 65 insertions(+), 11 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index 31d3de8a5b..732ff364b5 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -11,6 +11,7 @@ module.exports = (config) => { files: ['test/browser-main.js'], webpack: { + mode: 'development', externals: { sinon: true, }, @@ -36,7 +37,6 @@ module.exports = (config) => { presets: ['airbnb'], }, }, - { test: /\.json$/, loader: 'json-loader' }, // Inject the Airbnb shims into the bundle { test: /test\/_helpers/, loader: 'imports-loader?shims=airbnb-js-shims' }, diff --git a/package.json b/package.json index 3b3a9c4d2e..170c56d98f 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,6 @@ "git-directory-deploy": "^1.5.1", "imports-loader": "^0.8.0", "in-publish": "^2.0.0", - "json-loader": "^0.5.7", "karma": "^3.1.1", "karma-chai": "^0.1.0", "karma-firefox-launcher": "^1.1.0", diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index 931f498ab2..850e63af46 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -249,7 +249,16 @@ class SingleDatePicker extends React.PureComponent { onFocusOut(e) { const { onFocusChange } = this.props; - if (this.dayPickerContainer.contains(e.relatedTarget || e.target) || this.dayPickerContainer.contains(e.target)) return; + // In cases where **relatedTarget** is not null, it points to the right + // element here. However, in cases where it is null (such as clicking on a + // specific day) or it is **document.body** (IE11), the appropriate value is **event.target**. + // + // We handle both situations here by using the ` || ` operator to fallback + // to *event.target** when **relatedTarget** is not provided. + const relatedTarget = e.relatedTarget === document.body + ? e.target + : (e.relatedTarget || e.target); + if (this.dayPickerContainer.contains(relatedTarget)) return; onFocusChange({ focused: false }); } diff --git a/test/components/SingleDatePicker_spec.jsx b/test/components/SingleDatePicker_spec.jsx index a0094df441..6d63f862d9 100644 --- a/test/components/SingleDatePicker_spec.jsx +++ b/test/components/SingleDatePicker_spec.jsx @@ -595,20 +595,66 @@ describe('SingleDatePicker', () => { }); }); - describe('#onFocusOut', () => { - it('calls props.onFocusChange with focused: false', () => { - const onFocusChangeStub = sinon.stub(); - const wrapper = shallow(( + describeIfWindow('#onFocusOut', () => { + let wrapper; + let ctrl; + let onFocusChangeStub; + let dpcContainsStub; + + beforeEach(() => { + onFocusChangeStub = sinon.stub(); + dpcContainsStub = sinon.stub(); + wrapper = shallow(( )).dive(); - - wrapper.instance().dayPickerContainer = { - contains: () => false, + ctrl = wrapper.instance(); + ctrl.dayPickerContainer = { + contains: dpcContainsStub.returns(true), }; + }); + + afterEach(() => { + onFocusChangeStub.reset(); + dpcContainsStub.reset(); + }); - wrapper.instance().onFocusOut({}); + it('calls props.onFocusChange with focused: false when dayPickerContainer does not contain the target', () => { + dpcContainsStub.returns(false); + ctrl.onFocusOut({}); expect(onFocusChangeStub.callCount).to.equal(1); expect(onFocusChangeStub.getCall(0).args[0]).to.deep.equal({ focused: false }); }); + + it('should not call props.onFocusChange when dayPickerContainer contains the target', () => { + ctrl.onFocusOut({}); + expect(onFocusChangeStub.callCount).to.equal(0); + }); + + it('should check the target when related target is the same as the document body', () => { + const event = { + relatedTarget: document.body, + target: 'target', + }; + ctrl.onFocusOut(event); + expect(dpcContainsStub.getCall(0).args[0]).to.equal(event.target); + }); + + it('should check the target when related target is defined', () => { + const event = { + relatedTarget: 'related target', + target: 'target', + }; + ctrl.onFocusOut(event); + expect(dpcContainsStub.getCall(0).args[0]).to.equal(event.relatedTarget); + }); + + it('should check the target when related target is not defined', () => { + const event = { + relatedTarget: undefined, + target: 'target', + }; + ctrl.onFocusOut(event); + expect(dpcContainsStub.getCall(0).args[0]).to.equal(event.target); + }); }); }); From 5f1964b38c5d020b40a0caf897e6612179ada9c9 Mon Sep 17 00:00:00 2001 From: Gideon Pyzer Date: Sat, 6 Apr 2019 11:37:38 -0500 Subject: [PATCH 431/618] [New] `CalendarMonth`: Pass isVisible to renderMonthElement --- README.md | 6 +++--- src/components/CalendarMonth.jsx | 7 ++++++- test/components/CalendarMonth_spec.jsx | 20 ++++++++++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6ca688041e..862afeb695 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,7 @@ numberOfMonths: PropTypes.number, keepOpenOnDateSelect: PropTypes.bool, reopenPickerOnClearDates: PropTypes.bool, renderCalendarInfo: PropTypes.func, -renderMonthElement: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), PropTypes.func, // ({ month, onMonthSelect, onYearSelect }) => PropTypes.node, +renderMonthElement: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), PropTypes.func, // ({ month, onMonthSelect, onYearSelect, isVisible }) => PropTypes.node, hideKeyboardShortcutsPanel: PropTypes.bool, // navigation related props @@ -254,7 +254,7 @@ numberOfMonths: PropTypes.number, keepOpenOnDateSelect: PropTypes.bool, reopenPickerOnClearDate: PropTypes.bool, renderCalendarInfo: PropTypes.func, -renderMonthElement: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), // ({ month, onMonthSelect, onYearSelect }) => PropTypes.node, +renderMonthElement: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), // ({ month, onMonthSelect, onYearSelect, isVisible }) => PropTypes.node, hideKeyboardShortcutsPanel: PropTypes.bool, daySize: nonNegativeInteger, isRTL: PropTypes.bool, @@ -308,7 +308,7 @@ The following is a list of other *OPTIONAL* props you may provide to the `DayPic withPortal: PropTypes.bool, initialVisibleMonth: PropTypes.func, renderCalendarInfo: PropTypes.func, - renderMonthElement: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), // ({ month, onMonthSelect, onYearSelect }) => PropTypes.node, + renderMonthElement: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), // ({ month, onMonthSelect, onYearSelect, isVisible }) => PropTypes.node, onOutsideClick: PropTypes.func, keepOpenOnDateSelect: PropTypes.bool, noBorder: PropTypes.bool, diff --git a/src/components/CalendarMonth.jsx b/src/components/CalendarMonth.jsx index a894432ff5..76e540a1c7 100644 --- a/src/components/CalendarMonth.jsx +++ b/src/components/CalendarMonth.jsx @@ -197,7 +197,12 @@ class CalendarMonth extends React.PureComponent { )} > {renderMonthElement ? ( - renderMonthElement({ month, onMonthSelect, onYearSelect }) + renderMonthElement({ + month, + onMonthSelect, + onYearSelect, + isVisible, + }) ) : ( {monthTitle} diff --git a/test/components/CalendarMonth_spec.jsx b/test/components/CalendarMonth_spec.jsx index 777aa73e2d..05a3a6dd82 100644 --- a/test/components/CalendarMonth_spec.jsx +++ b/test/components/CalendarMonth_spec.jsx @@ -1,6 +1,7 @@ import React from 'react'; import { expect } from 'chai'; import { shallow } from 'enzyme'; +import sinon from 'sinon-sandbox'; import moment from 'moment'; import CalendarMonth from '../../src/components/CalendarMonth'; @@ -26,5 +27,24 @@ describe('CalendarMonth', () => { expect(captionWrapper.text()).to.equal(MONTH.format('MMMM YYYY')); }); }); + + it('renderMonthElement renders month element when month changes', () => { + const renderMonthElementStub = sinon.stub().returns(
    ); + const wrapper = shallow().dive(); + wrapper.setProps({ month: moment().subtract(1, 'months') }); + + const [{ + month, + onMonthSelect, + onYearSelect, + isVisible, + }] = renderMonthElementStub.getCall(0).args; + + expect(moment.isMoment(month)).to.equal(true); + expect(typeof onMonthSelect).to.equal('function'); + expect(typeof onYearSelect).to.equal('function'); + expect(typeof isVisible).to.equal('boolean'); + expect(wrapper.find('#month-element').exists()).to.equal(true); + }); }); }); From 5a184695cf14718a587e7d2d01cf5d8512f3251a Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sat, 18 May 2019 09:02:30 -0700 Subject: [PATCH 432/618] [Dev Deps] update `@babel/cli`, `@babel/core`, `@babel/register`, `acorn`, `airbnb-js-shims`, `aphrodite`, `babel-loader`, `babel-plugin-inline-react-svg`, `babel-plugin-istanbul`, `babel-preset-airbnb`, `coveralls`, `enzyme`, `enzyme-adapter-react-helper`, `eslint`, `eslint-plugin-import`, `eslint-plugin-jsx-a11y`, `eslint-plugin-react`, `node-sass`, `rimraf`, `sinon`, `webpack` --- package.json | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 4c7a0cca1b..71df533dcc 100644 --- a/package.json +++ b/package.json @@ -51,34 +51,34 @@ }, "homepage": "https://github.com/airbnb/react-dates#readme", "devDependencies": { - "@babel/cli": "^7.1.2", - "@babel/core": "^7.1.2", - "@babel/register": "^7.0.0", + "@babel/cli": "^7.4.4", + "@babel/core": "^7.4.4", + "@babel/register": "^7.4.4", "@storybook/addon-actions": "^4.0.0", "@storybook/addon-info": "^4.0.0", "@storybook/addon-links": "^4.0.0", "@storybook/addon-options": "^4.0.0", "@storybook/react": "^4.0.0", - "acorn": "^6.0.2", - "airbnb-js-shims": "^2.1.1", - "aphrodite": "^2.2.3", - "babel-loader": "^8.0.4", - "babel-plugin-inline-react-svg": "^1.0.1", + "acorn": "^6.1.1", + "airbnb-js-shims": "^2.2.0", + "aphrodite": "^2.3.1", + "babel-loader": "^8.0.6", + "babel-plugin-inline-react-svg": "^1.1.0", "babel-plugin-inline-svg": "^1.0.0", - "babel-plugin-istanbul": "^5.1.0", + "babel-plugin-istanbul": "^5.1.4", "babel-plugin-transform-replace-object-assign": "^2.0.0", - "babel-preset-airbnb": "^3.0.1", + "babel-preset-airbnb": "^3.2.1", "chai": "^4.2.0", "clean-css": "^4.2.1", - "coveralls": "^3.0.2", + "coveralls": "^3.0.3", "cross-env": "^5.2.0", - "enzyme": "^3.7.0", - "enzyme-adapter-react-helper": "^1.3.1", - "eslint": "^5.7.0", + "enzyme": "^3.9.0", + "enzyme-adapter-react-helper": "^1.3.4", + "eslint": "^5.16.0", "eslint-config-airbnb": "^17.1.0", - "eslint-plugin-import": "^2.14.0", - "eslint-plugin-jsx-a11y": "^6.1.2", - "eslint-plugin-react": "^7.11.1", + "eslint-plugin-import": "^2.17.2", + "eslint-plugin-jsx-a11y": "^6.2.1", + "eslint-plugin-react": "^7.13.0", "eslint-plugin-react-with-styles": "^2.1.0", "git-directory-deploy": "^1.5.1", "imports-loader": "^0.8.0", @@ -91,22 +91,22 @@ "karma-webpack": "^4.0.0-rc.2", "mocha": "^3.5.3", "mocha-wrap": "^2.1.2", - "moment": "^2.22.2", + "moment": "^2.24.0", "moment-jalaali": "^0.7.4", - "node-sass": "^4.9.3", + "node-sass": "^4.12.0", "nyc": "^12.0.2", "raw-loader": "^0.5.1", "react": "^0.14 || ^15.5.4 || ^16.1.1", "react-dom": "^0.14 || ^15.5.4 || ^16.1.1", "react-with-styles-interface-aphrodite": "^5.0.1", "react-with-styles-interface-css-compiler": "^2.0.0", - "rimraf": "^2.6.2", + "rimraf": "^2.6.3", "safe-publish-latest": "^1.1.2", "sass-loader": "^7.1.0", "sinon": "^6.3.5", "sinon-sandbox": "^2.0.0", "style-loader": "^0.20.3", - "webpack": "^4.23.1", + "webpack": "^4.31.0", "why-did-you-update": "^1.0.6" }, "dependencies": { From 0d2a295a4f7ed40e92a7c3951d9e840a787935bc Mon Sep 17 00:00:00 2001 From: Jacob Parish Date: Mon, 8 Apr 2019 07:55:21 -0500 Subject: [PATCH 433/618] [test] transform modules in karma so sinon can stub properly - require karma tests - temporarily turn off module transformation on airbnb-preset --- .travis.yml | 3 --- karma.conf.js | 9 +++++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index cd38096a9d..394623fcae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,3 @@ matrix: env: COVERAGE=true TEST=false - node_js: "lts/*" env: KARMA=true TEST=false - allow_failures: - - node_js: "9" - - env: KARMA=true TEST=false # temporary diff --git a/karma.conf.js b/karma.conf.js index 732ff364b5..6e34dceadf 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -33,8 +33,13 @@ module.exports = (config) => { path.join(__dirname, 'test'), require.resolve('airbnb-js-shims'), ], - query: { - presets: ['airbnb'], + options: { + presets: [ + // setting modules to false so it does not transform twice + ['airbnb', { modules: false }], + // transform to cjs so sinon can stub properly + ['@babel/preset-env', { modules: 'cjs' }], + ], }, }, From d847a91c718fd60aa54eef22e1867905748bae4f Mon Sep 17 00:00:00 2001 From: Kris Salvador Date: Sat, 13 Apr 2019 23:46:13 -0400 Subject: [PATCH 434/618] Fix broken test --- src/components/DayPickerRangeController.jsx | 29 +++++++++---------- .../DayPickerRangeController_spec.jsx | 15 ++++++---- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index c265c7e836..484dda50c3 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -113,21 +113,21 @@ const defaultProps = { endDate: undefined, // TODO: use null minDate: null, maxDate: null, - onDatesChange() { }, + onDatesChange() {}, startDateOffset: undefined, endDateOffset: undefined, focusedInput: null, - onFocusChange() { }, - onClose() { }, + onFocusChange() {}, + onClose() {}, keepOpenOnDateSelect: false, minimumNights: 1, disabled: false, - isOutsideRange() { }, - isDayBlocked() { }, - isDayHighlighted() { }, - getMinNightsForHoverDate() { }, + isOutsideRange() {}, + isDayBlocked() {}, + isDayHighlighted() {}, + getMinNightsForHoverDate() {}, // DayPicker props renderMonthText: null, @@ -143,9 +143,9 @@ const defaultProps = { navNext: null, noNavButtons: false, - onPrevMonthClick() { }, - onNextMonthClick() { }, - onOutsideClick() { }, + onPrevMonthClick() {}, + onNextMonthClick() {}, + onOutsideClick() {}, renderCalendarDay: undefined, renderDayContents: null, @@ -161,11 +161,11 @@ const defaultProps = { horizontalMonthPadding: 13, // accessibility - onBlur() { }, + onBlur() {}, isFocused: false, showKeyboardShortcuts: false, - onTab() { }, - onShiftTab() { }, + onTab() {}, + onShiftTab() {}, // i18n monthFormat: 'MMMM YYYY', @@ -727,8 +727,7 @@ export default class DayPickerRangeController extends React.PureComponent { if (endDate) { const startSpan = endDate.clone().subtract(minimumNights, 'days'); - const endSpan = endDate.clone(); - modifiers = this.deleteModifierFromRange(modifiers, startSpan, endSpan, 'before-hovered-end'); + modifiers = this.deleteModifierFromRange(modifiers, startSpan, endDate, 'before-hovered-end'); if (isSameDay(day, endDate)) { const newStartSpan = endDate.clone().subtract(minimumNights, 'days'); diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index a62ef71fac..2c086ddaa3 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -40,8 +40,8 @@ describe('DayPickerRangeController', () => { describe('#componentDidMount', () => { const props = { ...DayPickerRangeController.defaultProps, - onDatesChange() {}, - onFocusChange() {}, + onDatesChange() { }, + onFocusChange() { }, }; describe('phrases', () => { @@ -98,8 +98,8 @@ describe('DayPickerRangeController', () => { describe('#componentWillReceiveProps', () => { const props = { ...DayPickerRangeController.defaultProps, - onDatesChange() {}, - onFocusChange() {}, + onDatesChange() { }, + onFocusChange() { }, }; describe('rebuilding currentMonth/visibleDays', () => { @@ -2306,10 +2306,13 @@ describe('DayPickerRangeController', () => { wrapper.setState({ hoverDate }); deleteModifierFromRangeSpy.resetHistory(); wrapper.instance().onDayMouseEnter(moment().add(10, 'days')); - expect(deleteModifierFromRangeSpy.callCount).to.equal(1); + expect(deleteModifierFromRangeSpy.callCount).to.equal(2); expect(deleteModifierFromRangeSpy.getCall(0).args[1]).to.equal(hoverDate); expect(deleteModifierFromRangeSpy.getCall(0).args[2]).to.equal(endDate); expect(deleteModifierFromRangeSpy.getCall(0).args[3]).to.equal('hovered-span'); + expect(isSameDay(deleteModifierFromRangeSpy.getCall(1).args[1], endDate.subtract(DayPickerRangeController.defaultProps.minimumNights, 'days'))).to.equal(true); + expect(deleteModifierFromRangeSpy.getCall(1).args[2]).to.equal(endDate); + expect(deleteModifierFromRangeSpy.getCall(1).args[3]).to.equal('before-hovered-end'); }); }); @@ -4429,7 +4432,7 @@ describe('DayPickerRangeController', () => { describe('renderKeyboardShortcutsButton prop', () => { it('pass down custom button render function', () => { - const testRenderKeyboardShortcutsButton = () => {}; + const testRenderKeyboardShortcutsButton = () => { }; const wrapper = shallow( Date: Sun, 14 Apr 2019 11:15:41 -0400 Subject: [PATCH 435/618] Eslint fixes --- src/components/CalendarDay.jsx | 2 +- src/components/DayPickerRangeController.jsx | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index deb49d71db..1335e6b534 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -353,5 +353,5 @@ export default withStyles(({ reactDates: { color, font } }) => ({ CalendarDay__selected_start_in_hovered_span: {}, CalendarDay__selected_end_in_hovered_span: {}, CalendarDay__selected_start_no_selected_end: {}, - CalendarDay__selected_end_no_selected_start: {} + CalendarDay__selected_end_no_selected_start: {}, }), { pureComponent: typeof React.PureComponent !== 'undefined' })(CalendarDay); diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 484dda50c3..a9b7fab251 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -213,10 +213,10 @@ export default class DayPickerRangeController extends React.PureComponent { 'hovered-start-first-possible-end': (day, hoverDate) => this.isFirstPossibleEndDateForHoveredStartDate(day, hoverDate), 'hovered-start-blocked-minimum-nights': (day, hoverDate) => this.doesNotMeetMinNightsForHoveredStartDate(day, hoverDate), 'before-hovered-end': day => this.isDayBeforeHoveredEndDate(day), - 'no-selected-start-before-selected-end': (day) => this.beforeSelectedEnd(day) && !props.startDate, + 'no-selected-start-before-selected-end': day => this.beforeSelectedEnd(day) && !props.startDate, 'selected-start-in-hovered-span': (day, hoverDate) => this.isStartDate(day) && isAfterDay(hoverDate, day), - 'selected-start-no-selected-end': (day) => this.isStartDate(day) && !props.endDate, - 'selected-end-no-selected-start': (day) => this.isEndDate(day) && !props.startDate + 'selected-start-no-selected-end': day => this.isStartDate(day) && !props.endDate, + 'selected-end-no-selected-start': day => this.isEndDate(day) && !props.startDate, }; const { currentMonth, visibleDays } = this.getStateForNewMonth(props); @@ -347,7 +347,7 @@ export default class DayPickerRangeController extends React.PureComponent { const momentObj = moment(day); if (isBeforeDay(momentObj, endDate)) { - modifiers = this.addModifier(modifiers, momentObj, 'no-selected-start-before-selected-end') + modifiers = this.addModifier(modifiers, momentObj, 'no-selected-start-before-selected-end'); } }); }); From 78c20b6ceb968d8b46729bb8d41c6d6903ea1ca8 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Mon, 13 May 2019 15:56:07 -0700 Subject: [PATCH 436/618] Version 20.2.0 --- CHANGELOG.md | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd629140eb..3c0f69ee4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,9 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> -## [Unreleased] -- [fix] Fix date selection in the SDP ([#1530]) - +## 20.2.0 +- [fix] Fix date selection in the SDP ([#1530](https://github.com/airbnb/react-dates/pull/1530)) +- [new] Add explicit aria label props ([#1594](https://github.com/airbnb/react-dates/pull/1594)) ## 20.1.0 - [new] Add `renderKeyboardShortcutsButton` prop ([#1576](https://github.com/airbnb/react-dates/pull/1576)) diff --git a/package.json b/package.json index 170c56d98f..4c7a0cca1b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "20.1.0", + "version": "20.2.0", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From c20807f4e85e3739ab9804e3b4ff2571c59d8bbe Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 26 May 2019 13:23:22 -0700 Subject: [PATCH 437/618] [Tests] fix travis-ci for karma, per https://benlimmer.com/2019/01/14/travis-ci-xvfb/ --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 394623fcae..cb0705112c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,10 @@ node_js: - "6" before_install: - 'nvm install-latest-npm' +services: + - xvfb before_script: - 'if [ -n "${KARMA-}" ]; then export DISPLAY=:99.0; fi' - - 'if [ -n "${KARMA-}" ]; then sh -e /etc/init.d/xvfb start; fi' - - 'if [ -n "${KARMA-}" ]; then sleep 3; fi' script: - 'if [ -n "${LINT-}" ]; then npm run lint ; fi' - 'if [ -n "${BUILD-}" ]; then npm run build ; fi' From 888f650565b4256dde39210fdba7faadba969516 Mon Sep 17 00:00:00 2001 From: alok Date: Sun, 26 May 2019 11:33:55 +0530 Subject: [PATCH 438/618] [Docs] added initialVisibleMonth to DayPickerRangeController in ReadMe Fixes #1619. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 862afeb695..c79b204f1e 100644 --- a/README.md +++ b/README.md @@ -296,6 +296,7 @@ Here is the minimum *REQUIRED* setup you need to get the `DayPickerRangeControll onDatesChange={({ startDate, endDate }) => this.setState({ startDate, endDate })} // PropTypes.func.isRequired, focusedInput={this.state.focusedInput} // PropTypes.oneOf([START_DATE, END_DATE]) or null, onFocusChange={focusedInput => this.setState({ focusedInput })} // PropTypes.func.isRequired, + initialVisibleMonth={() => moment().add(2, "M")} // PropTypes.func or null, /> ``` From 4073d24c9b76966156866cf4972faa444bbd9cfc Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Thu, 30 May 2019 08:41:33 -0700 Subject: [PATCH 439/618] Compile classes in loose mode This is low-risk and will give us a little better bundle size and runtime performance. More information: https://babeljs.io/docs/en/babel-plugin-transform-classes#loose --- .babelrc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.babelrc b/.babelrc index 1b0f8475f1..4e91c4584c 100644 --- a/.babelrc +++ b/.babelrc @@ -1,7 +1,7 @@ { "env": { "test": { - "presets": ["airbnb"], + "presets": [["airbnb", { looseClasses: true }]], "plugins": [ ["inline-react-svg", { "svgo": false @@ -13,7 +13,7 @@ }, "development": { - "presets": ["airbnb"], + "presets": [["airbnb", { looseClasses: true }]], "plugins": [ ["inline-react-svg", { "svgo": false @@ -24,7 +24,7 @@ }, "production": { - "presets": [["airbnb", { removePropTypes: true }]], + "presets": [["airbnb", { looseClasses: true, removePropTypes: true }]], "plugins": [ ["inline-react-svg", { "svgo": false @@ -35,7 +35,7 @@ }, "cjs": { - "presets": [["airbnb", { removePropTypes: true }]], + "presets": [["airbnb", { looseClasses: true, removePropTypes: true }]], "plugins": [ ["inline-react-svg", { "svgo": false @@ -46,7 +46,7 @@ }, "esm": { - "presets": [["airbnb", { modules: false, removePropTypes: true }]], + "presets": [["airbnb", { looseClasses: true, modules: false, removePropTypes: true }]], "plugins": [ ["inline-react-svg", { "svgo": false From 9f541162d275da3458de36a68a9256e0383afb2c Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Thu, 30 May 2019 09:09:29 -0700 Subject: [PATCH 440/618] Optimize toISODateString and toISOMonthString These functions get called repeatedly when computing modifiers, which happens any time components like DayPickerRangeController receives props. By manually building this string instead of relying on moment's format function, we can get better performance. In my profiling, this cuts the time spent in DayPickerRangeController#componentWillReceiveProps from ~50ms to ~40ms. --- src/constants.js | 2 +- src/utils/toISODateString.js | 8 +++++--- src/utils/toISOMonthString.js | 8 +++++--- test/utils/toISODateString_spec.js | 6 ++++++ 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/constants.js b/src/constants.js index de308b98e5..31eec9a200 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,6 +1,6 @@ export const DISPLAY_FORMAT = 'L'; export const ISO_FORMAT = 'YYYY-MM-DD'; -export const ISO_MONTH_FORMAT = 'YYYY-MM'; +export const ISO_MONTH_FORMAT = 'YYYY-MM'; // TODO delete this line of dead code on next breaking change export const START_DATE = 'startDate'; export const END_DATE = 'endDate'; diff --git a/src/utils/toISODateString.js b/src/utils/toISODateString.js index de3cd5ac6b..eb28a1a5f4 100644 --- a/src/utils/toISODateString.js +++ b/src/utils/toISODateString.js @@ -2,11 +2,13 @@ import moment from 'moment'; import toMomentObject from './toMomentObject'; -import { ISO_FORMAT } from '../constants'; - export default function toISODateString(date, currentFormat) { const dateObj = moment.isMoment(date) ? date : toMomentObject(date, currentFormat); if (!dateObj) return null; - return dateObj.format(ISO_FORMAT); + // Template strings compiled in strict mode uses concat, which is slow. Since + // this code is in a hot path and we want it to be as fast as possible, we + // want to use old-fashioned +. + // eslint-disable-next-line prefer-template + return dateObj.year() + '-' + String(dateObj.month() + 1).padStart(2, '0') + '-' + String(dateObj.date()).padStart(2, '0'); } diff --git a/src/utils/toISOMonthString.js b/src/utils/toISOMonthString.js index d024d51c5b..f480e5cc6f 100644 --- a/src/utils/toISOMonthString.js +++ b/src/utils/toISOMonthString.js @@ -2,11 +2,13 @@ import moment from 'moment'; import toMomentObject from './toMomentObject'; -import { ISO_MONTH_FORMAT } from '../constants'; - export default function toISOMonthString(date, currentFormat) { const dateObj = moment.isMoment(date) ? date : toMomentObject(date, currentFormat); if (!dateObj) return null; - return dateObj.format(ISO_MONTH_FORMAT); + // Template strings compiled in strict mode uses concat, which is slow. Since + // this code is in a hot path and we want it to be as fast as possible, we + // want to use old-fashioned +. + // eslint-disable-next-line prefer-template + return dateObj.year() + '-' + String(dateObj.month() + 1).padStart(2, '0'); } diff --git a/test/utils/toISODateString_spec.js b/test/utils/toISODateString_spec.js index d089c8c0e1..7af4cff558 100644 --- a/test/utils/toISODateString_spec.js +++ b/test/utils/toISODateString_spec.js @@ -15,6 +15,12 @@ describe('toISODateString', () => { expect(dateString).to.equal('1991-07-13'); }); + it('matches moment format behavior', () => { + const testDate = moment('1991-07-13'); + const dateString = toISODateString(testDate); + expect(dateString).to.equal(testDate.format(ISO_FORMAT)); + }); + it('converts iso date string to ISO date string', () => { const testDate = moment('1991-07-13'); const dateString = toISODateString(testDate.format(ISO_FORMAT)); From 5e0c8c140e68528f84e1313d7293cca90dab44b8 Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Thu, 30 May 2019 09:26:09 -0700 Subject: [PATCH 441/618] Optimize isDayVisible Cloning is a little expensive, so we want to do it as little as possible. Here we clone the month once and keep mutating that moment object. In my profiling this change by itself appears to reduce the amount of time spent in this function when updating DayPickeRangeController from ~16ms to ~11ms, with almost all of the time being spent in Moment clone. --- src/utils/isDayVisible.js | 20 ++++++++++++++++---- test/utils/isDayVisible_spec.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/utils/isDayVisible.js b/src/utils/isDayVisible.js index b04f2f6355..1bd4b22e71 100644 --- a/src/utils/isDayVisible.js +++ b/src/utils/isDayVisible.js @@ -5,11 +5,23 @@ import isAfterDay from './isAfterDay'; export default function isDayVisible(day, month, numberOfMonths, enableOutsideDays) { if (!moment.isMoment(day)) return false; - let firstDayOfFirstMonth = month.clone().startOf('month'); - if (enableOutsideDays) firstDayOfFirstMonth = firstDayOfFirstMonth.startOf('week'); + + // Cloning is a little expensive, so we want to do it as little as possible. + // Here we clone the month once and keep mutating that moment object. + const mutableMonth = month.clone(); + + const firstDayOfFirstMonth = enableOutsideDays + ? mutableMonth.startOf('month').startOf('week') + : mutableMonth.startOf('month'); + if (isBeforeDay(day, firstDayOfFirstMonth)) return false; - let lastDayOfLastMonth = month.clone().add(numberOfMonths - 1, 'months').endOf('month'); - if (enableOutsideDays) lastDayOfLastMonth = lastDayOfLastMonth.endOf('week'); + const lastDayOfLastMonth = enableOutsideDays + // We need to call endOf('week') when enableOutsideDays is true, because we + // are reusing the moment object, and our earlier mutation may have moved it + // to a previous month. This should snap us back to a good starting place. + ? mutableMonth.endOf('week').add(numberOfMonths - 1, 'months').endOf('month').endOf('week') + : mutableMonth.add(numberOfMonths - 1, 'months').endOf('month'); + return !isAfterDay(day, lastDayOfLastMonth); } diff --git a/test/utils/isDayVisible_spec.js b/test/utils/isDayVisible_spec.js index 7af0f334be..f42455b549 100644 --- a/test/utils/isDayVisible_spec.js +++ b/test/utils/isDayVisible_spec.js @@ -21,4 +21,32 @@ describe('#isDayVisible', () => { const currentMonth = moment().add(2, 'months'); expect(isDayVisible(test, currentMonth, 2)).to.equal(false); }); + + describe('enableOutsideDays', () => { + it('returns true if arg is in partial week before visible months', () => { + const test = moment('2019-04-30'); + const currentMonth = moment('2019-05-01'); + expect(isDayVisible(test, currentMonth, 1, false)).to.equal(false); + expect(isDayVisible(test, currentMonth, 1, true)).to.equal(true); + }); + + it('returns true if arg is in partial week after visible months', () => { + const test = moment('2019-06-01'); + const currentMonth = moment('2019-05-01'); + expect(isDayVisible(test, currentMonth, 1, false)).to.equal(false); + expect(isDayVisible(test, currentMonth, 1, true)).to.equal(true); + }); + + it('returns false if arg is before partial week before visible months', () => { + const test = moment('2019-04-27'); + const currentMonth = moment('2019-05-01'); + expect(isDayVisible(test, currentMonth, 1, true)).to.equal(false); + }); + + it('returns false if arg is after partial week after visible months', () => { + const test = moment('2019-06-03'); + const currentMonth = moment('2019-05-01'); + expect(isDayVisible(test, currentMonth, 1, true)).to.equal(false); + }); + }); }); From e3b9c8d36564f66b5d13d0f3ee4bbaf4fbc1df79 Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Thu, 30 May 2019 15:46:04 -0700 Subject: [PATCH 442/618] Add caching to isDayVisible After the optimizations I applied to generating modifiers in https://github.com/airbnb/react-dates/pull/1659, I noticed that about 50% of the time in componentWillReceiveProps is spent in isDayVisible() doing moment operations to generate the boundaries for the check. Since these boundaries remain very stable across each call, we can reduce most of the work done here to very little by adding some caching of these values. On top of my reduce optimizations, this brings down the time spent in componentWillReceiveProps to ~15ms. I'm not doing anything to limit the size of these Maps, so this will leak some memory, but I think the size of the cache will be really small in most cases, so I'm not sure it matters. --- src/utils/isDayVisible.js | 55 ++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/src/utils/isDayVisible.js b/src/utils/isDayVisible.js index 1bd4b22e71..34b9420339 100644 --- a/src/utils/isDayVisible.js +++ b/src/utils/isDayVisible.js @@ -2,26 +2,55 @@ import moment from 'moment'; import isBeforeDay from './isBeforeDay'; import isAfterDay from './isAfterDay'; +import toISOMonthString from './toISOMonthString'; + +const startCacheOutsideDays = new Map(); +const endCacheOutsideDays = new Map(); + +const startCacheInsideDays = new Map(); +const endCacheInsideDays = new Map(); export default function isDayVisible(day, month, numberOfMonths, enableOutsideDays) { if (!moment.isMoment(day)) return false; // Cloning is a little expensive, so we want to do it as little as possible. - // Here we clone the month once and keep mutating that moment object. - const mutableMonth = month.clone(); - const firstDayOfFirstMonth = enableOutsideDays - ? mutableMonth.startOf('month').startOf('week') - : mutableMonth.startOf('month'); + const startKey = toISOMonthString(month); + // eslint-disable-next-line prefer-template + const endKey = startKey + '+' + numberOfMonths; + + if (enableOutsideDays) { + if (!startCacheOutsideDays.has(startKey)) { + startCacheOutsideDays.set(startKey, month.clone().startOf('month').startOf('week')); + } + + if (isBeforeDay(day, startCacheOutsideDays.get(startKey))) return false; + + if (!endCacheOutsideDays.has(endKey)) { + endCacheOutsideDays.set( + endKey, + month.clone().endOf('week').add(numberOfMonths - 1, 'months').endOf('month') + .endOf('week'), + ); + } + + return !isAfterDay(day, endCacheOutsideDays.get(endKey)); + } + + // !enableOutsideDays + + if (!startCacheInsideDays.has(startKey)) { + startCacheInsideDays.set(startKey, month.clone().startOf('month')); + } - if (isBeforeDay(day, firstDayOfFirstMonth)) return false; + if (isBeforeDay(day, startCacheInsideDays.get(startKey))) return false; - const lastDayOfLastMonth = enableOutsideDays - // We need to call endOf('week') when enableOutsideDays is true, because we - // are reusing the moment object, and our earlier mutation may have moved it - // to a previous month. This should snap us back to a good starting place. - ? mutableMonth.endOf('week').add(numberOfMonths - 1, 'months').endOf('month').endOf('week') - : mutableMonth.add(numberOfMonths - 1, 'months').endOf('month'); + if (!endCacheInsideDays.has(endKey)) { + endCacheInsideDays.set( + endKey, + month.clone().add(numberOfMonths - 1, 'months').endOf('month'), + ); + } - return !isAfterDay(day, lastDayOfLastMonth); + return !isAfterDay(day, endCacheInsideDays.get(endKey)); } From 0e2ed9a322dd0d8f9782614b40a362413406a76b Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Thu, 30 May 2019 16:02:40 -0700 Subject: [PATCH 443/618] Avoid some moment object creation in modifier generation Creating these moment objects is expensive, and we do it a lot in addModifier/deleteModifier, which get called a lot whenever these day pickers update. For this particular one, we almost always want the same value every time it is called, so we can memoize the previous value, a la reselect. This reduces the amount of time spent generating modifiers from ~46ms to ~32ms. --- src/components/DayPickerRangeController.jsx | 5 +++-- src/components/DayPickerSingleDateController.jsx | 5 +++-- src/utils/getPreviousMonthMemoLast.js | 11 +++++++++++ 3 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 src/utils/getPreviousMonthMemoLast.js diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 2bc4b65075..15cbeb8ce7 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -39,6 +39,7 @@ import { } from '../constants'; import DayPicker from './DayPicker'; +import getPreviousMonthMemoLast from '../utils/getPreviousMonthMemoLast'; const propTypes = forbidExtraProps({ startDate: momentPropTypes.momentObj, @@ -990,7 +991,7 @@ export default class DayPickerRangeController extends React.PureComponent { if (orientation === VERTICAL_SCROLLABLE) { numberOfMonths = Object.keys(visibleDays).length; } else { - currentMonth = currentMonth.clone().subtract(1, 'month'); + currentMonth = getPreviousMonthMemoLast(currentMonth); numberOfMonths += 2; } if (!day || !isDayVisible(day, currentMonth, numberOfMonths, enableOutsideDays)) { @@ -1055,7 +1056,7 @@ export default class DayPickerRangeController extends React.PureComponent { if (orientation === VERTICAL_SCROLLABLE) { numberOfMonths = Object.keys(visibleDays).length; } else { - currentMonth = currentMonth.clone().subtract(1, 'month'); + currentMonth = getPreviousMonthMemoLast(currentMonth); numberOfMonths += 2; } if (!day || !isDayVisible(day, currentMonth, numberOfMonths, enableOutsideDays)) { diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 2650a47842..66a018b1c3 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -30,6 +30,7 @@ import { } from '../constants'; import DayPicker from './DayPicker'; +import getPreviousMonthMemoLast from '../utils/getPreviousMonthMemoLast'; const propTypes = forbidExtraProps({ date: momentPropTypes.momentObj, @@ -517,7 +518,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { if (orientation === VERTICAL_SCROLLABLE) { numberOfMonths = Object.keys(visibleDays).length; } else { - currentMonth = currentMonth.clone().subtract(1, 'month'); + currentMonth = getPreviousMonthMemoLast(currentMonth); numberOfMonths += 2; } if (!day || !isDayVisible(day, currentMonth, numberOfMonths, enableOutsideDays)) { @@ -571,7 +572,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { if (orientation === VERTICAL_SCROLLABLE) { numberOfMonths = Object.keys(visibleDays).length; } else { - currentMonth = currentMonth.clone().subtract(1, 'month'); + currentMonth = getPreviousMonthMemoLast(currentMonth); numberOfMonths += 2; } if (!day || !isDayVisible(day, currentMonth, numberOfMonths, enableOutsideDays)) { diff --git a/src/utils/getPreviousMonthMemoLast.js b/src/utils/getPreviousMonthMemoLast.js new file mode 100644 index 0000000000..79b552af16 --- /dev/null +++ b/src/utils/getPreviousMonthMemoLast.js @@ -0,0 +1,11 @@ +let getPreviousMonthMemoKey; +let getPreviousMonthMemoValue; + +export default function getPreviousMonthMemoLast(month) { + if (month !== getPreviousMonthMemoKey) { + getPreviousMonthMemoKey = month; + getPreviousMonthMemoValue = month.clone().subtract(1, 'month'); + } + + return getPreviousMonthMemoValue; +} From ea51d5af1f247baad95a6bf9abbb827b6caad40d Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Thu, 30 May 2019 09:40:22 -0700 Subject: [PATCH 444/618] Use accumulator-mutating reducers While profiling DayRangePickerController, I noticed that we spend a significant amount of time generating modifiers whenever anything is changed. A good amount of this cost comes from these reducers that unnecessarily create new objects and spread into them on every iteration. We can make this faster by mutating the accumulator while inside the reducer. In my profiling, this reduces the speed of deleteModifier from ~28ms to ~20ms, and I think it will reduce the memory usage, which should further help reduce garbage collection times. --- src/components/DayPickerRangeController.jsx | 14 ++++++-------- src/components/DayPickerSingleDateController.jsx | 14 ++++++-------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 15cbeb8ce7..ae87067a2b 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -1006,17 +1006,16 @@ export default class DayPickerRangeController extends React.PureComponent { Object.keys(visibleDays[monthKey]).indexOf(iso) > -1 )); - updatedDaysAfterAddition = monthsToUpdate.reduce((days, monthIso) => { + updatedDaysAfterAddition = monthsToUpdate.reduce((acc, monthIso) => { const month = updatedDays[monthIso] || visibleDays[monthIso]; const modifiers = new Set(month[iso]); modifiers.add(modifier); - return { - ...days, + return Object.assign(acc, { [monthIso]: { ...month, [iso]: modifiers, }, - }; + }); }, updatedDaysAfterAddition); } else { const monthIso = toISOMonthString(day); @@ -1071,17 +1070,16 @@ export default class DayPickerRangeController extends React.PureComponent { Object.keys(visibleDays[monthKey]).indexOf(iso) > -1 )); - updatedDaysAfterDeletion = monthsToUpdate.reduce((days, monthIso) => { + updatedDaysAfterDeletion = monthsToUpdate.reduce((acc, monthIso) => { const month = updatedDays[monthIso] || visibleDays[monthIso]; const modifiers = new Set(month[iso]); modifiers.delete(modifier); - return { - ...days, + return Object.assign(acc, { [monthIso]: { ...month, [iso]: modifiers, }, - }; + }); }, updatedDaysAfterDeletion); } else { const monthIso = toISOMonthString(day); diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 66a018b1c3..5ba65c493b 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -533,17 +533,16 @@ export default class DayPickerSingleDateController extends React.PureComponent { Object.keys(visibleDays[monthKey]).indexOf(iso) > -1 )); - updatedDaysAfterAddition = monthsToUpdate.reduce((days, monthIso) => { + updatedDaysAfterAddition = monthsToUpdate.reduce((acc, monthIso) => { const month = updatedDays[monthIso] || visibleDays[monthIso]; const modifiers = new Set(month[iso]); modifiers.add(modifier); - return { - ...days, + return Object.assign(acc, { [monthIso]: { ...month, [iso]: modifiers, }, - }; + }); }, updatedDaysAfterAddition); } else { const monthIso = toISOMonthString(day); @@ -587,17 +586,16 @@ export default class DayPickerSingleDateController extends React.PureComponent { Object.keys(visibleDays[monthKey]).indexOf(iso) > -1 )); - updatedDaysAfterDeletion = monthsToUpdate.reduce((days, monthIso) => { + updatedDaysAfterDeletion = monthsToUpdate.reduce((acc, monthIso) => { const month = updatedDays[monthIso] || visibleDays[monthIso]; const modifiers = new Set(month[iso]); modifiers.delete(modifier); - return { - ...days, + return Object.assign(acc, { [monthIso]: { ...month, [iso]: modifiers, }, - }; + }); }, updatedDaysAfterDeletion); } else { const monthIso = toISOMonthString(day); From ac0f70ce7f4148ca90c2f7c93ceb77ab807fa9e1 Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Thu, 30 May 2019 11:24:21 -0700 Subject: [PATCH 445/618] Mutate updatedDaysAfterDeletion in deleteModifier/addModifier We create this object in this function and then return it at the end, so it should be safe to just mutate it while we are in this function instead of rebuilding it immediately again. This reduces the amount of time spent in deleteModifier from ~20ms to ~16ms. --- src/components/DayPickerRangeController.jsx | 42 ++++++++----------- .../DayPickerSingleDateController.jsx | 42 ++++++++----------- 2 files changed, 36 insertions(+), 48 deletions(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index ae87067a2b..60b0263c06 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -1008,14 +1008,15 @@ export default class DayPickerRangeController extends React.PureComponent { updatedDaysAfterAddition = monthsToUpdate.reduce((acc, monthIso) => { const month = updatedDays[monthIso] || visibleDays[monthIso]; + const modifiers = new Set(month[iso]); modifiers.add(modifier); - return Object.assign(acc, { - [monthIso]: { - ...month, - [iso]: modifiers, - }, - }); + acc[monthIso] = { + ...month, + [iso]: modifiers, + }; + + return acc; }, updatedDaysAfterAddition); } else { const monthIso = toISOMonthString(day); @@ -1023,12 +1024,9 @@ export default class DayPickerRangeController extends React.PureComponent { const modifiers = new Set(month[iso]); modifiers.add(modifier); - updatedDaysAfterAddition = { - ...updatedDaysAfterAddition, - [monthIso]: { - ...month, - [iso]: modifiers, - }, + updatedDaysAfterAddition[monthIso] = { + ...month, + [iso]: modifiers, }; } @@ -1074,12 +1072,11 @@ export default class DayPickerRangeController extends React.PureComponent { const month = updatedDays[monthIso] || visibleDays[monthIso]; const modifiers = new Set(month[iso]); modifiers.delete(modifier); - return Object.assign(acc, { - [monthIso]: { - ...month, - [iso]: modifiers, - }, - }); + acc[monthIso] = { + ...month, + [iso]: modifiers, + }; + return acc; }, updatedDaysAfterDeletion); } else { const monthIso = toISOMonthString(day); @@ -1087,12 +1084,9 @@ export default class DayPickerRangeController extends React.PureComponent { const modifiers = new Set(month[iso]); modifiers.delete(modifier); - updatedDaysAfterDeletion = { - ...updatedDaysAfterDeletion, - [monthIso]: { - ...month, - [iso]: modifiers, - }, + updatedDaysAfterDeletion[monthIso] = { + ...month, + [iso]: modifiers, }; } diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 5ba65c493b..c4b1cd6dc9 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -535,14 +535,15 @@ export default class DayPickerSingleDateController extends React.PureComponent { updatedDaysAfterAddition = monthsToUpdate.reduce((acc, monthIso) => { const month = updatedDays[monthIso] || visibleDays[monthIso]; + const modifiers = new Set(month[iso]); modifiers.add(modifier); - return Object.assign(acc, { - [monthIso]: { - ...month, - [iso]: modifiers, - }, - }); + acc[monthIso] = { + ...month, + [iso]: modifiers, + }; + + return acc; }, updatedDaysAfterAddition); } else { const monthIso = toISOMonthString(day); @@ -550,12 +551,9 @@ export default class DayPickerSingleDateController extends React.PureComponent { const modifiers = new Set(month[iso]); modifiers.add(modifier); - updatedDaysAfterAddition = { - ...updatedDaysAfterAddition, - [monthIso]: { - ...month, - [iso]: modifiers, - }, + updatedDaysAfterAddition[monthIso] = { + ...month, + [iso]: modifiers, }; } @@ -590,12 +588,11 @@ export default class DayPickerSingleDateController extends React.PureComponent { const month = updatedDays[monthIso] || visibleDays[monthIso]; const modifiers = new Set(month[iso]); modifiers.delete(modifier); - return Object.assign(acc, { - [monthIso]: { - ...month, - [iso]: modifiers, - }, - }); + acc[monthIso] = { + ...month, + [iso]: modifiers, + }; + return acc; }, updatedDaysAfterDeletion); } else { const monthIso = toISOMonthString(day); @@ -603,12 +600,9 @@ export default class DayPickerSingleDateController extends React.PureComponent { const modifiers = new Set(month[iso]); modifiers.delete(modifier); - updatedDaysAfterDeletion = { - ...updatedDaysAfterDeletion, - [monthIso]: { - ...month, - [iso]: modifiers, - }, + updatedDaysAfterDeletion[monthIso] = { + ...month, + [iso]: modifiers, }; } From 2e1dd23457dd26830439a50f1079b40c51d83d60 Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Thu, 30 May 2019 11:32:02 -0700 Subject: [PATCH 446/618] Avoid creating new Sets when deleting/adding modifiers I noticed that we always create a new set and then delete from it even if the modifier is not in the set, or create a new set and then add it even it the modifier is already in the set. This optimization reduces the cost of DayPickerRangeController#componentWillReceiveProps from ~16ms to ~12ms. --- src/components/DayPickerRangeController.jsx | 58 +++++++++++-------- .../DayPickerSingleDateController.jsx | 58 +++++++++++-------- .../DayPickerRangeController_spec.jsx | 28 +++++---- .../DayPickerSingleDateController_spec.jsx | 36 +++++++----- 4 files changed, 106 insertions(+), 74 deletions(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 60b0263c06..2383ab0277 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -1009,12 +1009,14 @@ export default class DayPickerRangeController extends React.PureComponent { updatedDaysAfterAddition = monthsToUpdate.reduce((acc, monthIso) => { const month = updatedDays[monthIso] || visibleDays[monthIso]; - const modifiers = new Set(month[iso]); - modifiers.add(modifier); - acc[monthIso] = { - ...month, - [iso]: modifiers, - }; + if (!month[iso] || !month[iso].has(modifier)) { + const modifiers = new Set(month[iso]); + modifiers.add(modifier); + acc[monthIso] = { + ...month, + [iso]: modifiers, + }; + } return acc; }, updatedDaysAfterAddition); @@ -1022,12 +1024,14 @@ export default class DayPickerRangeController extends React.PureComponent { const monthIso = toISOMonthString(day); const month = updatedDays[monthIso] || visibleDays[monthIso]; - const modifiers = new Set(month[iso]); - modifiers.add(modifier); - updatedDaysAfterAddition[monthIso] = { - ...month, - [iso]: modifiers, - }; + if (!month[iso] || !month[iso].has(modifier)) { + const modifiers = new Set(month[iso]); + modifiers.add(modifier); + updatedDaysAfterAddition[monthIso] = { + ...month, + [iso]: modifiers, + }; + } } return updatedDaysAfterAddition; @@ -1070,24 +1074,30 @@ export default class DayPickerRangeController extends React.PureComponent { updatedDaysAfterDeletion = monthsToUpdate.reduce((acc, monthIso) => { const month = updatedDays[monthIso] || visibleDays[monthIso]; - const modifiers = new Set(month[iso]); - modifiers.delete(modifier); - acc[monthIso] = { - ...month, - [iso]: modifiers, - }; + + if (month[iso] && month[iso].has(modifier)) { + const modifiers = new Set(month[iso]); + modifiers.delete(modifier); + acc[monthIso] = { + ...month, + [iso]: modifiers, + }; + } + return acc; }, updatedDaysAfterDeletion); } else { const monthIso = toISOMonthString(day); const month = updatedDays[monthIso] || visibleDays[monthIso]; - const modifiers = new Set(month[iso]); - modifiers.delete(modifier); - updatedDaysAfterDeletion[monthIso] = { - ...month, - [iso]: modifiers, - }; + if (month[iso] && month[iso].has(modifier)) { + const modifiers = new Set(month[iso]); + modifiers.delete(modifier); + updatedDaysAfterDeletion[monthIso] = { + ...month, + [iso]: modifiers, + }; + } } return updatedDaysAfterDeletion; diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index c4b1cd6dc9..c6b9642706 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -536,12 +536,14 @@ export default class DayPickerSingleDateController extends React.PureComponent { updatedDaysAfterAddition = monthsToUpdate.reduce((acc, monthIso) => { const month = updatedDays[monthIso] || visibleDays[monthIso]; - const modifiers = new Set(month[iso]); - modifiers.add(modifier); - acc[monthIso] = { - ...month, - [iso]: modifiers, - }; + if (!month[iso] || !month[iso].has(modifier)) { + const modifiers = new Set(month[iso]); + modifiers.add(modifier); + acc[monthIso] = { + ...month, + [iso]: modifiers, + }; + } return acc; }, updatedDaysAfterAddition); @@ -549,12 +551,14 @@ export default class DayPickerSingleDateController extends React.PureComponent { const monthIso = toISOMonthString(day); const month = updatedDays[monthIso] || visibleDays[monthIso]; - const modifiers = new Set(month[iso]); - modifiers.add(modifier); - updatedDaysAfterAddition[monthIso] = { - ...month, - [iso]: modifiers, - }; + if (!month[iso] || !month[iso].has(modifier)) { + const modifiers = new Set(month[iso]); + modifiers.add(modifier); + updatedDaysAfterAddition[monthIso] = { + ...month, + [iso]: modifiers, + }; + } } return updatedDaysAfterAddition; @@ -586,24 +590,30 @@ export default class DayPickerSingleDateController extends React.PureComponent { updatedDaysAfterDeletion = monthsToUpdate.reduce((acc, monthIso) => { const month = updatedDays[monthIso] || visibleDays[monthIso]; - const modifiers = new Set(month[iso]); - modifiers.delete(modifier); - acc[monthIso] = { - ...month, - [iso]: modifiers, - }; + + if (month[iso] && month[iso].has(modifier)) { + const modifiers = new Set(month[iso]); + modifiers.delete(modifier); + acc[monthIso] = { + ...month, + [iso]: modifiers, + }; + } + return acc; }, updatedDaysAfterDeletion); } else { const monthIso = toISOMonthString(day); const month = updatedDays[monthIso] || visibleDays[monthIso]; - const modifiers = new Set(month[iso]); - modifiers.delete(modifier); - updatedDaysAfterDeletion[monthIso] = { - ...month, - [iso]: modifiers, - }; + if (month[iso] && month[iso].has(modifier)) { + const modifiers = new Set(month[iso]); + modifiers.delete(modifier); + updatedDaysAfterDeletion[monthIso] = { + ...month, + [iso]: modifiers, + }; + } } return updatedDaysAfterDeletion; diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index a62ef71fac..7cd803010d 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -891,9 +891,9 @@ describe('DayPickerRangeController', () => { const startOfMonth = today.clone().startOf('month'); visibleDays = { [toISOMonthString(startOfMonth)]: { - [toISODateString(startOfMonth)]: [], - [toISODateString(startOfMonth.clone().add(1, 'day'))]: [], - [toISODateString(startOfMonth.clone().add(2, 'days'))]: [], + [toISODateString(startOfMonth)]: new Set(), + [toISODateString(startOfMonth.clone().add(1, 'day'))]: new Set(), + [toISODateString(startOfMonth.clone().add(2, 'days'))]: new Set(), }, }; }); @@ -974,9 +974,9 @@ describe('DayPickerRangeController', () => { const startOfMonth = today.clone().startOf('month'); visibleDays = { [toISOMonthString(startOfMonth)]: { - [toISODateString(startOfMonth)]: [], - [toISODateString(startOfMonth.clone().add(1, 'day'))]: [], - [toISODateString(startOfMonth.clone().add(2, 'days'))]: [], + [toISODateString(startOfMonth)]: new Set(), + [toISODateString(startOfMonth.clone().add(1, 'day'))]: new Set(), + [toISODateString(startOfMonth.clone().add(2, 'days'))]: new Set(), }, }; }); @@ -1057,9 +1057,9 @@ describe('DayPickerRangeController', () => { const startOfMonth = today.clone().startOf('month'); visibleDays = { [toISOMonthString(startOfMonth)]: { - [toISODateString(startOfMonth)]: [], - [toISODateString(startOfMonth.clone().add(1, 'day'))]: [], - [toISODateString(startOfMonth.clone().add(2, 'days'))]: [], + [toISODateString(startOfMonth)]: new Set(), + [toISODateString(startOfMonth.clone().add(1, 'day'))]: new Set(), + [toISODateString(startOfMonth.clone().add(2, 'days'))]: new Set(), }, }; }); @@ -3798,8 +3798,14 @@ describe('DayPickerRangeController', () => { onFocusChange={sinon.stub()} /> )); - const modifiers = wrapper.instance().deleteModifier({}, today); - expect(Object.keys(modifiers)).to.contain(toISOMonthString(today)); + + const isoMonth = toISOMonthString(today); + const isoDate = toISODateString(today); + const modifiers = wrapper.instance() + .deleteModifier({ [isoMonth]: { [isoDate]: new Set(['foo']) } }, today, 'foo'); + + expect(Object.keys(modifiers)).to.contain(isoMonth); + expect(modifiers[isoMonth][isoDate].size).to.equal(0); }); it('has day ISO as key one layer down', () => { diff --git a/test/components/DayPickerSingleDateController_spec.jsx b/test/components/DayPickerSingleDateController_spec.jsx index e23e048aff..cf5ad01f71 100644 --- a/test/components/DayPickerSingleDateController_spec.jsx +++ b/test/components/DayPickerSingleDateController_spec.jsx @@ -111,9 +111,9 @@ describe('DayPickerSingleDateController', () => { const startOfMonth = today.clone().startOf('month'); visibleDays = { [toISOMonthString(startOfMonth)]: { - [toISODateString(startOfMonth)]: [], - [toISODateString(startOfMonth.clone().add(1, 'day'))]: [], - [toISODateString(startOfMonth.clone().add(2, 'days'))]: [], + [toISODateString(startOfMonth)]: new Set(), + [toISODateString(startOfMonth.clone().add(1, 'day'))]: new Set(), + [toISODateString(startOfMonth.clone().add(2, 'days'))]: new Set(), }, }; }); @@ -194,9 +194,9 @@ describe('DayPickerSingleDateController', () => { const startOfMonth = today.clone().startOf('month'); visibleDays = { [toISOMonthString(startOfMonth)]: { - [toISODateString(startOfMonth)]: [], - [toISODateString(startOfMonth.clone().add(1, 'day'))]: [], - [toISODateString(startOfMonth.clone().add(2, 'days'))]: [], + [toISODateString(startOfMonth)]: new Set(), + [toISODateString(startOfMonth.clone().add(1, 'day'))]: new Set(), + [toISODateString(startOfMonth.clone().add(2, 'days'))]: new Set(), }, }; }); @@ -289,9 +289,9 @@ describe('DayPickerSingleDateController', () => { const startOfMonth = today.clone().startOf('month'); visibleDays = { [toISOMonthString(startOfMonth)]: { - [toISODateString(startOfMonth)]: [], - [toISODateString(startOfMonth.clone().add(1, 'day'))]: [], - [toISODateString(startOfMonth.clone().add(2, 'days'))]: [], + [toISODateString(startOfMonth)]: new Set(), + [toISODateString(startOfMonth.clone().add(1, 'day'))]: new Set(), + [toISODateString(startOfMonth.clone().add(2, 'days'))]: new Set(), }, }; }); @@ -372,9 +372,9 @@ describe('DayPickerSingleDateController', () => { const startOfMonth = today.clone().startOf('month'); visibleDays = { [toISOMonthString(startOfMonth)]: { - [toISODateString(startOfMonth)]: [], - [toISODateString(startOfMonth.clone().add(1, 'day'))]: [], - [toISODateString(startOfMonth.clone().add(2, 'days'))]: [], + [toISODateString(startOfMonth)]: new Set(), + [toISODateString(startOfMonth.clone().add(1, 'day'))]: new Set(), + [toISODateString(startOfMonth.clone().add(2, 'days'))]: new Set(), }, }; }); @@ -1087,7 +1087,7 @@ describe('DayPickerSingleDateController', () => { onFocusChange={sinon.stub()} /> )); - const modifiers = wrapper.instance().addModifier({}, today); + const modifiers = wrapper.instance().addModifier({}, today, 'foo'); expect(Object.keys(modifiers)).to.contain(toISOMonthString(today)); }); @@ -1200,8 +1200,14 @@ describe('DayPickerSingleDateController', () => { onFocusChange={sinon.stub()} /> )); - const modifiers = wrapper.instance().deleteModifier({}, today); - expect(Object.keys(modifiers)).to.contain(toISOMonthString(today)); + + const isoMonth = toISOMonthString(today); + const isoDate = toISODateString(today); + const modifiers = wrapper.instance() + .deleteModifier({ [isoMonth]: { [isoDate]: new Set(['foo']) } }, today, 'foo'); + + expect(Object.keys(modifiers)).to.contain(isoMonth); + expect(modifiers[isoMonth][isoDate].size).to.equal(0); }); it('has day ISO as key one layer down', () => { From a60a76a14bddbba43033ad20b507137d18ab0248 Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Fri, 31 May 2019 09:22:16 -0700 Subject: [PATCH 447/618] Cache some moment instances when rebuilding modifiers When modifiers need to be rebuilt, which happens pretty frequently, we iterate over every day and create a new moment instance for these operations. These moment instances are never mutated, so we can safely store and reuse them to improve performance. This optimization reduces the time spent in DayPickerRangeController#componentWillReceiveProps when selecting dates from ~16ms to ~6-10ms. --- src/components/DayPickerRangeController.jsx | 3 ++- .../DayPickerSingleDateController.jsx | 3 ++- src/utils/getPooledMoment.js | 10 ++++++++ test/utils/getPooledMoment_spec.js | 24 +++++++++++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 src/utils/getPooledMoment.js create mode 100644 test/utils/getPooledMoment_spec.js diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 2383ab0277..83aaa21b9a 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -40,6 +40,7 @@ import { import DayPicker from './DayPicker'; import getPreviousMonthMemoLast from '../utils/getPreviousMonthMemoLast'; +import getPooledMoment from '../utils/getPooledMoment'; const propTypes = forbidExtraProps({ startDate: momentPropTypes.momentObj, @@ -394,7 +395,7 @@ export default class DayPickerRangeController extends React.PureComponent { if (didFocusChange || recomputePropModifiers) { values(visibleDays).forEach((days) => { Object.keys(days).forEach((day) => { - const momentObj = moment(day); + const momentObj = getPooledMoment(day); let isBlocked = false; if (didFocusChange || recomputeOutsideRange) { diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index c6b9642706..24649038fd 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -31,6 +31,7 @@ import { import DayPicker from './DayPicker'; import getPreviousMonthMemoLast from '../utils/getPreviousMonthMemoLast'; +import getPooledMoment from '../utils/getPooledMoment'; const propTypes = forbidExtraProps({ date: momentPropTypes.momentObj, @@ -268,7 +269,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { if (didFocusChange || recomputePropModifiers) { values(visibleDays).forEach((days) => { Object.keys(days).forEach((day) => { - const momentObj = moment(day); + const momentObj = getPooledMoment(day); if (this.isBlocked(momentObj)) { modifiers = this.addModifier(modifiers, momentObj, 'blocked'); } else { diff --git a/src/utils/getPooledMoment.js b/src/utils/getPooledMoment.js new file mode 100644 index 0000000000..38cd9fd25e --- /dev/null +++ b/src/utils/getPooledMoment.js @@ -0,0 +1,10 @@ +import moment from 'moment'; + +const momentPool = new Map(); +export default function getPooledMoment(dayString) { + if (!momentPool.has(dayString)) { + momentPool.set(dayString, moment(dayString)); + } + + return momentPool.get(dayString); +} diff --git a/test/utils/getPooledMoment_spec.js b/test/utils/getPooledMoment_spec.js new file mode 100644 index 0000000000..7c28318270 --- /dev/null +++ b/test/utils/getPooledMoment_spec.js @@ -0,0 +1,24 @@ +import { expect } from 'chai'; +import moment from 'moment'; + +import getPooledMoment from '../../src/utils/getPooledMoment'; + +describe('getPooledMoment', () => { + it('returns a moment given a day string', () => { + const momentObj = getPooledMoment('2017-12-10'); + expect(moment.isMoment(momentObj)).to.equal(true); + expect(momentObj.format('YYYY MM DD')).to.equal('2017 12 10'); + }); + + it('returns the same moment given the same day string', () => { + const momentObj1 = getPooledMoment('2017-12-10'); + const momentObj2 = getPooledMoment('2017-12-10'); + expect(momentObj1).to.equal(momentObj2); + }); + + it('returns a different moment given a different day string', () => { + const momentObj1 = getPooledMoment('2017-12-10'); + const momentObj2 = getPooledMoment('2017-12-11'); + expect(momentObj1).not.to.equal(momentObj2); + }); +}); From 8af003610ae746b296ec4d82d9930f34d2ef9f1f Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Mon, 3 Jun 2019 09:08:54 -0700 Subject: [PATCH 448/618] Version 20.2.1 --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c0f69ee4e..2e11692794 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## 20.2.1 +- [fix] Compile classes in loose mode ([#1655](https://github.com/airbnb/react-dates/pull/1655)) +- [fix] Performance optimizations ([#1656](https://github.com/airbnb/react-dates/pull/1656), [#1657](https://github.com/airbnb/react-dates/pull/1657), [#1659](https://github.com/airbnb/react-dates/pull/1659), [#1661](https://github.com/airbnb/react-dates/pull/1661), [#1662](https://github.com/airbnb/react-dates/pull/1662), and [#1663](https://github.com/airbnb/react-dates/pull/1663)) +- [docs] Add `initialVisibleMonth` to `DayPickerRangeController` in readme ([@AlokTakshak](https://github.com/AlokTakshak) [1652](https://github.com/airbnb/react-dates/pull/1652)) + ## 20.2.0 - [fix] Fix date selection in the SDP ([#1530](https://github.com/airbnb/react-dates/pull/1530)) - [new] Add explicit aria label props ([#1594](https://github.com/airbnb/react-dates/pull/1594)) diff --git a/package.json b/package.json index 71df533dcc..39fc451542 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "20.2.0", + "version": "20.2.1", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From ae3b1a68639d1d1df600b8eb469d2fb6f7f9e319 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 4 Jun 2019 00:08:30 -0700 Subject: [PATCH 449/618] [Dev Deps] update `@babel/core`, `@storybook/addon-actions`, `@storybook/addon-info`, `@storybook/addon-links`, `@storybook/addon-options`, `@storybook/react , `enzyme`, `enzyme-adapter-react-helper`, `eslint-plugin-import`, `karma` --- package.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 39fc451542..a590fd5ddf 100644 --- a/package.json +++ b/package.json @@ -52,13 +52,13 @@ "homepage": "https://github.com/airbnb/react-dates#readme", "devDependencies": { "@babel/cli": "^7.4.4", - "@babel/core": "^7.4.4", + "@babel/core": "^7.4.5", "@babel/register": "^7.4.4", - "@storybook/addon-actions": "^4.0.0", - "@storybook/addon-info": "^4.0.0", - "@storybook/addon-links": "^4.0.0", - "@storybook/addon-options": "^4.0.0", - "@storybook/react": "^4.0.0", + "@storybook/addon-actions": "^4.1.18", + "@storybook/addon-info": "^4.1.18", + "@storybook/addon-links": "^4.1.18", + "@storybook/addon-options": "^4.1.18", + "@storybook/react": "^4.1.18", "acorn": "^6.1.1", "airbnb-js-shims": "^2.2.0", "aphrodite": "^2.3.1", @@ -72,18 +72,18 @@ "clean-css": "^4.2.1", "coveralls": "^3.0.3", "cross-env": "^5.2.0", - "enzyme": "^3.9.0", - "enzyme-adapter-react-helper": "^1.3.4", + "enzyme": "^3.10.0", + "enzyme-adapter-react-helper": "^1.3.5", "eslint": "^5.16.0", "eslint-config-airbnb": "^17.1.0", - "eslint-plugin-import": "^2.17.2", + "eslint-plugin-import": "^2.17.3", "eslint-plugin-jsx-a11y": "^6.2.1", "eslint-plugin-react": "^7.13.0", "eslint-plugin-react-with-styles": "^2.1.0", "git-directory-deploy": "^1.5.1", "imports-loader": "^0.8.0", "in-publish": "^2.0.0", - "karma": "^3.1.1", + "karma": "^3.1.4", "karma-chai": "^0.1.0", "karma-firefox-launcher": "^1.1.0", "karma-mocha": "^1.3.0", From d6304cba0fb315ff21f5dd8a4d6405ba45b46db4 Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Tue, 4 Jun 2019 08:14:37 -0700 Subject: [PATCH 450/618] Add guard for undefined objects in addModifier After updating to v20.2.1, we started seeing a handful of errors about `undefined is not an object` coming from these addModifier calls. It isn't entirely clear to me how this happens, but I think we can prevent it from being a problem by adding some guards in these codepaths. --- src/components/DayPickerRangeController.jsx | 2 +- src/components/DayPickerSingleDateController.jsx | 2 +- test/components/DayPickerRangeController_spec.jsx | 12 ++++++++++++ .../DayPickerSingleDateController_spec.jsx | 12 ++++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 83aaa21b9a..f76464a0ae 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -1023,7 +1023,7 @@ export default class DayPickerRangeController extends React.PureComponent { }, updatedDaysAfterAddition); } else { const monthIso = toISOMonthString(day); - const month = updatedDays[monthIso] || visibleDays[monthIso]; + const month = updatedDays[monthIso] || visibleDays[monthIso] || {}; if (!month[iso] || !month[iso].has(modifier)) { const modifiers = new Set(month[iso]); diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 24649038fd..4ddfb102ca 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -550,7 +550,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { }, updatedDaysAfterAddition); } else { const monthIso = toISOMonthString(day); - const month = updatedDays[monthIso] || visibleDays[monthIso]; + const month = updatedDays[monthIso] || visibleDays[monthIso] || {}; if (!month[iso] || !month[iso].has(modifier)) { const modifiers = new Set(month[iso]); diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index 7cd803010d..0abd776cfb 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -3819,6 +3819,18 @@ describe('DayPickerRangeController', () => { expect(Object.keys(modifiers[toISOMonthString(today)])).to.contain(toISODateString(today)); }); + it('is resilient when visibleDays is an empty object', () => { + const wrapper = shallow(( + + )); + wrapper.instance().setState({ visibleDays: {} }); + const modifiers = wrapper.instance().addModifier({}, today); + expect(Object.keys(modifiers[toISOMonthString(today)])).to.contain(toISODateString(today)); + }); + it('return value no longer has modifier arg for day if was in first arg', () => { const modifierToDelete = 'foo'; const monthISO = toISOMonthString(today); diff --git a/test/components/DayPickerSingleDateController_spec.jsx b/test/components/DayPickerSingleDateController_spec.jsx index cf5ad01f71..d7016baec8 100644 --- a/test/components/DayPickerSingleDateController_spec.jsx +++ b/test/components/DayPickerSingleDateController_spec.jsx @@ -1102,6 +1102,18 @@ describe('DayPickerSingleDateController', () => { expect(Object.keys(modifiers[toISOMonthString(today)])).to.contain(toISODateString(today)); }); + it('is resilient when visibleDays is an empty object', () => { + const wrapper = shallow(( + + )); + wrapper.instance().setState({ visibleDays: {} }); + const modifiers = wrapper.instance().addModifier({}, today); + expect(Object.keys(modifiers[toISOMonthString(today)])).to.contain(toISODateString(today)); + }); + it('return value no longer has modifier arg for day if was in first arg', () => { const modifierToAdd = 'foo'; const monthISO = toISOMonthString(today); From 8b3f93932a54604fbd8815991a8091d4dc348ca4 Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Tue, 4 Jun 2019 09:19:54 -0700 Subject: [PATCH 451/618] Version 20.2.2 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e11692794..030504873a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## 20.2.2 +- [fix] Add guard for undefined objects in addModifier ([#1667](https://github.com/airbnb/react-dates/pull/1667)) + ## 20.2.1 - [fix] Compile classes in loose mode ([#1655](https://github.com/airbnb/react-dates/pull/1655)) - [fix] Performance optimizations ([#1656](https://github.com/airbnb/react-dates/pull/1656), [#1657](https://github.com/airbnb/react-dates/pull/1657), [#1659](https://github.com/airbnb/react-dates/pull/1659), [#1661](https://github.com/airbnb/react-dates/pull/1661), [#1662](https://github.com/airbnb/react-dates/pull/1662), and [#1663](https://github.com/airbnb/react-dates/pull/1663)) diff --git a/package.json b/package.json index a590fd5ddf..0793205c86 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "20.2.1", + "version": "20.2.2", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From b6295428e618b1eaf7184080fd81893ef912e50a Mon Sep 17 00:00:00 2001 From: Henric Trotzig Date: Wed, 5 Jun 2019 22:48:44 +0200 Subject: [PATCH 452/618] Update to Storybook v5 I was running into issues making a static Storybook build on v4 of Storybook (see https://github.com/airbnb/react-dates/pull/1382#issuecomment-498856007). The error I'm seeing is something like ERR! ./examples/DateRangePickerWrapper.jsx ERR! Module build failed (from ./node_modules/babel-loader/lib/index.js): ERR! TypeError: Property declarations[0] of VariableDeclaration expected node to be of a type ["VariableDeclarator"] but instead got "ExpressionStatement" ERR! at validate (/home/travis/build/airbnb/react-dates/node_modules/@babel/types/lib/definitions/utils.js:128:13) ERR! at validator (/home/travis/build/airbnb/react-dates/node_modules/@babel/types/lib/definitions/utils.js:97:7) ERR! at Object.validate (/home/travis/build/airbnb/react-dates/node_modules/@babel/types/lib/definitions/utils.js:172:7) ERR! at validate (/home/travis/build/airbnb/react-dates/node_modules/@babel/types/lib/validators/validate.js:17:9) ERR! at builder (/home/travis/build/airbnb/react-dates/node_modules/@babel/types/lib/builders/builder.js:46:27) ERR! at Object.VariableDeclaration (/home/travis/build/airbnb/react- It seems to happen at the minification step, because right before that error happens I'm seeing "92% chunk asset TerserPlugin". I started going down the route of tracing this down in Storybook v4, but then decided to give v5 a spin instead. It turns out the upgrade isn't all that complicated, and it does resolve the error I was seeing. The only change needed except for bumping all the @storybook dependencies was to turn the webpack config from "extend mode" to "full control mode". See https://storybook.js.org/docs/configurations/custom-webpack-config/ I used this guide as a starting point to the upgrade: https://medium.com/storybookjs/storybook-5-migration-guide-d804b38c739d --- .storybook/webpack.config.js | 17 +++++++---------- package.json | 10 +++++----- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js index 88fcdd2cf2..801ae4fee0 100644 --- a/.storybook/webpack.config.js +++ b/.storybook/webpack.config.js @@ -1,14 +1,12 @@ const path = require('path'); -module.exports = { - module: { - rules: [ +module.exports = ({ config }) => { + config.module.rules.push( + ...[ { test: /\.s?css$/, use: ['style-loader', 'raw-loader', 'sass-loader'], - include: [ - path.resolve(__dirname, '../css/'), - ], + include: [path.resolve(__dirname, '../css/')], }, { test: /\.svg$/, @@ -33,8 +31,7 @@ module.exports = { ], }, ], - }, - resolve: { - extensions: ['.js', '.jsx'], - }, + ); + config.resolve.extensions = ['.js', '.jsx']; + return config; }; diff --git a/package.json b/package.json index 0793205c86..e71121451d 100644 --- a/package.json +++ b/package.json @@ -54,11 +54,11 @@ "@babel/cli": "^7.4.4", "@babel/core": "^7.4.5", "@babel/register": "^7.4.4", - "@storybook/addon-actions": "^4.1.18", - "@storybook/addon-info": "^4.1.18", - "@storybook/addon-links": "^4.1.18", - "@storybook/addon-options": "^4.1.18", - "@storybook/react": "^4.1.18", + "@storybook/addon-actions": "^5.1.1", + "@storybook/addon-info": "^5.1.1", + "@storybook/addon-links": "^5.1.1", + "@storybook/addon-options": "^5.1.1", + "@storybook/react": "^5.1.1", "acorn": "^6.1.1", "airbnb-js-shims": "^2.2.0", "aphrodite": "^2.3.1", From fcdeeb463f4ce9645cbf1e6777a14bababf06d25 Mon Sep 17 00:00:00 2001 From: Henric Trotzig Date: Wed, 5 Jun 2019 23:06:37 +0200 Subject: [PATCH 453/618] Avoid unnecessary spread We can just push multiple objects into the array in one go. --- .storybook/webpack.config.js | 52 +++++++++++++++++------------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js index 801ae4fee0..0e6b53922b 100644 --- a/.storybook/webpack.config.js +++ b/.storybook/webpack.config.js @@ -2,35 +2,33 @@ const path = require('path'); module.exports = ({ config }) => { config.module.rules.push( - ...[ - { - test: /\.s?css$/, - use: ['style-loader', 'raw-loader', 'sass-loader'], - include: [path.resolve(__dirname, '../css/')], - }, - { - test: /\.svg$/, - use: [ - { - loader: 'babel-loader', - query: { - presets: ['airbnb'], - }, + { + test: /\.s?css$/, + use: ['style-loader', 'raw-loader', 'sass-loader'], + include: [path.resolve(__dirname, '../css/')], + }, + { + test: /\.svg$/, + use: [ + { + loader: 'babel-loader', + query: { + presets: ['airbnb'], }, - ], - }, - { - test: /\.jsx$/, - use: [ - { - loader: 'babel-loader', - query: { - presets: ['airbnb'], - }, + }, + ], + }, + { + test: /\.jsx$/, + use: [ + { + loader: 'babel-loader', + query: { + presets: ['airbnb'], }, - ], - }, - ], + }, + ], + }, ); config.resolve.extensions = ['.js', '.jsx']; return config; From 25df22f16c753b517e33a09865226832f3d7293d Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Fri, 14 Jun 2019 14:40:07 -0700 Subject: [PATCH 454/618] Add guard for undefined objects in deleteModifier I fixed this for addModifier in d6304cb, but failed to address deleteModifier. --- src/components/DayPickerRangeController.jsx | 2 +- src/components/DayPickerSingleDateController.jsx | 2 +- test/components/DayPickerRangeController_spec.jsx | 15 +++++++++++++-- .../DayPickerSingleDateController_spec.jsx | 11 +++++++++++ 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index f76464a0ae..866446c20c 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -1089,7 +1089,7 @@ export default class DayPickerRangeController extends React.PureComponent { }, updatedDaysAfterDeletion); } else { const monthIso = toISOMonthString(day); - const month = updatedDays[monthIso] || visibleDays[monthIso]; + const month = updatedDays[monthIso] || visibleDays[monthIso] || {}; if (month[iso] && month[iso].has(modifier)) { const modifiers = new Set(month[iso]); diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 4ddfb102ca..c0f939ca6f 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -605,7 +605,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { }, updatedDaysAfterDeletion); } else { const monthIso = toISOMonthString(day); - const month = updatedDays[monthIso] || visibleDays[monthIso]; + const month = updatedDays[monthIso] || visibleDays[monthIso] || {}; if (month[iso] && month[iso].has(modifier)) { const modifiers = new Set(month[iso]); diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index 0abd776cfb..c6c0435d2a 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -3615,6 +3615,18 @@ describe('DayPickerRangeController', () => { expect(Object.keys(modifiers)).to.contain(toISOMonthString(today)); }); + it('is resilient when visibleDays is an empty object', () => { + const wrapper = shallow(( + + )); + wrapper.instance().setState({ visibleDays: {} }); + const modifiers = wrapper.instance().addModifier({}, today); + expect(Object.keys(modifiers[toISOMonthString(today)])).to.contain(toISODateString(today)); + }); + it('has day ISO as key one layer down', () => { const wrapper = shallow(( { /> )); wrapper.instance().setState({ visibleDays: {} }); - const modifiers = wrapper.instance().addModifier({}, today); - expect(Object.keys(modifiers[toISOMonthString(today)])).to.contain(toISODateString(today)); + expect(() => { wrapper.instance().deleteModifier({}, today); }).to.not.throw(); }); it('return value no longer has modifier arg for day if was in first arg', () => { diff --git a/test/components/DayPickerSingleDateController_spec.jsx b/test/components/DayPickerSingleDateController_spec.jsx index d7016baec8..6d756cccb0 100644 --- a/test/components/DayPickerSingleDateController_spec.jsx +++ b/test/components/DayPickerSingleDateController_spec.jsx @@ -1233,6 +1233,17 @@ describe('DayPickerSingleDateController', () => { expect(Object.keys(modifiers[toISOMonthString(today)])).to.contain(toISODateString(today)); }); + it('is resilient when visibleDays is an empty object', () => { + const wrapper = shallow(( + + )); + wrapper.instance().setState({ visibleDays: {} }); + expect(() => { wrapper.instance().deleteModifier({}, today); }).to.not.throw(); + }); + it('return value no longer has modifier arg for day if was in first arg', () => { const modifierToDelete = 'foo'; const monthISO = toISOMonthString(today); From 02645b73ec0ec7c41a70acaf21b72b80382d8c4c Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Fri, 14 Jun 2019 15:55:52 -0700 Subject: [PATCH 455/618] Version 20.2.3 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 030504873a..63bbb63486 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## 20.2.3 +- [fix] Add guard for undefined objects in deleteModifier ([#1687](https://github.com/airbnb/react-dates/pull/1687)) +- [dev] Update Storybook from v4 to v5 ([@trotzig](https://github.com/trotzig) [#1673](https://github.com/airbnb/react-dates/pull/1673)) + ## 20.2.2 - [fix] Add guard for undefined objects in addModifier ([#1667](https://github.com/airbnb/react-dates/pull/1667)) diff --git a/package.json b/package.json index e71121451d..6dfc7ee3ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "20.2.2", + "version": "20.2.3", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From bf7e3347702fadebec3ea3dc2ce9d273d5fea6ff Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 16 Jun 2019 09:50:16 -0700 Subject: [PATCH 456/618] [Refactor] use `enzyme-shallow-equal` instead of `react-addons-shallow-compare` Closes #1671. --- package.json | 2 +- scripts/pure-component-fallback.js | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 6dfc7ee3ff..37774d2ec8 100644 --- a/package.json +++ b/package.json @@ -112,12 +112,12 @@ "dependencies": { "airbnb-prop-types": "^2.10.0", "consolidated-events": "^1.1.1 || ^2.0.0", + "enzyme-shallow-equal": "^1.0.0", "is-touch-device": "^1.0.1", "lodash": "^4.1.1", "object.assign": "^4.1.0", "object.values": "^1.0.4", "prop-types": "^15.6.1", - "react-addons-shallow-compare": "^15.6.2", "react-moment-proptypes": "^1.6.0", "react-outside-click-handler": "^1.2.0", "react-portal": "^4.1.5", diff --git a/scripts/pure-component-fallback.js b/scripts/pure-component-fallback.js index 21de3715a6..8cf60d5420 100644 --- a/scripts/pure-component-fallback.js +++ b/scripts/pure-component-fallback.js @@ -12,7 +12,7 @@ module.exports = function pureComponentFallback({ types: t, template }) { t.stringLiteral('shouldComponentUpdate') ), [t.identifier('nextProps'), t.identifier('nextState')], - t.blockStatement([template('return shallowCompare(this, nextProps, nextState);')()]), + t.blockStatement([template('return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState);')()]), true ); return method; @@ -38,11 +38,11 @@ module.exports = function pureComponentFallback({ types: t, template }) { visitor: { Program: { exit({ node }, { file }) { - if (file.get('addShallowCompareImport')) { - const shallowCompareImportDeclaration = t.importDeclaration([ - t.importDefaultSpecifier(t.identifier('shallowCompare')), - ], t.stringLiteral('react-addons-shallow-compare')); - node.body.unshift(shallowCompareImportDeclaration); + if (file.get('addShallowEqualImport')) { + const shallowEqualImportDeclaration = t.importDeclaration([ + t.importDefaultSpecifier(t.identifier('shallowEqual')), + ], t.stringLiteral('enzyme-shallow-equal')); + node.body.unshift(shallowEqualImportDeclaration); } }, }, @@ -60,7 +60,7 @@ module.exports = function pureComponentFallback({ types: t, template }) { )); if (!existingSCU) { - file.set('addShallowCompareImport', true); + file.set('addShallowEqualImport', true); path.get('body').unshiftContainer('body', buildShouldComponentUpdate()); } } From 182fd56feb566537fcd17a01dc885f8093e1d133 Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Mon, 17 Jun 2019 10:21:19 -0700 Subject: [PATCH 457/618] Run SVGs through SVGO with most aggressive settings I noticed that these SVGs could be optimized a little more, by reducing the precision. The end result is slightly different, but at the size they are used, I don't think it will really be perceptible. I left close.svg alone because it has a 12x12 viewBox instead of a 1000x1000 viewbox, so the precision here is actually pretty important for the shape. I think some more bytes could maybe be squeezed out of these by recreating them from hand using different shapes instead of paths, but that's a bit more work than I'd like to do at this time. This optimization reduces the size of the svg directory from 2678 bytes to 2106 bytes. --- src/svg/arrow-left.svg | 2 +- src/svg/arrow-right.svg | 2 +- src/svg/calendar.svg | 2 +- src/svg/chevron-down.svg | 2 +- src/svg/chevron-up.svg | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/svg/arrow-left.svg b/src/svg/arrow-left.svg index 96cdee9bfd..e183dcb709 100644 --- a/src/svg/arrow-left.svg +++ b/src/svg/arrow-left.svg @@ -1 +1 @@ - + diff --git a/src/svg/arrow-right.svg b/src/svg/arrow-right.svg index 765ffda559..ad9d30934d 100644 --- a/src/svg/arrow-right.svg +++ b/src/svg/arrow-right.svg @@ -1 +1 @@ - + diff --git a/src/svg/calendar.svg b/src/svg/calendar.svg index 35c4d101d5..d26c6e7bff 100644 --- a/src/svg/calendar.svg +++ b/src/svg/calendar.svg @@ -1 +1 @@ - + diff --git a/src/svg/chevron-down.svg b/src/svg/chevron-down.svg index 4c864e2201..dd9918fddd 100644 --- a/src/svg/chevron-down.svg +++ b/src/svg/chevron-down.svg @@ -1 +1 @@ - + diff --git a/src/svg/chevron-up.svg b/src/svg/chevron-up.svg index ad1c1a6e82..a9c2948bee 100644 --- a/src/svg/chevron-up.svg +++ b/src/svg/chevron-up.svg @@ -1 +1 @@ - + From 32de4305e9cb42418602c1b1e76d5c298bc64cc3 Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Mon, 17 Jun 2019 10:39:05 -0700 Subject: [PATCH 458/618] Run calendar.svg through svgcleaner I found that I can squeeze out a few extra bytes in this SVG by running it through svgcleaner. --- src/svg/calendar.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/svg/calendar.svg b/src/svg/calendar.svg index d26c6e7bff..b07cb0a6ef 100644 --- a/src/svg/calendar.svg +++ b/src/svg/calendar.svg @@ -1 +1 @@ - + From 6a6ee4b5b894478e0b9c191fcb0e493a297e62e1 Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Tue, 18 Jun 2019 11:53:23 -0700 Subject: [PATCH 459/618] =?UTF-8?q?Update=20babel-preset-airbnb=203.2.1=20?= =?UTF-8?q?=E2=86=92=204.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This version adds `@babel/plugin-transform-runtime` to help us reduce bundle size. This should have the same effect as https://github.com/airbnb/react-dates/pull/1689 --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 37774d2ec8..151934abd0 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "babel-plugin-inline-svg": "^1.0.0", "babel-plugin-istanbul": "^5.1.4", "babel-plugin-transform-replace-object-assign": "^2.0.0", - "babel-preset-airbnb": "^3.2.1", + "babel-preset-airbnb": "^4.0.0", "chai": "^4.2.0", "clean-css": "^4.2.1", "coveralls": "^3.0.3", @@ -110,6 +110,7 @@ "why-did-you-update": "^1.0.6" }, "dependencies": { + "@babel/runtime": "^7.4.5", "airbnb-prop-types": "^2.10.0", "consolidated-events": "^1.1.1 || ^2.0.0", "enzyme-shallow-equal": "^1.0.0", From 259feb967f4f848e2d5a82e0e909261e765f34f3 Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Tue, 18 Jun 2019 14:55:56 -0700 Subject: [PATCH 460/618] Version 20.2.4 --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63bbb63486..d7e6010da5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## 20.2.4 +- [fix] Replace react-addons-shallow-compare with enzyme-shallow-equal (bf7e3347702f) +- [fix] Optimize SVG assets ([#1690](https://github.com/airbnb/react-dates/pull/1690)) +- [fix] Update babel-preset-airbnb 3.2.1 -> 4.0.0 ([#1692](https://github.com/airbnb/react-dates/pull/1692)) + ## 20.2.3 - [fix] Add guard for undefined objects in deleteModifier ([#1687](https://github.com/airbnb/react-dates/pull/1687)) - [dev] Update Storybook from v4 to v5 ([@trotzig](https://github.com/trotzig) [#1673](https://github.com/airbnb/react-dates/pull/1673)) diff --git a/package.json b/package.json index 151934abd0..4d25d8fe44 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "20.2.3", + "version": "20.2.4", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From c6debdc233cce15f733266a8384798ad6dd8114c Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Tue, 18 Jun 2019 16:41:59 -0700 Subject: [PATCH 461/618] Extract addModifier and deleteModifier to shared module I noticed that these two beefy methods were identical in both of these components, so I'm moving them out to shared code to reduce duplication. --- src/components/DayPickerRangeController.jsx | 108 +--------------- .../DayPickerSingleDateController.jsx | 110 +---------------- src/utils/modifiers.js | 116 ++++++++++++++++++ 3 files changed, 122 insertions(+), 212 deletions(-) create mode 100644 src/utils/modifiers.js diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 866446c20c..b4db0fd3e8 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -21,7 +21,7 @@ import isDayVisible from '../utils/isDayVisible'; import getSelectedDateOffset from '../utils/getSelectedDateOffset'; import toISODateString from '../utils/toISODateString'; -import toISOMonthString from '../utils/toISOMonthString'; +import { addModifier, deleteModifier } from '../utils/modifiers'; import DisabledShape from '../shapes/DisabledShape'; import FocusedInputShape from '../shapes/FocusedInputShape'; @@ -39,7 +39,6 @@ import { } from '../constants'; import DayPicker from './DayPicker'; -import getPreviousMonthMemoLast from '../utils/getPreviousMonthMemoLast'; import getPooledMoment from '../utils/getPooledMoment'; const propTypes = forbidExtraProps({ @@ -984,58 +983,7 @@ export default class DayPickerRangeController extends React.PureComponent { } addModifier(updatedDays, day, modifier) { - const { numberOfMonths: numberOfVisibleMonths, enableOutsideDays, orientation } = this.props; - const { currentMonth: firstVisibleMonth, visibleDays } = this.state; - - let currentMonth = firstVisibleMonth; - let numberOfMonths = numberOfVisibleMonths; - if (orientation === VERTICAL_SCROLLABLE) { - numberOfMonths = Object.keys(visibleDays).length; - } else { - currentMonth = getPreviousMonthMemoLast(currentMonth); - numberOfMonths += 2; - } - if (!day || !isDayVisible(day, currentMonth, numberOfMonths, enableOutsideDays)) { - return updatedDays; - } - - const iso = toISODateString(day); - - let updatedDaysAfterAddition = { ...updatedDays }; - if (enableOutsideDays) { - const monthsToUpdate = Object.keys(visibleDays).filter(monthKey => ( - Object.keys(visibleDays[monthKey]).indexOf(iso) > -1 - )); - - updatedDaysAfterAddition = monthsToUpdate.reduce((acc, monthIso) => { - const month = updatedDays[monthIso] || visibleDays[monthIso]; - - if (!month[iso] || !month[iso].has(modifier)) { - const modifiers = new Set(month[iso]); - modifiers.add(modifier); - acc[monthIso] = { - ...month, - [iso]: modifiers, - }; - } - - return acc; - }, updatedDaysAfterAddition); - } else { - const monthIso = toISOMonthString(day); - const month = updatedDays[monthIso] || visibleDays[monthIso] || {}; - - if (!month[iso] || !month[iso].has(modifier)) { - const modifiers = new Set(month[iso]); - modifiers.add(modifier); - updatedDaysAfterAddition[monthIso] = { - ...month, - [iso]: modifiers, - }; - } - } - - return updatedDaysAfterAddition; + return addModifier(updatedDays, day, modifier, this.props, this.state); } addModifierToRange(updatedDays, start, end, modifier) { @@ -1051,57 +999,7 @@ export default class DayPickerRangeController extends React.PureComponent { } deleteModifier(updatedDays, day, modifier) { - const { numberOfMonths: numberOfVisibleMonths, enableOutsideDays, orientation } = this.props; - const { currentMonth: firstVisibleMonth, visibleDays } = this.state; - let currentMonth = firstVisibleMonth; - let numberOfMonths = numberOfVisibleMonths; - if (orientation === VERTICAL_SCROLLABLE) { - numberOfMonths = Object.keys(visibleDays).length; - } else { - currentMonth = getPreviousMonthMemoLast(currentMonth); - numberOfMonths += 2; - } - if (!day || !isDayVisible(day, currentMonth, numberOfMonths, enableOutsideDays)) { - return updatedDays; - } - - const iso = toISODateString(day); - - let updatedDaysAfterDeletion = { ...updatedDays }; - if (enableOutsideDays) { - const monthsToUpdate = Object.keys(visibleDays).filter(monthKey => ( - Object.keys(visibleDays[monthKey]).indexOf(iso) > -1 - )); - - updatedDaysAfterDeletion = monthsToUpdate.reduce((acc, monthIso) => { - const month = updatedDays[monthIso] || visibleDays[monthIso]; - - if (month[iso] && month[iso].has(modifier)) { - const modifiers = new Set(month[iso]); - modifiers.delete(modifier); - acc[monthIso] = { - ...month, - [iso]: modifiers, - }; - } - - return acc; - }, updatedDaysAfterDeletion); - } else { - const monthIso = toISOMonthString(day); - const month = updatedDays[monthIso] || visibleDays[monthIso] || {}; - - if (month[iso] && month[iso].has(modifier)) { - const modifiers = new Set(month[iso]); - modifiers.delete(modifier); - updatedDaysAfterDeletion[monthIso] = { - ...month, - [iso]: modifiers, - }; - } - } - - return updatedDaysAfterDeletion; + return deleteModifier(updatedDays, day, modifier, this.props, this.state); } deleteModifierFromRange(updatedDays, start, end, modifier) { diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index c0f939ca6f..6884fef2fe 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -13,10 +13,9 @@ import isSameDay from '../utils/isSameDay'; import isAfterDay from '../utils/isAfterDay'; import getVisibleDays from '../utils/getVisibleDays'; -import isDayVisible from '../utils/isDayVisible'; import toISODateString from '../utils/toISODateString'; -import toISOMonthString from '../utils/toISOMonthString'; +import { addModifier, deleteModifier } from '../utils/modifiers'; import ScrollableOrientationShape from '../shapes/ScrollableOrientationShape'; import DayOfWeekShape from '../shapes/DayOfWeekShape'; @@ -30,7 +29,6 @@ import { } from '../constants'; import DayPicker from './DayPicker'; -import getPreviousMonthMemoLast from '../utils/getPreviousMonthMemoLast'; import getPooledMoment from '../utils/getPooledMoment'; const propTypes = forbidExtraProps({ @@ -511,113 +509,11 @@ export default class DayPickerSingleDateController extends React.PureComponent { } addModifier(updatedDays, day, modifier) { - const { numberOfMonths: numberOfVisibleMonths, enableOutsideDays, orientation } = this.props; - const { currentMonth: firstVisibleMonth, visibleDays } = this.state; - - let currentMonth = firstVisibleMonth; - let numberOfMonths = numberOfVisibleMonths; - if (orientation === VERTICAL_SCROLLABLE) { - numberOfMonths = Object.keys(visibleDays).length; - } else { - currentMonth = getPreviousMonthMemoLast(currentMonth); - numberOfMonths += 2; - } - if (!day || !isDayVisible(day, currentMonth, numberOfMonths, enableOutsideDays)) { - return updatedDays; - } - - const iso = toISODateString(day); - - let updatedDaysAfterAddition = { ...updatedDays }; - if (enableOutsideDays) { - const monthsToUpdate = Object.keys(visibleDays).filter(monthKey => ( - Object.keys(visibleDays[monthKey]).indexOf(iso) > -1 - )); - - updatedDaysAfterAddition = monthsToUpdate.reduce((acc, monthIso) => { - const month = updatedDays[monthIso] || visibleDays[monthIso]; - - if (!month[iso] || !month[iso].has(modifier)) { - const modifiers = new Set(month[iso]); - modifiers.add(modifier); - acc[monthIso] = { - ...month, - [iso]: modifiers, - }; - } - - return acc; - }, updatedDaysAfterAddition); - } else { - const monthIso = toISOMonthString(day); - const month = updatedDays[monthIso] || visibleDays[monthIso] || {}; - - if (!month[iso] || !month[iso].has(modifier)) { - const modifiers = new Set(month[iso]); - modifiers.add(modifier); - updatedDaysAfterAddition[monthIso] = { - ...month, - [iso]: modifiers, - }; - } - } - - return updatedDaysAfterAddition; + return addModifier(updatedDays, day, modifier, this.props, this.state); } deleteModifier(updatedDays, day, modifier) { - const { numberOfMonths: numberOfVisibleMonths, enableOutsideDays, orientation } = this.props; - const { currentMonth: firstVisibleMonth, visibleDays } = this.state; - - let currentMonth = firstVisibleMonth; - let numberOfMonths = numberOfVisibleMonths; - if (orientation === VERTICAL_SCROLLABLE) { - numberOfMonths = Object.keys(visibleDays).length; - } else { - currentMonth = getPreviousMonthMemoLast(currentMonth); - numberOfMonths += 2; - } - if (!day || !isDayVisible(day, currentMonth, numberOfMonths, enableOutsideDays)) { - return updatedDays; - } - - const iso = toISODateString(day); - - let updatedDaysAfterDeletion = { ...updatedDays }; - if (enableOutsideDays) { - const monthsToUpdate = Object.keys(visibleDays).filter(monthKey => ( - Object.keys(visibleDays[monthKey]).indexOf(iso) > -1 - )); - - updatedDaysAfterDeletion = monthsToUpdate.reduce((acc, monthIso) => { - const month = updatedDays[monthIso] || visibleDays[monthIso]; - - if (month[iso] && month[iso].has(modifier)) { - const modifiers = new Set(month[iso]); - modifiers.delete(modifier); - acc[monthIso] = { - ...month, - [iso]: modifiers, - }; - } - - return acc; - }, updatedDaysAfterDeletion); - } else { - const monthIso = toISOMonthString(day); - const month = updatedDays[monthIso] || visibleDays[monthIso] || {}; - - if (month[iso] && month[iso].has(modifier)) { - const modifiers = new Set(month[iso]); - modifiers.delete(modifier); - updatedDaysAfterDeletion[monthIso] = { - ...month, - [iso]: modifiers, - }; - } - } - - return updatedDaysAfterDeletion; + return deleteModifier(updatedDays, day, modifier, this.props, this.state); } isBlocked(day) { diff --git a/src/utils/modifiers.js b/src/utils/modifiers.js new file mode 100644 index 0000000000..5f1dbb13fd --- /dev/null +++ b/src/utils/modifiers.js @@ -0,0 +1,116 @@ +import isDayVisible from './isDayVisible'; +import toISODateString from './toISODateString'; +import toISOMonthString from './toISOMonthString'; +import getPreviousMonthMemoLast from './getPreviousMonthMemoLast'; + +import { VERTICAL_SCROLLABLE } from '../constants'; + +export function addModifier(updatedDays, day, modifier, props, state) { + const { numberOfMonths: numberOfVisibleMonths, enableOutsideDays, orientation } = props; + const { currentMonth: firstVisibleMonth, visibleDays } = state; + + let currentMonth = firstVisibleMonth; + let numberOfMonths = numberOfVisibleMonths; + if (orientation === VERTICAL_SCROLLABLE) { + numberOfMonths = Object.keys(visibleDays).length; + } else { + currentMonth = getPreviousMonthMemoLast(currentMonth); + numberOfMonths += 2; + } + if (!day || !isDayVisible(day, currentMonth, numberOfMonths, enableOutsideDays)) { + return updatedDays; + } + + const iso = toISODateString(day); + + let updatedDaysAfterAddition = { ...updatedDays }; + if (enableOutsideDays) { + const monthsToUpdate = Object.keys(visibleDays).filter(monthKey => ( + Object.keys(visibleDays[monthKey]).indexOf(iso) > -1 + )); + + updatedDaysAfterAddition = monthsToUpdate.reduce((acc, monthIso) => { + const month = updatedDays[monthIso] || visibleDays[monthIso]; + + if (!month[iso] || !month[iso].has(modifier)) { + const modifiers = new Set(month[iso]); + modifiers.add(modifier); + acc[monthIso] = { + ...month, + [iso]: modifiers, + }; + } + + return acc; + }, updatedDaysAfterAddition); + } else { + const monthIso = toISOMonthString(day); + const month = updatedDays[monthIso] || visibleDays[monthIso] || {}; + + if (!month[iso] || !month[iso].has(modifier)) { + const modifiers = new Set(month[iso]); + modifiers.add(modifier); + updatedDaysAfterAddition[monthIso] = { + ...month, + [iso]: modifiers, + }; + } + } + + return updatedDaysAfterAddition; +} + +export function deleteModifier(updatedDays, day, modifier, props, state) { + const { numberOfMonths: numberOfVisibleMonths, enableOutsideDays, orientation } = props; + const { currentMonth: firstVisibleMonth, visibleDays } = state; + + let currentMonth = firstVisibleMonth; + let numberOfMonths = numberOfVisibleMonths; + if (orientation === VERTICAL_SCROLLABLE) { + numberOfMonths = Object.keys(visibleDays).length; + } else { + currentMonth = getPreviousMonthMemoLast(currentMonth); + numberOfMonths += 2; + } + if (!day || !isDayVisible(day, currentMonth, numberOfMonths, enableOutsideDays)) { + return updatedDays; + } + + const iso = toISODateString(day); + + let updatedDaysAfterDeletion = { ...updatedDays }; + if (enableOutsideDays) { + const monthsToUpdate = Object.keys(visibleDays).filter(monthKey => ( + Object.keys(visibleDays[monthKey]).indexOf(iso) > -1 + )); + + updatedDaysAfterDeletion = monthsToUpdate.reduce((acc, monthIso) => { + const month = updatedDays[monthIso] || visibleDays[monthIso]; + + if (month[iso] && month[iso].has(modifier)) { + const modifiers = new Set(month[iso]); + modifiers.delete(modifier); + acc[monthIso] = { + ...month, + [iso]: modifiers, + }; + } + + return acc; + }, updatedDaysAfterDeletion); + } else { + const monthIso = toISOMonthString(day); + const month = updatedDays[monthIso] || visibleDays[monthIso] || {}; + + if (month[iso] && month[iso].has(modifier)) { + const modifiers = new Set(month[iso]); + modifiers.delete(modifier); + updatedDaysAfterDeletion[monthIso] = { + ...month, + [iso]: modifiers, + }; + } + } + + return updatedDaysAfterDeletion; +} From 1babba998e4fe43aadeadcac4a917bc5ac1b2c5d Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Tue, 25 Jun 2019 15:27:24 -0700 Subject: [PATCH 462/618] Defer day focusing until next animation frame At Airbnb we noticed a bug affecting only mobile devices or desktop with mobile device emulation enabled. In months that span 5 weeks that are followed by months that span 6 weeks (e.g. May 2019), the scroll position of the calendar days was scrolled down some, causing the numbers to awkwardly overlap with the days of the week and look broken. This only happened when first opening the date picker, and once you go to a different month it would fix itself. After some sleuthing, I determined that this was caused by these .focus() calls happening at a weird time, causing a parent container (I think maybe the DayPicker transition container with overflow hidden) to have a > 0 scrollTop. It seems that we can prevent this from happening by deferring the focus until the next animation frame. I think this could also help us avoid some layout thrashing, which could give us a little performance boost here. I was entirely unable to reproduce this in react-dates storybook, but testing this fix out in the Airbnb codebase seems to fix the issue. --- src/components/CalendarDay.jsx | 6 +++++- src/components/CustomizableCalendarDay.jsx | 6 +++++- test/components/CalendarDay_spec.jsx | 18 ++++++++++++++++++ .../CustomizableCalendarDay_spec.jsx | 18 ++++++++++++++++++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index 7256cd0710..f69f12fc2d 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -58,7 +58,11 @@ class CalendarDay extends React.PureComponent { const { isFocused, tabIndex } = this.props; if (tabIndex === 0) { if (isFocused || tabIndex !== prevProps.tabIndex) { - this.buttonRef.focus(); + requestAnimationFrame(() => { + if (this.buttonRef) { + this.buttonRef.focus(); + } + }); } } } diff --git a/src/components/CustomizableCalendarDay.jsx b/src/components/CustomizableCalendarDay.jsx index 46c4bef564..b37590b74b 100644 --- a/src/components/CustomizableCalendarDay.jsx +++ b/src/components/CustomizableCalendarDay.jsx @@ -228,7 +228,11 @@ class CustomizableCalendarDay extends React.PureComponent { const { isFocused, tabIndex } = this.props; if (tabIndex === 0) { if (isFocused || tabIndex !== prevProps.tabIndex) { - this.buttonRef.focus(); + requestAnimationFrame(() => { + if (this.buttonRef) { + this.buttonRef.focus(); + } + }); } } } diff --git a/test/components/CalendarDay_spec.jsx b/test/components/CalendarDay_spec.jsx index 4394e02655..6640c4e048 100644 --- a/test/components/CalendarDay_spec.jsx +++ b/test/components/CalendarDay_spec.jsx @@ -238,6 +238,24 @@ describe('CalendarDay', () => { }); }); + describe('#componentDidUpdate', () => { + it('focuses buttonRef after a delay when isFocused, tabIndex is 0, and tabIndex was not 0', () => { + const clock = sinon.useFakeTimers(); + const originalRAF = global.requestAnimationFrame; + global.requestAnimationFrame = global.setTimeout; + + const wrapper = shallow().dive(); + const focus = sinon.spy(); + wrapper.instance().buttonRef = { focus }; + wrapper.instance().componentDidUpdate({ isFocused: true, tabIndex: -1 }); + expect(focus.callCount).to.eq(0); + clock.tick(16); + expect(focus.callCount).to.eq(1); + clock.restore(); + global.requestAnimationFrame = originalRAF; + }); + }); + describe('#onDayClick', () => { let onDayClickSpy; beforeEach(() => { diff --git a/test/components/CustomizableCalendarDay_spec.jsx b/test/components/CustomizableCalendarDay_spec.jsx index b75c4c759b..6910a7154d 100644 --- a/test/components/CustomizableCalendarDay_spec.jsx +++ b/test/components/CustomizableCalendarDay_spec.jsx @@ -189,6 +189,24 @@ describe('CustomizableCalendarDay', () => { }); }); + describe('#componentDidUpdate', () => { + it('focuses buttonRef after a delay when isFocused, tabIndex is 0, and tabIndex was not 0', () => { + const clock = sinon.useFakeTimers(); + const originalRAF = global.requestAnimationFrame; + global.requestAnimationFrame = global.setTimeout; + + const wrapper = shallow().dive(); + const focus = sinon.spy(); + wrapper.instance().buttonRef = { focus }; + wrapper.instance().componentDidUpdate({ isFocused: true, tabIndex: -1 }); + expect(focus.callCount).to.eq(0); + clock.tick(16); + expect(focus.callCount).to.eq(1); + clock.restore(); + global.requestAnimationFrame = originalRAF; + }); + }); + describe('#onKeyDown', () => { const day = moment('10/10/2017'); From 731b98877747e878fb87b00bdcee3331054ca33f Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Tue, 25 Jun 2019 16:35:26 -0700 Subject: [PATCH 463/618] Use raf instead of requestAnimationFrame This helps maintain browser compatibility. --- package.json | 1 + src/components/CalendarDay.jsx | 3 ++- src/components/CustomizableCalendarDay.jsx | 3 ++- .../_helpers/registerReactWithStylesInterface.js | 9 +++++++++ test/components/CalendarDay_spec.jsx | 16 ++++++++-------- test/components/CustomizableCalendarDay_spec.jsx | 16 ++++++++-------- .../DayPickerKeyboardShortcuts_spec.jsx | 2 +- 7 files changed, 31 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 4d25d8fe44..ab04b10cd6 100644 --- a/package.json +++ b/package.json @@ -119,6 +119,7 @@ "object.assign": "^4.1.0", "object.values": "^1.0.4", "prop-types": "^15.6.1", + "raf": "^3.4.1", "react-moment-proptypes": "^1.6.0", "react-outside-click-handler": "^1.2.0", "react-portal": "^4.1.5", diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index f69f12fc2d..7e630093bf 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -4,6 +4,7 @@ import momentPropTypes from 'react-moment-proptypes'; import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types'; import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; import moment from 'moment'; +import raf from 'raf'; import { CalendarDayPhrases } from '../defaultPhrases'; import getPhrasePropTypes from '../utils/getPhrasePropTypes'; @@ -58,7 +59,7 @@ class CalendarDay extends React.PureComponent { const { isFocused, tabIndex } = this.props; if (tabIndex === 0) { if (isFocused || tabIndex !== prevProps.tabIndex) { - requestAnimationFrame(() => { + raf(() => { if (this.buttonRef) { this.buttonRef.focus(); } diff --git a/src/components/CustomizableCalendarDay.jsx b/src/components/CustomizableCalendarDay.jsx index b37590b74b..2a3ee15752 100644 --- a/src/components/CustomizableCalendarDay.jsx +++ b/src/components/CustomizableCalendarDay.jsx @@ -4,6 +4,7 @@ import momentPropTypes from 'react-moment-proptypes'; import { forbidExtraProps, nonNegativeInteger, or } from 'airbnb-prop-types'; import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; import moment from 'moment'; +import raf from 'raf'; import { CalendarDayPhrases } from '../defaultPhrases'; import getPhrasePropTypes from '../utils/getPhrasePropTypes'; @@ -228,7 +229,7 @@ class CustomizableCalendarDay extends React.PureComponent { const { isFocused, tabIndex } = this.props; if (tabIndex === 0) { if (isFocused || tabIndex !== prevProps.tabIndex) { - requestAnimationFrame(() => { + raf(() => { if (this.buttonRef) { this.buttonRef.focus(); } diff --git a/test/_helpers/registerReactWithStylesInterface.js b/test/_helpers/registerReactWithStylesInterface.js index 5678bdb108..f0dc45750b 100644 --- a/test/_helpers/registerReactWithStylesInterface.js +++ b/test/_helpers/registerReactWithStylesInterface.js @@ -1,7 +1,16 @@ import ThemedStyleSheet from 'react-with-styles/lib/ThemedStyleSheet'; import aphroditeInterface from 'react-with-styles-interface-aphrodite'; +import { StyleSheetTestUtils } from 'aphrodite'; import DefaultTheme from '../../src/theme/DefaultTheme'; ThemedStyleSheet.registerTheme(DefaultTheme); ThemedStyleSheet.registerInterface(aphroditeInterface); + +beforeEach(() => { + StyleSheetTestUtils.suppressStyleInjection(); +}); + +afterEach(() => { + StyleSheetTestUtils.clearBufferAndResumeStyleInjection(); +}); diff --git a/test/components/CalendarDay_spec.jsx b/test/components/CalendarDay_spec.jsx index 6640c4e048..c0a6281cef 100644 --- a/test/components/CalendarDay_spec.jsx +++ b/test/components/CalendarDay_spec.jsx @@ -3,6 +3,7 @@ import { expect } from 'chai'; import sinon from 'sinon-sandbox'; import { shallow } from 'enzyme'; import moment from 'moment'; +import raf from 'raf'; import { BLOCKED_MODIFIER } from '../../src/constants'; import CalendarDay, { PureCalendarDay } from '../../src/components/CalendarDay'; @@ -240,19 +241,18 @@ describe('CalendarDay', () => { describe('#componentDidUpdate', () => { it('focuses buttonRef after a delay when isFocused, tabIndex is 0, and tabIndex was not 0', () => { - const clock = sinon.useFakeTimers(); - const originalRAF = global.requestAnimationFrame; - global.requestAnimationFrame = global.setTimeout; - const wrapper = shallow().dive(); const focus = sinon.spy(); wrapper.instance().buttonRef = { focus }; wrapper.instance().componentDidUpdate({ isFocused: true, tabIndex: -1 }); expect(focus.callCount).to.eq(0); - clock.tick(16); - expect(focus.callCount).to.eq(1); - clock.restore(); - global.requestAnimationFrame = originalRAF; + + return new Promise((resolve) => { + raf(() => { + expect(focus.callCount).to.eq(1); + resolve(); + }); + }); }); }); diff --git a/test/components/CustomizableCalendarDay_spec.jsx b/test/components/CustomizableCalendarDay_spec.jsx index 6910a7154d..a138b482e4 100644 --- a/test/components/CustomizableCalendarDay_spec.jsx +++ b/test/components/CustomizableCalendarDay_spec.jsx @@ -3,6 +3,7 @@ import { expect } from 'chai'; import sinon from 'sinon-sandbox'; import { shallow } from 'enzyme'; import moment from 'moment'; +import raf from 'raf'; import { BLOCKED_MODIFIER } from '../../src/constants'; import CustomizableCalendarDay, { PureCustomizableCalendarDay } from '../../src/components/CustomizableCalendarDay'; @@ -191,19 +192,18 @@ describe('CustomizableCalendarDay', () => { describe('#componentDidUpdate', () => { it('focuses buttonRef after a delay when isFocused, tabIndex is 0, and tabIndex was not 0', () => { - const clock = sinon.useFakeTimers(); - const originalRAF = global.requestAnimationFrame; - global.requestAnimationFrame = global.setTimeout; - const wrapper = shallow().dive(); const focus = sinon.spy(); wrapper.instance().buttonRef = { focus }; wrapper.instance().componentDidUpdate({ isFocused: true, tabIndex: -1 }); expect(focus.callCount).to.eq(0); - clock.tick(16); - expect(focus.callCount).to.eq(1); - clock.restore(); - global.requestAnimationFrame = originalRAF; + + return new Promise((resolve) => { + raf(() => { + expect(focus.callCount).to.eq(1); + resolve(); + }); + }); }); }); diff --git a/test/components/DayPickerKeyboardShortcuts_spec.jsx b/test/components/DayPickerKeyboardShortcuts_spec.jsx index bc4c786ace..5321a32fcf 100644 --- a/test/components/DayPickerKeyboardShortcuts_spec.jsx +++ b/test/components/DayPickerKeyboardShortcuts_spec.jsx @@ -54,7 +54,7 @@ describe('DayPickerKeyboardShortcuts', () => { const openKeyboardShortcutsPanelStub = sinon.stub(); const showButtonFocusStub = sinon.stub(); - before(() => { + beforeEach(() => { const wrapper = shallow().dive(); From bf28ba4db6e15f919498fe2b6ae1be07361fe319 Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Wed, 26 Jun 2019 10:17:34 -0700 Subject: [PATCH 464/618] =?UTF-8?q?Update=20eslint=20v5=20=E2=86=92=20v6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ab04b10cd6..8970d7badc 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "cross-env": "^5.2.0", "enzyme": "^3.10.0", "enzyme-adapter-react-helper": "^1.3.5", - "eslint": "^5.16.0", + "eslint": "^6.0.1", "eslint-config-airbnb": "^17.1.0", "eslint-plugin-import": "^2.17.3", "eslint-plugin-jsx-a11y": "^6.2.1", From 9862d2898b2baa727a5d68ed0c0e63e66749405e Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Mon, 3 Jun 2019 09:14:57 -0700 Subject: [PATCH 465/618] npmignore .cache, .github, .lgtm, and _gh-pages I don't think there is any value in distributing these files in the package. --- .npmignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.npmignore b/.npmignore index bb95d36efb..f88e53c229 100644 --- a/.npmignore +++ b/.npmignore @@ -32,13 +32,17 @@ node_modules # Optional REPL history .node_repl_history +.cache +.github +.lgtm .storybook .storybook-css +_gh-pages examples +MAINTAINERS public react-dates-demo.gif stories test css !lib/css - From 3b284c9619d7dde46d6adeb53f564c15b8ae5012 Mon Sep 17 00:00:00 2001 From: Ian Duvall Date: Tue, 26 Mar 2019 11:43:13 -0500 Subject: [PATCH 466/618] Update blocked navigation (min/maxDate) stories --- stories/DayPickerRangeController.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index 8ff8f15c2f..50aaec35f1 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -293,8 +293,8 @@ storiesOf('DayPickerRangeController', module) ))) .add('with custom month navigation and blocked navigation (minDate and maxDate)', withInfo()(() => ( ( Date: Thu, 7 Mar 2019 15:09:56 +1100 Subject: [PATCH 467/618] Add positioning to custom navigation in stories --- stories/DateRangePicker_calendar.js | 8 ++++---- stories/DayPicker.js | 6 ++++-- stories/DayPickerRangeController.js | 6 ++++++ stories/DayPickerSingleDateController.js | 6 ++++++ stories/SingleDatePicker_calendar.js | 8 +++++++- 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/stories/DateRangePicker_calendar.js b/stories/DateRangePicker_calendar.js index c465937f70..23d250a5fc 100644 --- a/stories/DateRangePicker_calendar.js +++ b/stories/DateRangePicker_calendar.js @@ -19,8 +19,8 @@ function CustomMonthNav({ children, style }) { padding: '0 3px', position: 'absolute', marginTop: -2, - top: 30, - left: 26, + top: 19, + left: 13, outline: 'inherit', ...style, }} @@ -131,7 +131,7 @@ storiesOf('DRP - Calendar Props', module) .add('with custom month navigation', withInfo()(() => ( ‹} - navNext={} + navNext={} numberOfMonths={1} autoFocus /> @@ -140,7 +140,7 @@ storiesOf('DRP - Calendar Props', module) ‹} - navNext={} + navNext={} autoFocus /> ))) diff --git a/stories/DayPicker.js b/stories/DayPicker.js index 11dde2f34c..c0c53b1ab0 100644 --- a/stories/DayPicker.js +++ b/stories/DayPicker.js @@ -15,9 +15,10 @@ const TestPrevIcon = () => ( border: '1px solid #dce0e0', backgroundColor: '#fff', color: '#484848', - left: '24px', + left: '22px', padding: '3px', position: 'absolute', + top: '20px', width: '40px', }} tabindex="0" @@ -34,7 +35,8 @@ const TestNextIcon = () => ( color: '#484848', padding: '3px', position: 'absolute', - right: '24px', + right: '22px', + top: '20px', width: '40px', }} tabindex="0" diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index 50aaec35f1..cb1b984e64 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -36,7 +36,10 @@ const TestPrevIcon = () => ( border: '1px solid #dce0e0', backgroundColor: '#fff', color: '#484848', + left: '22px', padding: '3px', + position: 'absolute', + top: '20px', }} > Prev @@ -50,6 +53,9 @@ const TestNextIcon = () => ( backgroundColor: '#fff', color: '#484848', padding: '3px', + position: 'absolute', + right: '22px', + top: '20px', }} > Next diff --git a/stories/DayPickerSingleDateController.js b/stories/DayPickerSingleDateController.js index 2c7e948e2e..a77bd72b61 100644 --- a/stories/DayPickerSingleDateController.js +++ b/stories/DayPickerSingleDateController.js @@ -37,7 +37,10 @@ const TestPrevIcon = () => ( border: '1px solid #dce0e0', backgroundColor: '#fff', color: '#484848', + left: '22px', padding: '3px', + position: 'absolute', + top: '20px', }} > Prev @@ -51,6 +54,9 @@ const TestNextIcon = () => ( backgroundColor: '#fff', color: '#484848', padding: '3px', + position: 'absolute', + right: '22px', + top: '20px', }} > Next diff --git a/stories/SingleDatePicker_calendar.js b/stories/SingleDatePicker_calendar.js index 9e6d6aac47..cd85951872 100644 --- a/stories/SingleDatePicker_calendar.js +++ b/stories/SingleDatePicker_calendar.js @@ -13,7 +13,10 @@ const TestPrevIcon = () => ( border: '1px solid #dce0e0', backgroundColor: '#fff', color: '#484848', + left: '22px', padding: '3px', + position: 'absolute', + top: '20px', }} > Prev @@ -27,6 +30,9 @@ const TestNextIcon = () => ( backgroundColor: '#fff', color: '#484848', padding: '3px', + position: 'absolute', + right: '22px', + top: '20px', }} > Next @@ -149,7 +155,7 @@ storiesOf('SDP - Calendar Props', module) .add('with info panel default', withInfo()(() => ( ( - + )} autoFocus /> From 310205c04af8d2a1e66ab28c800389655f8fa596 Mon Sep 17 00:00:00 2001 From: Doug MacKenzie Date: Fri, 8 Mar 2019 10:46:16 +1100 Subject: [PATCH 468/618] Add tabindex=0 to other custom nav examples --- stories/DateRangePicker_calendar.js | 6 +++--- stories/DayPickerRangeController.js | 10 ++++++---- stories/DayPickerSingleDateController.js | 10 ++++++---- stories/SingleDatePicker_calendar.js | 10 ++++++---- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/stories/DateRangePicker_calendar.js b/stories/DateRangePicker_calendar.js index 23d250a5fc..478937e873 100644 --- a/stories/DateRangePicker_calendar.js +++ b/stories/DateRangePicker_calendar.js @@ -9,7 +9,7 @@ import DateRangePickerWrapper from '../examples/DateRangePickerWrapper'; function CustomMonthNav({ children, style }) { return ( - {children} - +
    ); } diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index cb1b984e64..fa189f7ef4 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -31,7 +31,7 @@ const dayPickerRangeControllerInfo = `The ${monospace('DayPickerRangeController' implement your own inputs.`; const TestPrevIcon = () => ( - ( position: 'absolute', top: '20px', }} + tabindex="0" > Prev - +
); const TestNextIcon = () => ( - ( right: '22px', top: '20px', }} + tabindex="0" > Next - +
); const TestCustomInfoPanel = () => ( diff --git a/stories/DayPickerSingleDateController.js b/stories/DayPickerSingleDateController.js index a77bd72b61..f6375aa9bc 100644 --- a/stories/DayPickerSingleDateController.js +++ b/stories/DayPickerSingleDateController.js @@ -32,7 +32,7 @@ const dayPickerSingleDateControllerInfo = `The ${monospace('DayPickerSingleDateC implement your own input.`; const TestPrevIcon = () => ( - ( position: 'absolute', top: '20px', }} + tabindex="0" > Prev - +
); const TestNextIcon = () => ( - ( right: '22px', top: '20px', }} + tabindex="0" > Next - +
); const TestCustomInfoPanel = () => ( diff --git a/stories/SingleDatePicker_calendar.js b/stories/SingleDatePicker_calendar.js index cd85951872..13db3464c5 100644 --- a/stories/SingleDatePicker_calendar.js +++ b/stories/SingleDatePicker_calendar.js @@ -8,7 +8,7 @@ import SingleDatePickerWrapper from '../examples/SingleDatePickerWrapper'; import { VERTICAL_ORIENTATION, ANCHOR_RIGHT, OPEN_UP } from '../src/constants'; const TestPrevIcon = () => ( - ( position: 'absolute', top: '20px', }} + tabindex="0" > Prev - +
); const TestNextIcon = () => ( - ( right: '22px', top: '20px', }} + tabindex="0" > Next - +
); const TestCustomInfoPanel = () => ( From bc476e945fe287b6ce264b49029cc7416026c38f Mon Sep 17 00:00:00 2001 From: alok Date: Sat, 8 Jun 2019 11:29:37 +0530 Subject: [PATCH 469/618] fixed wrong props to SDP onClose --- src/components/SingleDatePicker.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index 850e63af46..94c513c01e 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -188,8 +188,7 @@ class SingleDatePicker extends React.PureComponent { focused, onFocusChange, onClose, - startDate, - endDate, + date, appendToBody, } = this.props; @@ -203,7 +202,7 @@ class SingleDatePicker extends React.PureComponent { }); onFocusChange({ focused: false }); - onClose({ startDate, endDate }); + onClose({ date}); } onInputFocus({ focused }) { From 2370d4cf9523f9c61dc752fb2b395dc64da0b024 Mon Sep 17 00:00:00 2001 From: alok Date: Sat, 8 Jun 2019 11:51:14 +0530 Subject: [PATCH 470/618] resolved linting on props of onClose --- src/components/SingleDatePicker.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index 94c513c01e..c07a2a8092 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -202,7 +202,7 @@ class SingleDatePicker extends React.PureComponent { }); onFocusChange({ focused: false }); - onClose({ date}); + onClose({ date }); } onInputFocus({ focused }) { From 754ac5d477afab66ad41279767d5f1e93fab9f60 Mon Sep 17 00:00:00 2001 From: alok Date: Sat, 8 Jun 2019 14:19:26 +0530 Subject: [PATCH 471/618] test case for onClose of SDP --- test/components/SingleDatePicker_spec.jsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/components/SingleDatePicker_spec.jsx b/test/components/SingleDatePicker_spec.jsx index 6d63f862d9..41352ef74a 100644 --- a/test/components/SingleDatePicker_spec.jsx +++ b/test/components/SingleDatePicker_spec.jsx @@ -460,6 +460,23 @@ describe('SingleDatePicker', () => { wrapper.instance().onOutsideClick(); expect(onFocusChangeStub.getCall(0).args[0].focused).to.equal(false); }); + + it('calls props.onClose with { date: "08-06-2019" } as arg', () => { + const onFocusChangeStub = sinon.stub(); + const onCloseStub = sinon.stub(); + const wrapper = shallow(( + {}} + onFocusChange={onFocusChangeStub} + focused + date="08-06-2019" + /> + )).dive(); + wrapper.instance().onOutsideClick(); + expect(onCloseStub.getCall(0).args[0].date).to.equal("08-06-2019"); + }); }); }); From 3290bb4e9d9c741e283adcc7d74fb4875bc827ca Mon Sep 17 00:00:00 2001 From: alok Date: Sat, 8 Jun 2019 14:45:55 +0530 Subject: [PATCH 472/618] resolved string linting in test case --- test/components/SingleDatePicker_spec.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/components/SingleDatePicker_spec.jsx b/test/components/SingleDatePicker_spec.jsx index 41352ef74a..3b05e5e421 100644 --- a/test/components/SingleDatePicker_spec.jsx +++ b/test/components/SingleDatePicker_spec.jsx @@ -475,7 +475,7 @@ describe('SingleDatePicker', () => { /> )).dive(); wrapper.instance().onOutsideClick(); - expect(onCloseStub.getCall(0).args[0].date).to.equal("08-06-2019"); + expect(onCloseStub.getCall(0).args[0].date).to.equal('08-06-2019'); }); }); }); From 9cfd74e5070404823bcb2ac0c50101c917183cab Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Wed, 26 Jun 2019 11:50:02 -0700 Subject: [PATCH 473/618] =?UTF-8?q?Update=20karma=203=20=E2=86=92=204,=20m?= =?UTF-8?q?ocha=203=20=E2=86=92=206,=20nyc=2012=20=E2=86=92=2014,=20sinon?= =?UTF-8?q?=206=20=E2=86=92=207?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 10 +++++----- test/mocha.opts | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 8970d7badc..1cfe5e66d2 100644 --- a/package.json +++ b/package.json @@ -83,18 +83,18 @@ "git-directory-deploy": "^1.5.1", "imports-loader": "^0.8.0", "in-publish": "^2.0.0", - "karma": "^3.1.4", + "karma": "^4.1.0", "karma-chai": "^0.1.0", "karma-firefox-launcher": "^1.1.0", "karma-mocha": "^1.3.0", "karma-sinon": "^1.0.5", - "karma-webpack": "^4.0.0-rc.2", - "mocha": "^3.5.3", + "karma-webpack": "^4.0.2", + "mocha": "^6.1.4", "mocha-wrap": "^2.1.2", "moment": "^2.24.0", "moment-jalaali": "^0.7.4", "node-sass": "^4.12.0", - "nyc": "^12.0.2", + "nyc": "^14.1.1", "raw-loader": "^0.5.1", "react": "^0.14 || ^15.5.4 || ^16.1.1", "react-dom": "^0.14 || ^15.5.4 || ^16.1.1", @@ -103,7 +103,7 @@ "rimraf": "^2.6.3", "safe-publish-latest": "^1.1.2", "sass-loader": "^7.1.0", - "sinon": "^6.3.5", + "sinon": "^7.3.2", "sinon-sandbox": "^2.0.0", "style-loader": "^0.20.3", "webpack": "^4.31.0", diff --git a/test/mocha.opts b/test/mocha.opts index 670077bcd8..b2d2150ca3 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,4 +1,5 @@ +--extension js +--extension jsx --require @babel/register ---compilers js:@babel/register,jsx:@babel/register --require airbnb-js-shims --recursive From 13ee10730d6c0522891e47c206fe9aa1d60a5714 Mon Sep 17 00:00:00 2001 From: mapkeji Date: Wed, 26 Jun 2019 11:31:58 +0300 Subject: [PATCH 474/618] Fix-Start-Date-Style --- src/components/DayPickerRangeController.jsx | 2 +- .../DayPickerRangeController_spec.jsx | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index b4db0fd3e8..169f05b85d 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -1085,7 +1085,7 @@ export default class DayPickerRangeController extends React.PureComponent { isInSelectedSpan(day) { const { startDate, endDate } = this.props; - return day.isBetween(startDate, endDate); + return day.isBetween(startDate, endDate, 'days'); } isLastInRange(day) { diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index c6c0435d2a..5e0506bd3a 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -4249,6 +4249,26 @@ describe('DayPickerRangeController', () => { expect(wrapper.instance().isInSelectedSpan(testDate)).to.equal(true); }); + it('returns false if arg = props.startDate && arg < 12', () => { + const endDate = moment(today).add(5, 'days'); + const wrapper = shallow(); + const testDate = moment(today.hours(10)); + expect(wrapper.instance().isInSelectedSpan(testDate)).to.equal(false); + }); + + it('returns false if arg = props.startDate && arg > 12', () => { + const endDate = moment(today).add(5, 'days'); + const wrapper = shallow(); + const testDate = moment(today.hours(16)); + expect(wrapper.instance().isInSelectedSpan(testDate)).to.equal(false); + }); + it('returns false if arg < props.startDate', () => { const endDate = moment(today).add(5, 'days'); const wrapper = shallow( Date: Wed, 26 Jun 2019 12:31:10 -0700 Subject: [PATCH 475/618] Version 20.2.5 --- CHANGELOG.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7e6010da5..9add23ac75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## 20.2.5 +- [fix] Defer day focusing until next animation frame ([#1707](https://github.com/airbnb/react-dates/pull/1707)) +- [fix] Fix startDate style (@mmarkelov, [#1710](https://github.com/airbnb/react-dates/pull/1710)) +- [fix] Pass correct props to SingleDatePicker on close (@AlokTakshak, [#1678](https://github.com/airbnb/react-dates/pull/1678)) +- [dev] Update blocked navigation (min/maxDate) stories (@ianduvall, [#1598](https://github.com/airbnb/react-dates/pull/1598)) +- [dev] Add positioning to custom navigation in stories (@dougmacknz, [#1573](https://github.com/airbnb/react-dates/pull/1573)) +- [dev] Update karma 3 → 4, mocha 3 → 6, nyc 12 → 14, sinon 6 → 7, eslint 5 → 6 ([#1713](https://github.com/airbnb/react-dates/pull/1713), [#1712](https://github.com/airbnb/react-dates/pull/1713)) + ## 20.2.4 - [fix] Replace react-addons-shallow-compare with enzyme-shallow-equal (bf7e3347702f) - [fix] Optimize SVG assets ([#1690](https://github.com/airbnb/react-dates/pull/1690)) diff --git a/package.json b/package.json index 1cfe5e66d2..6f1527a29e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "20.2.4", + "version": "20.2.5", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From f19a6e74201df8c55a45ad570f4f7094456e93f2 Mon Sep 17 00:00:00 2001 From: Minh Nguyen Date: Thu, 25 Jul 2019 12:45:29 +0100 Subject: [PATCH 476/618] Bump the dependency on react-with-styles to ^3.2.3 This closes #1198, closes #1493. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6f1527a29e..b44cd62133 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,7 @@ "react-outside-click-handler": "^1.2.0", "react-portal": "^4.1.5", "react-with-direction": "^1.3.0", - "react-with-styles": "^3.2.0", + "react-with-styles": "^3.2.3", "react-with-styles-interface-css": "^4.0.2" }, "peerDependencies": { From fd1595d18036b83212d7a61f5ea2d54c01a88848 Mon Sep 17 00:00:00 2001 From: mmarkelov Date: Sat, 27 Jul 2019 19:34:03 +0300 Subject: [PATCH 477/618] Optimize setState dayPickerContainerStyles in responsivizePickerPosition function --- src/components/DateRangePicker.jsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index fa9868cc5a..bdfb3274e4 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -318,7 +318,11 @@ class DateRangePicker extends React.PureComponent { responsivizePickerPosition() { // It's possible the portal props have been changed in response to window resizes // So let's ensure we reset this back to the base state each time - this.setState({ dayPickerContainerStyles: {} }); + const { dayPickerContainerStyles } = this.state; + + if (Object.keys(dayPickerContainerStyles).length > 0) { + this.setState({ dayPickerContainerStyles: {} }); + } if (!this.isOpened()) { return; @@ -332,7 +336,6 @@ class DateRangePicker extends React.PureComponent { withFullScreenPortal, appendToBody, } = this.props; - const { dayPickerContainerStyles } = this.state; const isAnchoredLeft = anchorDirection === ANCHOR_LEFT; if (!withPortal && !withFullScreenPortal) { From 74447a744cafe5c4e0b0255936119465104b4339 Mon Sep 17 00:00:00 2001 From: edraitsev Date: Sat, 27 Jul 2019 21:17:37 +0200 Subject: [PATCH 478/618] fixup: Added tests for screenReaderMessage --- .../components/SingleDatePickerInput_spec.jsx | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/components/SingleDatePickerInput_spec.jsx b/test/components/SingleDatePickerInput_spec.jsx index 7d4935c4ee..3914360c43 100644 --- a/test/components/SingleDatePickerInput_spec.jsx +++ b/test/components/SingleDatePickerInput_spec.jsx @@ -4,6 +4,8 @@ import { shallow } from 'enzyme'; import sinon from 'sinon-sandbox'; import SingleDatePickerInput from '../../src/components/SingleDatePickerInput'; +import DateInput from '../../src/components/DateInput'; +import { SingleDatePickerInputPhrases } from '../../src/defaultPhrases'; describe('SingleDatePickerInput', () => { describe('render', () => { @@ -95,4 +97,33 @@ describe('SingleDatePickerInput', () => { }); }); }); + + describe('screen reader message', () => { + describe('props.screenReaderMessage is falsy', () => { + it('default value is passed to DateInput', () => { + const wrapper = shallow().dive(); + const dateInput = wrapper.find(DateInput); + expect(dateInput).to.have.lengthOf(1); + expect(dateInput.props()).to.have.property( + 'screenReaderMessage', + SingleDatePickerInputPhrases.keyboardForwardNavigationInstructions, + ); + }); + }); + + describe('props.screenReaderMessage is truthy', () => { + it('prop value is passed to DateInput', () => { + const message = 'test message'; + const wrapper = shallow(( + + )).dive(); + const dateInput = wrapper.find(DateInput); + expect(dateInput).to.have.lengthOf(1); + expect(dateInput.props()).to.have.property('screenReaderMessage', message); + }); + }); + }); }); From 9440e286b7e34a61bf4eb08595c701e3acbc3616 Mon Sep 17 00:00:00 2001 From: Rodolphe Courtier Date: Thu, 8 Aug 2019 11:43:58 -0700 Subject: [PATCH 479/618] [a11y] Add `aria-roledescription` As per the recommendation of the W3C, adding an `aria-roledescription` to describe the role of the datepicker application. Since this is supposed to be a human-readable and localized description of the element it's passed in through the existing phrases prop, and a default value was included. --- src/components/DayPicker.jsx | 1 + src/defaultPhrases.js | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index 8501e2863e..c954226a76 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -1027,6 +1027,7 @@ class DayPicker extends React.PureComponent { return (
`Selected as end date. ${date}`; export default { calendarLabel, + roleDescription, closeDatePicker, focusStartDate, clearDate, @@ -71,6 +73,7 @@ export default { export const DateRangePickerPhrases = { calendarLabel, + roleDescription, closeDatePicker, clearDates, focusStartDate, @@ -112,6 +115,7 @@ export const DateRangePickerInputPhrases = { export const SingleDatePickerPhrases = { calendarLabel, + roleDescription, closeDatePicker, clearDate, jumpToPrevMonth, @@ -148,6 +152,7 @@ export const SingleDatePickerInputPhrases = { export const DayPickerPhrases = { calendarLabel, + roleDescription, jumpToPrevMonth, jumpToNextMonth, keyboardShortcuts, From 8c0ba9886a9f8410f65e65153e5a02e8c987ba08 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Thu, 15 Aug 2019 12:36:34 -0700 Subject: [PATCH 480/618] Version 20.3.0 --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9add23ac75..8d443c6a37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## 20.3.0 +- [fix] Optimize setState dayPickerContainerStyles in responsivizePickerPosition ([#1735](https://github.com/airbnb/react-dates/pull/1735)) +- [fix] Stop calendar blinking on DateRangePickerInput focus switch (fixes #1523) ([#1553](https://github.com/airbnb/react-dates/pull/1553)) +- [new] [a11y] Add `aria-roledescription` ([#1746](https://github.com/airbnb/react-dates/pull/1746)) + ## 20.2.5 - [fix] Defer day focusing until next animation frame ([#1707](https://github.com/airbnb/react-dates/pull/1707)) - [fix] Fix startDate style (@mmarkelov, [#1710](https://github.com/airbnb/react-dates/pull/1710)) diff --git a/package.json b/package.json index b44cd62133..0410862c50 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "20.2.5", + "version": "20.3.0", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From 8a6d2207c0007649a7d91bfd605a2379af3285b5 Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Thu, 15 Aug 2019 16:56:08 -0700 Subject: [PATCH 481/618] Remove all direct imports of css in favor of injected prop --- examples/PresetDateRangePicker.jsx | 4 ++-- src/components/CalendarDay.jsx | 3 ++- src/components/CalendarMonth.jsx | 3 ++- src/components/CalendarMonthGrid.jsx | 3 ++- src/components/CustomizableCalendarDay.jsx | 3 ++- src/components/DateInput.jsx | 3 ++- src/components/DateRangePicker.jsx | 4 +++- src/components/DateRangePickerInput.jsx | 3 ++- src/components/DayPicker.jsx | 4 +++- src/components/DayPickerKeyboardShortcuts.jsx | 3 ++- src/components/DayPickerNavigation.jsx | 3 ++- src/components/KeyboardShortcutRow.jsx | 3 ++- src/components/SingleDatePicker.jsx | 4 +++- src/components/SingleDatePickerInput.jsx | 3 ++- 14 files changed, 31 insertions(+), 15 deletions(-) diff --git a/examples/PresetDateRangePicker.jsx b/examples/PresetDateRangePicker.jsx index ce8640a79b..c02ea3e123 100644 --- a/examples/PresetDateRangePicker.jsx +++ b/examples/PresetDateRangePicker.jsx @@ -4,7 +4,7 @@ import momentPropTypes from 'react-moment-proptypes'; import moment from 'moment'; import omit from 'lodash/omit'; -import { withStyles, withStylesPropTypes, css } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import DateRangePicker from '../src/components/DateRangePicker'; @@ -123,7 +123,7 @@ class DateRangePickerWrapper extends React.Component { } renderDatePresets() { - const { presets, styles } = this.props; + const { presets, styles, css } = this.props; const { startDate, endDate } = this.state; return ( diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index 7e630093bf..836a26e0cf 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import momentPropTypes from 'react-moment-proptypes'; import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import moment from 'moment'; import raf from 'raf'; @@ -107,6 +107,7 @@ class CalendarDay extends React.PureComponent { tabIndex, styles, phrases, + css, } = this.props; if (!day) return ; diff --git a/src/components/CalendarMonth.jsx b/src/components/CalendarMonth.jsx index 76e540a1c7..508ff8947e 100644 --- a/src/components/CalendarMonth.jsx +++ b/src/components/CalendarMonth.jsx @@ -4,7 +4,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import momentPropTypes from 'react-moment-proptypes'; import { forbidExtraProps, mutuallyExclusiveProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import moment from 'moment'; import { CalendarDayPhrases } from '../defaultPhrases'; @@ -152,6 +152,7 @@ class CalendarMonth extends React.PureComponent { render() { const { + css, dayAriaLabelFormat, daySize, focusedDate, diff --git a/src/components/CalendarMonthGrid.jsx b/src/components/CalendarMonthGrid.jsx index 0954594bd9..b876e181aa 100644 --- a/src/components/CalendarMonthGrid.jsx +++ b/src/components/CalendarMonthGrid.jsx @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import momentPropTypes from 'react-moment-proptypes'; import { forbidExtraProps, mutuallyExclusiveProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import moment from 'moment'; import { addEventListener } from 'consolidated-events'; @@ -265,6 +265,7 @@ class CalendarMonthGrid extends React.PureComponent { transitionDuration, verticalBorderSpacing, setMonthTitleHeight, + css, } = this.props; const { months } = this.state; diff --git a/src/components/CustomizableCalendarDay.jsx b/src/components/CustomizableCalendarDay.jsx index 2a3ee15752..97b983af57 100644 --- a/src/components/CustomizableCalendarDay.jsx +++ b/src/components/CustomizableCalendarDay.jsx @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import momentPropTypes from 'react-moment-proptypes'; import { forbidExtraProps, nonNegativeInteger, or } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import moment from 'moment'; import raf from 'raf'; @@ -272,6 +272,7 @@ class CustomizableCalendarDay extends React.PureComponent { render() { const { + css, day, ariaLabelFormat, daySize, diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index d50ecd9411..a119ac91c2 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import throttle from 'lodash/throttle'; import isTouchDevice from 'is-touch-device'; @@ -190,6 +190,7 @@ class DateInput extends React.PureComponent { block, styles, theme: { reactDates }, + css, } = this.props; const value = dateString || displayValue || ''; diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index bdfb3274e4..3e2d69f409 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -1,6 +1,6 @@ import React from 'react'; import moment from 'moment'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import { Portal } from 'react-portal'; import { forbidExtraProps } from 'airbnb-prop-types'; import { addEventListener } from 'consolidated-events'; @@ -392,6 +392,7 @@ class DateRangePicker extends React.PureComponent { renderDayPicker() { const { anchorDirection, + css, openDirection, isDayBlocked, isDayHighlighted, @@ -542,6 +543,7 @@ class DateRangePicker extends React.PureComponent { render() { const { + css, startDate, startDateId, startDatePlaceholderText, diff --git a/src/components/DateRangePickerInput.jsx b/src/components/DateRangePickerInput.jsx index 2389595852..5989b4f94f 100644 --- a/src/components/DateRangePickerInput.jsx +++ b/src/components/DateRangePickerInput.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import { DateRangePickerInputPhrases } from '../defaultPhrases'; import getPhrasePropTypes from '../utils/getPhrasePropTypes'; @@ -132,6 +132,7 @@ const defaultProps = { function DateRangePickerInput({ children, + css, startDate, startDateId, startDatePlaceholderText, diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index c954226a76..3f7e6b41fe 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps, mutuallyExclusiveProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import moment from 'moment'; import throttle from 'lodash/throttle'; @@ -856,6 +856,7 @@ class DayPicker extends React.PureComponent { orientation, weekDayFormat, styles, + css, } = this.props; const { calendarMonthWidth } = this.state; const verticalScrollable = orientation === VERTICAL_SCROLLABLE; @@ -918,6 +919,7 @@ class DayPicker extends React.PureComponent { } = this.state; const { + css, enableOutsideDays, numberOfMonths, orientation, diff --git a/src/components/DayPickerKeyboardShortcuts.jsx b/src/components/DayPickerKeyboardShortcuts.jsx index ffb8e83a8f..e257e2097e 100644 --- a/src/components/DayPickerKeyboardShortcuts.jsx +++ b/src/components/DayPickerKeyboardShortcuts.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import { DayPickerKeyboardShortcutsPhrases } from '../defaultPhrases'; import getPhrasePropTypes from '../utils/getPhrasePropTypes'; @@ -162,6 +162,7 @@ class DayPickerKeyboardShortcuts extends React.PureComponent { const { block, buttonLocation, + css, showKeyboardShortcutsPanel, closeKeyboardShortcutsPanel, styles, diff --git a/src/components/DayPickerNavigation.jsx b/src/components/DayPickerNavigation.jsx index 8f2f344604..689f3b019a 100644 --- a/src/components/DayPickerNavigation.jsx +++ b/src/components/DayPickerNavigation.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import { DayPickerNavigationPhrases } from '../defaultPhrases'; import getPhrasePropTypes from '../utils/getPhrasePropTypes'; @@ -51,6 +51,7 @@ const defaultProps = { }; function DayPickerNavigation({ + css, disablePrev, disableNext, navPrev, diff --git a/src/components/KeyboardShortcutRow.jsx b/src/components/KeyboardShortcutRow.jsx index 7e3d2401c4..fa719c07ac 100644 --- a/src/components/KeyboardShortcutRow.jsx +++ b/src/components/KeyboardShortcutRow.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; const propTypes = forbidExtraProps({ ...withStylesPropTypes, @@ -16,6 +16,7 @@ const defaultProps = { }; function KeyboardShortcutRow({ + css, unicode, label, action, diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index c07a2a8092..d636721551 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -1,6 +1,6 @@ import React from 'react'; import moment from 'moment'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import { Portal } from 'react-portal'; import { forbidExtraProps } from 'airbnb-prop-types'; import { addEventListener } from 'consolidated-events'; @@ -384,6 +384,7 @@ class SingleDatePicker extends React.PureComponent { renderDayPicker() { const { anchorDirection, + css, openDirection, onDateChange, date, @@ -519,6 +520,7 @@ class SingleDatePicker extends React.PureComponent { render() { const { + css, id, placeholder, ariaLabel, diff --git a/src/components/SingleDatePickerInput.jsx b/src/components/SingleDatePickerInput.jsx index af7b4d6731..88dbac23ff 100644 --- a/src/components/SingleDatePickerInput.jsx +++ b/src/components/SingleDatePickerInput.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import { SingleDatePickerInputPhrases } from '../defaultPhrases'; import getPhrasePropTypes from '../utils/getPhrasePropTypes'; @@ -95,6 +95,7 @@ const defaultProps = { function SingleDatePickerInput({ id, children, + css, placeholder, ariaLabel, displayValue, From a9cee419fee7b5440f36ab5481052aa1bb57320e Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 13 Aug 2019 09:03:18 -0700 Subject: [PATCH 482/618] [Dev Deps] update `@babel/*`, `@storybook/*`, `babel-plugin-inline-svg`, `babel-plugin-istanbul`, `babel-preset-airbnb`, `eslint-config-airbnb` to v18 (plus peer deps), `eslint` to v6; `eslint-plugin-react-with-styles`, `karma`, `karma-firefox-launcher`, `mocha`, `safe-publish-latest`, `sass-loader`, `sinon`, `sinon-sandbox`, `coveralls`, `enzyme-adapter-react-helper` --- .eslintrc | 23 +++++++- package.json | 55 ++++++++++--------- src/components/CalendarMonth.jsx | 2 +- src/components/CalendarMonthGrid.jsx | 2 +- src/components/DateRangePicker.jsx | 2 +- .../DateRangePickerInputController.jsx | 2 +- src/components/DayPickerRangeController.jsx | 46 ++++++++-------- .../DayPickerSingleDateController.jsx | 30 +++++----- src/components/SingleDatePicker.jsx | 2 +- .../SingleDatePickerInputController.jsx | 2 +- src/utils/disableScroll.js | 2 +- src/utils/getSelectedDateOffset.js | 2 +- src/utils/modifiers.js | 4 +- test/.eslintrc | 20 ------- test/browser-main.js | 2 +- test/components/CalendarDay_spec.jsx | 2 +- test/components/CalendarMonthGrid_spec.jsx | 4 +- .../CustomizableCalendarDay_spec.jsx | 2 +- .../DateRangePickerInputController_spec.jsx | 4 +- test/components/DateRangePicker_spec.jsx | 4 +- .../DayPickerRangeController_spec.jsx | 50 ++++++++--------- .../DayPickerSingleDateController_spec.jsx | 2 +- test/utils/getCalendarMonthWeeks_spec.js | 16 +++--- test/utils/getPhrasePropTypes_spec.js | 2 +- test/utils/getSelectedDateOffset_spec.js | 8 +-- test/utils/getVisibleDays_spec.js | 2 +- 26 files changed, 145 insertions(+), 147 deletions(-) delete mode 100644 test/.eslintrc diff --git a/.eslintrc b/.eslintrc index 7e07f00e93..cd5524f872 100644 --- a/.eslintrc +++ b/.eslintrc @@ -20,15 +20,32 @@ "jsx-a11y/click-events-have-key-events": 1, // TODO: enable - "react/no-did-mount-set-state": 0, // necessary for server-rendering - "react-with-styles/no-unused-styles": 2, "no-restricted-imports": 0, // TODO: enable with full RTL support - "react/jsx-one-expression-per-line": 1, // TODO: enable once fixed upstream + "react/jsx-props-no-spreading": 0, // TODO: re-evaluate }, + "overrides": [ + { + "files": "test/**/*", + "env": { + "mocha": true, + }, + "extends": "airbnb", + "rules": { + "react/jsx-props-no-spreading": 0, + //"import/no-extraneous-dependencies": [2, { + //"devDependencies": true + //}], + "indent": [2, 2, { + "MemberExpression": "off" + }], + }, + }, + ], + "settings": { "propWrapperFunctions": ["forbidExtraProps", "exact", "Object.freeze"], } diff --git a/package.json b/package.json index 0410862c50..d3560961c7 100644 --- a/package.json +++ b/package.json @@ -51,45 +51,45 @@ }, "homepage": "https://github.com/airbnb/react-dates#readme", "devDependencies": { - "@babel/cli": "^7.4.4", - "@babel/core": "^7.4.5", - "@babel/register": "^7.4.4", - "@storybook/addon-actions": "^5.1.1", - "@storybook/addon-info": "^5.1.1", - "@storybook/addon-links": "^5.1.1", - "@storybook/addon-options": "^5.1.1", - "@storybook/react": "^5.1.1", - "acorn": "^6.1.1", + "@babel/cli": "^7.5.5", + "@babel/core": "^7.5.5", + "@babel/register": "^7.5.5", + "@storybook/addon-actions": "^5.1.11", + "@storybook/addon-info": "^5.1.11", + "@storybook/addon-links": "^5.1.11", + "@storybook/addon-options": "^5.1.11", + "@storybook/react": "^5.1.11", "airbnb-js-shims": "^2.2.0", "aphrodite": "^2.3.1", "babel-loader": "^8.0.6", "babel-plugin-inline-react-svg": "^1.1.0", - "babel-plugin-inline-svg": "^1.0.0", - "babel-plugin-istanbul": "^5.1.4", + "babel-plugin-inline-svg": "^1.0.1", + "babel-plugin-istanbul": "^5.2.0", "babel-plugin-transform-replace-object-assign": "^2.0.0", - "babel-preset-airbnb": "^4.0.0", + "babel-preset-airbnb": "^4.0.1", "chai": "^4.2.0", "clean-css": "^4.2.1", - "coveralls": "^3.0.3", + "coveralls": "^3.0.6", "cross-env": "^5.2.0", "enzyme": "^3.10.0", - "enzyme-adapter-react-helper": "^1.3.5", - "eslint": "^6.0.1", - "eslint-config-airbnb": "^17.1.0", - "eslint-plugin-import": "^2.17.3", - "eslint-plugin-jsx-a11y": "^6.2.1", - "eslint-plugin-react": "^7.13.0", - "eslint-plugin-react-with-styles": "^2.1.0", + "enzyme-adapter-react-helper": "^1.3.6", + "eslint": "^6.2.1", + "eslint-config-airbnb": "^18.0.1", + "eslint-plugin-import": "^2.18.2", + "eslint-plugin-jsx-a11y": "^6.2.3", + "eslint-plugin-react": "^7.14.3", + "eslint-plugin-react-hooks": "^1.7.0", + "eslint-plugin-react-with-styles": "^2.2.0", "git-directory-deploy": "^1.5.1", "imports-loader": "^0.8.0", "in-publish": "^2.0.0", - "karma": "^4.1.0", + "karma": "^4.2.0", "karma-chai": "^0.1.0", - "karma-firefox-launcher": "^1.1.0", + "karma-firefox-launcher": "^1.2.0", "karma-mocha": "^1.3.0", "karma-sinon": "^1.0.5", "karma-webpack": "^4.0.2", - "mocha": "^6.1.4", + "mocha": "^3.5.3", "mocha-wrap": "^2.1.2", "moment": "^2.24.0", "moment-jalaali": "^0.7.4", @@ -101,11 +101,12 @@ "react-with-styles-interface-aphrodite": "^5.0.1", "react-with-styles-interface-css-compiler": "^2.0.0", "rimraf": "^2.6.3", - "safe-publish-latest": "^1.1.2", - "sass-loader": "^7.1.0", - "sinon": "^7.3.2", - "sinon-sandbox": "^2.0.0", + "safe-publish-latest": "^1.1.3", + "sass-loader": "^7.2.0", + "sinon": "^7.4.1", + "sinon-sandbox": "^2.0.5", "style-loader": "^0.20.3", + "typescript": "*", "webpack": "^4.31.0", "why-did-you-update": "^1.0.6" }, diff --git a/src/components/CalendarMonth.jsx b/src/components/CalendarMonth.jsx index 508ff8947e..910b963905 100644 --- a/src/components/CalendarMonth.jsx +++ b/src/components/CalendarMonth.jsx @@ -74,7 +74,7 @@ const defaultProps = { onMonthSelect() {}, onYearSelect() {}, renderMonthText: null, - renderCalendarDay: props => (), + renderCalendarDay: (props) => (), renderDayContents: null, renderMonthElement: null, firstDayOfWeek: null, diff --git a/src/components/CalendarMonthGrid.jsx b/src/components/CalendarMonthGrid.jsx index b876e181aa..7d23d972c1 100644 --- a/src/components/CalendarMonthGrid.jsx +++ b/src/components/CalendarMonthGrid.jsx @@ -173,7 +173,7 @@ class CalendarMonthGrid extends React.PureComponent { const momentLocale = moment.locale(); if (this.locale !== momentLocale) { this.locale = momentLocale; - newMonths = newMonths.map(m => m.locale(this.locale)); + newMonths = newMonths.map((m) => m.locale(this.locale)); } this.setState({ diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 3e2d69f409..d1af316264 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -112,7 +112,7 @@ const defaultProps = { minimumNights: 1, enableOutsideDays: false, isDayBlocked: () => false, - isOutsideRange: day => !isInclusivelyAfterDay(day, moment()), + isOutsideRange: (day) => !isInclusivelyAfterDay(day, moment()), isDayHighlighted: () => false, // internationalization diff --git a/src/components/DateRangePickerInputController.jsx b/src/components/DateRangePickerInputController.jsx index a504e3cc74..681beb2a48 100644 --- a/src/components/DateRangePickerInputController.jsx +++ b/src/components/DateRangePickerInputController.jsx @@ -117,7 +117,7 @@ const defaultProps = { reopenPickerOnClearDates: false, withFullScreenPortal: false, minimumNights: 1, - isOutsideRange: day => !isInclusivelyAfterDay(day, moment()), + isOutsideRange: (day) => !isInclusivelyAfterDay(day, moment()), displayFormat: () => moment.localeData().longDateFormat('L'), onFocusChange() {}, diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 169f05b85d..42b0729e45 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -193,23 +193,23 @@ export default class DayPickerRangeController extends React.PureComponent { this.isTouchDevice = isTouchDevice(); this.today = moment(); this.modifiers = { - today: day => this.isToday(day), - blocked: day => this.isBlocked(day), - 'blocked-calendar': day => props.isDayBlocked(day), - 'blocked-out-of-range': day => props.isOutsideRange(day), - 'highlighted-calendar': day => props.isDayHighlighted(day), - valid: day => !this.isBlocked(day), - 'selected-start': day => this.isStartDate(day), - 'selected-end': day => this.isEndDate(day), - 'blocked-minimum-nights': day => this.doesNotMeetMinimumNights(day), - 'selected-span': day => this.isInSelectedSpan(day), - 'last-in-range': day => this.isLastInRange(day), - hovered: day => this.isHovered(day), - 'hovered-span': day => this.isInHoveredSpan(day), - 'hovered-offset': day => this.isInHoveredSpan(day), - 'after-hovered-start': day => this.isDayAfterHoveredStartDate(day), - 'first-day-of-week': day => this.isFirstDayOfWeek(day), - 'last-day-of-week': day => this.isLastDayOfWeek(day), + today: (day) => this.isToday(day), + blocked: (day) => this.isBlocked(day), + 'blocked-calendar': (day) => props.isDayBlocked(day), + 'blocked-out-of-range': (day) => props.isOutsideRange(day), + 'highlighted-calendar': (day) => props.isDayHighlighted(day), + valid: (day) => !this.isBlocked(day), + 'selected-start': (day) => this.isStartDate(day), + 'selected-end': (day) => this.isEndDate(day), + 'blocked-minimum-nights': (day) => this.doesNotMeetMinimumNights(day), + 'selected-span': (day) => this.isInSelectedSpan(day), + 'last-in-range': (day) => this.isLastInRange(day), + hovered: (day) => this.isHovered(day), + 'hovered-span': (day) => this.isInHoveredSpan(day), + 'hovered-offset': (day) => this.isInHoveredSpan(day), + 'after-hovered-start': (day) => this.isDayAfterHoveredStartDate(day), + 'first-day-of-week': (day) => this.isFirstDayOfWeek(day), + 'last-day-of-week': (day) => this.isLastDayOfWeek(day), 'hovered-start-first-possible-end': (day, hoverDate) => this.isFirstPossibleEndDateForHoveredStartDate(day, hoverDate), 'hovered-start-blocked-minimum-nights': (day, hoverDate) => this.doesNotMeetMinNightsForHoveredStartDate(day, hoverDate), }; @@ -281,17 +281,17 @@ export default class DayPickerRangeController extends React.PureComponent { let recomputeDayHighlighted = false; if (isOutsideRange !== prevIsOutsideRange) { - this.modifiers['blocked-out-of-range'] = day => isOutsideRange(day); + this.modifiers['blocked-out-of-range'] = (day) => isOutsideRange(day); recomputeOutsideRange = true; } if (isDayBlocked !== prevIsDayBlocked) { - this.modifiers['blocked-calendar'] = day => isDayBlocked(day); + this.modifiers['blocked-calendar'] = (day) => isDayBlocked(day); recomputeDayBlocked = true; } if (isDayHighlighted !== prevIsDayHighlighted) { - this.modifiers['highlighted-calendar'] = day => isDayHighlighted(day); + this.modifiers['highlighted-calendar'] = (day) => isDayHighlighted(day); recomputeDayHighlighted = true; } @@ -619,7 +619,7 @@ export default class DayPickerRangeController extends React.PureComponent { if (hasOffset) { const start = getSelectedDateOffset(startDateOffset, day); - const end = getSelectedDateOffset(endDateOffset, day, rangeDay => rangeDay.add(1, 'day')); + const end = getSelectedDateOffset(endDateOffset, day, (rangeDay) => rangeDay.add(1, 'day')); nextDateOffset = { start, @@ -923,7 +923,7 @@ export default class DayPickerRangeController extends React.PureComponent { days.push(currentDay); } - const viableDays = days.filter(day => !this.isBlocked(day)); + const viableDays = days.filter((day) => !this.isBlocked(day)); if (viableDays.length > 0) { ([focusedDate] = viableDays); @@ -946,7 +946,7 @@ export default class DayPickerRangeController extends React.PureComponent { } getModifiersForDay(day) { - return new Set(Object.keys(this.modifiers).filter(modifier => this.modifiers[modifier](day))); + return new Set(Object.keys(this.modifiers).filter((modifier) => this.modifiers[modifier](day))); } getStateForNewMonth(nextProps) { diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 6884fef2fe..36b9dd512e 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -154,16 +154,16 @@ export default class DayPickerSingleDateController extends React.PureComponent { this.today = moment(); this.modifiers = { - today: day => this.isToday(day), - blocked: day => this.isBlocked(day), - 'blocked-calendar': day => props.isDayBlocked(day), - 'blocked-out-of-range': day => props.isOutsideRange(day), - 'highlighted-calendar': day => props.isDayHighlighted(day), - valid: day => !this.isBlocked(day), - hovered: day => this.isHovered(day), - selected: day => this.isSelected(day), - 'first-day-of-week': day => this.isFirstDayOfWeek(day), - 'last-day-of-week': day => this.isLastDayOfWeek(day), + today: (day) => this.isToday(day), + blocked: (day) => this.isBlocked(day), + 'blocked-calendar': (day) => props.isDayBlocked(day), + 'blocked-out-of-range': (day) => props.isOutsideRange(day), + 'highlighted-calendar': (day) => props.isDayHighlighted(day), + valid: (day) => !this.isBlocked(day), + hovered: (day) => this.isHovered(day), + selected: (day) => this.isSelected(day), + 'first-day-of-week': (day) => this.isFirstDayOfWeek(day), + 'last-day-of-week': (day) => this.isLastDayOfWeek(day), }; const { currentMonth, visibleDays } = this.getStateForNewMonth(props); @@ -218,17 +218,17 @@ export default class DayPickerSingleDateController extends React.PureComponent { let recomputeDayHighlighted = false; if (isOutsideRange !== prevIsOutsideRange) { - this.modifiers['blocked-out-of-range'] = day => isOutsideRange(day); + this.modifiers['blocked-out-of-range'] = (day) => isOutsideRange(day); recomputeOutsideRange = true; } if (isDayBlocked !== prevIsDayBlocked) { - this.modifiers['blocked-calendar'] = day => isDayBlocked(day); + this.modifiers['blocked-calendar'] = (day) => isDayBlocked(day); recomputeDayBlocked = true; } if (isDayHighlighted !== prevIsDayHighlighted) { - this.modifiers['highlighted-calendar'] = day => isDayHighlighted(day); + this.modifiers['highlighted-calendar'] = (day) => isDayHighlighted(day); recomputeDayHighlighted = true; } @@ -466,7 +466,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { days.push(currentDay); } - const viableDays = days.filter(day => !this.isBlocked(day) && isAfterDay(day, focusedDate)); + const viableDays = days.filter((day) => !this.isBlocked(day) && isAfterDay(day, focusedDate)); if (viableDays.length > 0) { ([focusedDate] = viableDays); } @@ -488,7 +488,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { } getModifiersForDay(day) { - return new Set(Object.keys(this.modifiers).filter(modifier => this.modifiers[modifier](day))); + return new Set(Object.keys(this.modifiers).filter((modifier) => this.modifiers[modifier](day))); } getStateForNewMonth(nextProps) { diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index d636721551..6e4b40109f 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -105,7 +105,7 @@ const defaultProps = { renderMonthElement: null, enableOutsideDays: false, isDayBlocked: () => false, - isOutsideRange: day => !isInclusivelyAfterDay(day, moment()), + isOutsideRange: (day) => !isInclusivelyAfterDay(day, moment()), isDayHighlighted: () => {}, // internationalization props diff --git a/src/components/SingleDatePickerInputController.jsx b/src/components/SingleDatePickerInputController.jsx index 5777068f1a..2da008ee16 100644 --- a/src/components/SingleDatePickerInputController.jsx +++ b/src/components/SingleDatePickerInputController.jsx @@ -97,7 +97,7 @@ const defaultProps = { keepOpenOnDateSelect: false, reopenPickerOnClearDate: false, - isOutsideRange: day => !isInclusivelyAfterDay(day, moment()), + isOutsideRange: (day) => !isInclusivelyAfterDay(day, moment()), displayFormat: () => moment.localeData().longDateFormat('L'), onClose() {}, diff --git a/src/utils/disableScroll.js b/src/utils/disableScroll.js index e8c6d506ee..1cf026771a 100644 --- a/src/utils/disableScroll.js +++ b/src/utils/disableScroll.js @@ -56,7 +56,7 @@ export function getScrollAncestorsOverflowY(node, acc = new Map()) { */ export default function disableScroll(node) { const scrollAncestorsOverflowY = getScrollAncestorsOverflowY(node); - const toggle = on => scrollAncestorsOverflowY.forEach((overflowY, ancestor) => { + const toggle = (on) => scrollAncestorsOverflowY.forEach((overflowY, ancestor) => { ancestor.style.setProperty('overflow-y', on ? 'hidden' : overflowY); }); diff --git a/src/utils/getSelectedDateOffset.js b/src/utils/getSelectedDateOffset.js index 6200f9b65d..d5ed045920 100644 --- a/src/utils/getSelectedDateOffset.js +++ b/src/utils/getSelectedDateOffset.js @@ -1,4 +1,4 @@ -const defaultModifier = day => day; +const defaultModifier = (day) => day; export default function getSelectedDateOffset(fn, day, modifier = defaultModifier) { if (!fn) return day; diff --git a/src/utils/modifiers.js b/src/utils/modifiers.js index 5f1dbb13fd..d76226bb9e 100644 --- a/src/utils/modifiers.js +++ b/src/utils/modifiers.js @@ -25,7 +25,7 @@ export function addModifier(updatedDays, day, modifier, props, state) { let updatedDaysAfterAddition = { ...updatedDays }; if (enableOutsideDays) { - const monthsToUpdate = Object.keys(visibleDays).filter(monthKey => ( + const monthsToUpdate = Object.keys(visibleDays).filter((monthKey) => ( Object.keys(visibleDays[monthKey]).indexOf(iso) > -1 )); @@ -80,7 +80,7 @@ export function deleteModifier(updatedDays, day, modifier, props, state) { let updatedDaysAfterDeletion = { ...updatedDays }; if (enableOutsideDays) { - const monthsToUpdate = Object.keys(visibleDays).filter(monthKey => ( + const monthsToUpdate = Object.keys(visibleDays).filter((monthKey) => ( Object.keys(visibleDays[monthKey]).indexOf(iso) > -1 )); diff --git a/test/.eslintrc b/test/.eslintrc deleted file mode 100644 index 7b52873412..0000000000 --- a/test/.eslintrc +++ /dev/null @@ -1,20 +0,0 @@ -{ - "globals": { - "describe": true, - "context": true, - "it": true, - "before": true, - "after": true, - "beforeEach": true, - "afterEach": true - }, - "extends": "airbnb", - "rules": { - "import/no-extraneous-dependencies": [2, { - "devDependencies": true - }], - "indent": [2, 2, { - "MemberExpression": "off" - }], - } -} diff --git a/test/browser-main.js b/test/browser-main.js index 7cbfda934e..3f61c7d401 100644 --- a/test/browser-main.js +++ b/test/browser-main.js @@ -1,4 +1,4 @@ -const requireAll = requireContext => requireContext.keys().forEach(requireContext); +const requireAll = (requireContext) => requireContext.keys().forEach(requireContext); if (typeof window !== 'undefined') { requireAll(require.context('./_helpers', true, /.jsx?$/)); diff --git a/test/components/CalendarDay_spec.jsx b/test/components/CalendarDay_spec.jsx index c0a6281cef..bc968fc746 100644 --- a/test/components/CalendarDay_spec.jsx +++ b/test/components/CalendarDay_spec.jsx @@ -28,7 +28,7 @@ describe('CalendarDay', () => { it('contains arbitrary content if renderDay is provided', () => { const dayName = moment().format('dddd'); - const renderDay = day => day.format('dddd'); + const renderDay = (day) => day.format('dddd'); const wrapper = shallow().dive(); expect(wrapper.text()).to.equal(dayName); }); diff --git a/test/components/CalendarMonthGrid_spec.jsx b/test/components/CalendarMonthGrid_spec.jsx index f90832cbab..209d7a3d92 100644 --- a/test/components/CalendarMonthGrid_spec.jsx +++ b/test/components/CalendarMonthGrid_spec.jsx @@ -39,7 +39,7 @@ describe('CalendarMonthGrid', () => { const { months } = wrapper.state(); const collisions = months - .map(m => m.format('YYYY-MM')) + .map((m) => m.format('YYYY-MM')) .reduce((acc, m) => ({ ...acc, [m]: true }), {}); expect(Object.keys(collisions).length).to.equal(months.length); @@ -60,7 +60,7 @@ describe('CalendarMonthGrid', () => { const { months } = wrapper.state(); const collisions = months - .map(m => m.format('YYYY-MM')) + .map((m) => m.format('YYYY-MM')) .reduce((acc, m) => ({ ...acc, [m]: true }), {}); expect(Object.keys(collisions).length).to.equal(months.length); diff --git a/test/components/CustomizableCalendarDay_spec.jsx b/test/components/CustomizableCalendarDay_spec.jsx index a138b482e4..9096e7129f 100644 --- a/test/components/CustomizableCalendarDay_spec.jsx +++ b/test/components/CustomizableCalendarDay_spec.jsx @@ -28,7 +28,7 @@ describe('CustomizableCalendarDay', () => { it('contains arbitrary content if renderDay is provided', () => { const dayName = moment().format('dddd'); - const renderDay = day => day.format('dddd'); + const renderDay = (day) => day.format('dddd'); const wrapper = shallow().dive(); expect(wrapper.text()).to.equal(dayName); }); diff --git a/test/components/DateRangePickerInputController_spec.jsx b/test/components/DateRangePickerInputController_spec.jsx index 09bb1f84c1..779450d8ec 100644 --- a/test/components/DateRangePickerInputController_spec.jsx +++ b/test/components/DateRangePickerInputController_spec.jsx @@ -390,7 +390,7 @@ describe('DateRangePickerInputController', () => { describe('is outside range', () => { const futureDate = moment().add(7, 'day').toISOString(); - const isOutsideRange = day => day >= moment().add(3, 'day'); + const isOutsideRange = (day) => day >= moment().add(3, 'day'); it('calls props.onDatesChange', () => { const onDatesChangeStub = sinon.stub(); @@ -721,7 +721,7 @@ describe('DateRangePickerInputController', () => { describe('is outside range', () => { const futureDate = moment().add(7, 'days').toISOString(); - const isOutsideRange = day => day > moment().add(5, 'days'); + const isOutsideRange = (day) => day > moment().add(5, 'days'); it('calls props.onDatesChange', () => { const onDatesChangeStub = sinon.stub(); diff --git a/test/components/DateRangePicker_spec.jsx b/test/components/DateRangePicker_spec.jsx index e573a0cf90..645b7e482a 100644 --- a/test/components/DateRangePicker_spec.jsx +++ b/test/components/DateRangePicker_spec.jsx @@ -708,7 +708,7 @@ describe('DateRangePicker', () => { const wrapper = shallow(( date.subtract(5, 'days')} + startDateOffset={(date) => date.subtract(5, 'days')} onDatesChange={onDatesChangeStub} focusedInput={START_DATE} /> @@ -728,7 +728,7 @@ describe('DateRangePicker', () => { const wrapper = shallow(( date.subtract(5, 'days')} + endDateOffset={(date) => date.subtract(5, 'days')} onDatesChange={onDatesChangeStub} focusedInput={START_DATE} /> diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index 5e0506bd3a..f12d496b7d 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -22,7 +22,7 @@ import { START_DATE, END_DATE, VERTICAL_SCROLLABLE } from '../../src/constants'; const today = moment().startOf('day').hours(12); function getCallsByModifier(stub, modifier) { - return stub.getCalls().filter(call => call.args[call.args.length - 1] === modifier); + return stub.getCalls().filter((call) => call.args[call.args.length - 1] === modifier); } describe('DayPickerRangeController', () => { @@ -1221,7 +1221,7 @@ describe('DayPickerRangeController', () => { isSameDay(day, today)} + isDayBlocked={(day) => isSameDay(day, today)} />, ); wrapper.setState({ hoverDate: today }); @@ -1293,7 +1293,7 @@ describe('DayPickerRangeController', () => { isSameDay(day, today)} + isDayBlocked={(day) => isSameDay(day, today)} />, ); wrapper.setState({ hoverDate: today }); @@ -1412,7 +1412,7 @@ describe('DayPickerRangeController', () => { isSameDay(day, today)} + isDayBlocked={(day) => isSameDay(day, today)} />, ); wrapper.setState({ hoverDate: today }); @@ -1483,7 +1483,7 @@ describe('DayPickerRangeController', () => { isSameDay(day, today)} + isDayBlocked={(day) => isSameDay(day, today)} />, ); wrapper.setState({ hoverDate: today }); @@ -1935,8 +1935,8 @@ describe('DayPickerRangeController', () => { const wrapper = shallow(( day.subtract(2, 'days')} - endDateOffset={day => day.add(4, 'days')} + startDateOffset={(day) => day.subtract(2, 'days')} + endDateOffset={(day) => day.add(4, 'days')} /> )); wrapper.instance().onDayClick(clickDate); @@ -1951,9 +1951,9 @@ describe('DayPickerRangeController', () => { const wrapper = shallow(( day.subtract(2, 'days')} - endDateOffset={day => day.add(4, 'days')} - isOutsideRange={day => day.isAfter(moment(today))} + startDateOffset={(day) => day.subtract(2, 'days')} + endDateOffset={(day) => day.add(4, 'days')} + isOutsideRange={(day) => day.isAfter(moment(today))} /> )); wrapper.instance().onDayClick(clickDate); @@ -1966,8 +1966,8 @@ describe('DayPickerRangeController', () => { const wrapper = shallow(( day.add(5, 'days')} - isOutsideRange={day => day.isAfter(moment(today).clone().add(1, 'days'))} + endDateOffset={(day) => day.add(5, 'days')} + isOutsideRange={(day) => day.isAfter(moment(today).clone().add(1, 'days'))} /> )); wrapper.instance().onDayClick(clickDate); @@ -1980,7 +1980,7 @@ describe('DayPickerRangeController', () => { const wrapper = shallow(( day.subtract(5, 'days')} + startDateOffset={(day) => day.subtract(5, 'days')} /> )); wrapper.instance().onDayClick(clickDate); @@ -1995,7 +1995,7 @@ describe('DayPickerRangeController', () => { const wrapper = shallow(( day.add(12, 'days')} + endDateOffset={(day) => day.add(12, 'days')} /> )); wrapper.instance().onDayClick(clickDate); @@ -2105,7 +2105,7 @@ describe('DayPickerRangeController', () => { const wrapper = shallow(( day.subtract(2, 'days')} + startDateOffset={(day) => day.subtract(2, 'days')} /> )); wrapper.instance().onDayClick(clickDate); @@ -2121,7 +2121,7 @@ describe('DayPickerRangeController', () => { const wrapper = shallow(( day.add(4, 'days')} + endDateOffset={(day) => day.add(4, 'days')} /> )); wrapper.instance().onDayClick(clickDate); @@ -2193,7 +2193,7 @@ describe('DayPickerRangeController', () => { const wrapper = shallow(( day.add(2, 'days')} + endDateOffset={(day) => day.add(2, 'days')} /> )); wrapper.instance().onDayMouseEnter(today); @@ -2449,7 +2449,7 @@ describe('DayPickerRangeController', () => { onFocusChange={sinon.stub()} focusedInput={START_DATE} getMinNightsForHoverDate={getMinNightsForHoverDateStub} - isDayBlocked={day => isSameDay(day, hoverDate)} + isDayBlocked={(day) => isSameDay(day, hoverDate)} />); wrapper.setState({ hoverDate }); wrapper.instance().onDayMouseEnter(today); @@ -2496,7 +2496,7 @@ describe('DayPickerRangeController', () => { onFocusChange={sinon.stub()} focusedInput={START_DATE} getMinNightsForHoverDate={getMinNightsForHoverDateStub} - isDayBlocked={day => isSameDay(day, today)} + isDayBlocked={(day) => isSameDay(day, today)} />); wrapper.setState({ hoverDate }); wrapper.instance().onDayMouseEnter(today); @@ -2606,7 +2606,7 @@ describe('DayPickerRangeController', () => { onFocusChange={sinon.stub()} focusedInput={START_DATE} getMinNightsForHoverDate={getMinNightsForHoverDateStub} - isDayBlocked={day => isSameDay(day, hoverDate)} + isDayBlocked={(day) => isSameDay(day, hoverDate)} />); wrapper.setState({ hoverDate }); wrapper.instance().onDayMouseEnter(today); @@ -2654,7 +2654,7 @@ describe('DayPickerRangeController', () => { onFocusChange={sinon.stub()} focusedInput={START_DATE} getMinNightsForHoverDate={getMinNightsForHoverDateStub} - isDayBlocked={day => isSameDay(day, today)} + isDayBlocked={(day) => isSameDay(day, today)} />); wrapper.setState({ hoverDate }); wrapper.instance().onDayMouseEnter(today); @@ -2893,7 +2893,7 @@ describe('DayPickerRangeController', () => { onFocusChange={sinon.stub()} focusedInput={START_DATE} getMinNightsForHoverDate={getMinNightsForHoverDateStub} - isDayBlocked={day => isSameDay(day, today)} + isDayBlocked={(day) => isSameDay(day, today)} />); wrapper.setState({ hoverDate: today }); wrapper.instance().onDayMouseLeave(today); @@ -2962,7 +2962,7 @@ describe('DayPickerRangeController', () => { onFocusChange={sinon.stub()} focusedInput={START_DATE} getMinNightsForHoverDate={getMinNightsForHoverDateStub} - isDayBlocked={day => isSameDay(day, today)} + isDayBlocked={(day) => isSameDay(day, today)} />); wrapper.setState({ hoverDate: today }); wrapper.instance().onDayMouseLeave(today); @@ -4006,7 +4006,7 @@ describe('DayPickerRangeController', () => { describe('props.startDate === null', () => { describe('props.focusedInput === END_DATE', () => { it('returns true if arg - props.minimumNights is outside allowed range', () => { - const isOutsideRange = day => !isInclusivelyAfterDay(day, today); + const isOutsideRange = (day) => !isInclusivelyAfterDay(day, today); const testDate = moment(today).add(MIN_NIGHTS - 1, 'days'); const wrapper = shallow( { }); it('returns false if arg - props.minimumNights is inside allowed range', () => { - const isOutsideRange = day => !isInclusivelyAfterDay(day, today); + const isOutsideRange = (day) => !isInclusivelyAfterDay(day, today); const testDate = moment(today).add(MIN_NIGHTS, 'days'); const wrapper = shallow( call.args[call.args.length - 1] === modifier); + return stub.getCalls().filter((call) => call.args[call.args.length - 1] === modifier); } describe('DayPickerSingleDateController', () => { diff --git a/test/utils/getCalendarMonthWeeks_spec.js b/test/utils/getCalendarMonthWeeks_spec.js index fc546ade41..c6163af117 100644 --- a/test/utils/getCalendarMonthWeeks_spec.js +++ b/test/utils/getCalendarMonthWeeks_spec.js @@ -121,19 +121,19 @@ describe('getCalendarMonthWeeks', () => { describe('enableOutsideDays arg is false', () => { it('first non-null element is first of the month', () => { const firstOfMonth = today.clone().startOf('month'); - const firstNonNullDay = weeks[0].filter(day => day)[0]; + const firstNonNullDay = weeks[0].filter((day) => day)[0]; expect(firstOfMonth.isSame(firstNonNullDay, 'day')).to.equal(true); }); it('last non-null element is last of the month', () => { const lastOfMonth = today.clone().endOf('month'); - const lastWeek = weeks[weeks.length - 1].filter(day => day); + const lastWeek = weeks[weeks.length - 1].filter((day) => day); const lastNonNullDay = lastWeek[lastWeek.length - 1]; expect(lastOfMonth.isSame(lastNonNullDay, 'day')).to.equal(true); }); it('number of non-null elements is equal to number of days in month', () => { - const daysInCalendarMonthWeeks = weeks.reduce((a, b) => a + b.filter(day => day).length, 0); + const daysInCalendarMonthWeeks = weeks.reduce((a, b) => a + b.filter((day) => day).length, 0); expect(daysInCalendarMonthWeeks).to.equal(today.daysInMonth()); }); }); @@ -142,14 +142,14 @@ describe('getCalendarMonthWeeks', () => { it('contains first of the month', () => { const firstOfMonth = today.clone().startOf('month'); const containsFirstOfMonth = weeksWithOutsideDays[0] - .filter(day => firstOfMonth.isSame(day, 'day')).length > 0; + .filter((day) => firstOfMonth.isSame(day, 'day')).length > 0; expect(containsFirstOfMonth).to.equal(true); }); it('last week contains last of the month', () => { const lastOfMonth = today.clone().endOf('month'); const containsLastOfMonth = weeks[weeksWithOutsideDays.length - 1] - .filter(day => lastOfMonth.isSame(day, 'day')).length > 0; + .filter((day) => lastOfMonth.isSame(day, 'day')).length > 0; expect(containsLastOfMonth).to.equal(true); }); @@ -158,7 +158,7 @@ describe('getCalendarMonthWeeks', () => { const lastOfMonth = december2016.clone().endOf('month'); const weeksInDecember = getCalendarMonthWeeks(december2016); const containsLastOfMonth = weeksInDecember[weeksInDecember.length - 1] - .filter(day => lastOfMonth.isSame(day, 'day')).length > 0; + .filter((day) => lastOfMonth.isSame(day, 'day')).length > 0; expect(containsLastOfMonth).to.equal(true); }); @@ -168,7 +168,7 @@ describe('getCalendarMonthWeeks', () => { const lastOfMonth = april2017.clone().endOf('month'); const weeksInApril = getCalendarMonthWeeks(april2017); const containsLastOfMonth = weeksInApril[weeksInApril.length - 1] - .filter(day => lastOfMonth.isSame(day, 'day')).length > 0; + .filter((day) => lastOfMonth.isSame(day, 'day')).length > 0; expect(containsLastOfMonth).to.equal(true); }); @@ -177,7 +177,7 @@ describe('getCalendarMonthWeeks', () => { const lastOfMonth = september2016.clone().endOf('month'); const weeksInSeptember = getCalendarMonthWeeks(september2016); const containsLastOfMonth = weeksInSeptember[weeksInSeptember.length - 1] - .filter(day => lastOfMonth.isSame(day, 'day')).length > 0; + .filter((day) => lastOfMonth.isSame(day, 'day')).length > 0; expect(containsLastOfMonth).to.equal(true); }); diff --git a/test/utils/getPhrasePropTypes_spec.js b/test/utils/getPhrasePropTypes_spec.js index 1b0ae5e4a1..0ba1d35dc1 100644 --- a/test/utils/getPhrasePropTypes_spec.js +++ b/test/utils/getPhrasePropTypes_spec.js @@ -12,7 +12,7 @@ describe('#getPhrasePropTypes', () => { it('contains each key from the original object', () => { const propTypes = getPhrasePropTypes(PhraseObject); Object.keys(PhraseObject).forEach((key) => { - expect(Object.keys(propTypes).filter(type => type === key).length).to.not.equal(0); + expect(Object.keys(propTypes).filter((type) => type === key).length).to.not.equal(0); }); }); }); diff --git a/test/utils/getSelectedDateOffset_spec.js b/test/utils/getSelectedDateOffset_spec.js index dfaff4b2c7..f612e4aa24 100644 --- a/test/utils/getSelectedDateOffset_spec.js +++ b/test/utils/getSelectedDateOffset_spec.js @@ -7,7 +7,7 @@ const today = moment(); describe('#getSelectedDateOffset', () => { it('returns a function modified moment object', () => { - const fn = day => day.add(2, 'days'); + const fn = (day) => day.add(2, 'days'); const modifiedDay = getSelectedDateOffset(fn, today); expect(modifiedDay.format()).to.equal(today.clone().add(2, 'days').format()); }); @@ -18,14 +18,14 @@ describe('#getSelectedDateOffset', () => { }); it('modifies the returned day using the modifier callback', () => { - const fn = day => day.add(2, 'days'); - const modifier = day => day.subtract(2, 'days'); + const fn = (day) => day.add(2, 'days'); + const modifier = (day) => day.subtract(2, 'days'); const modifiedDay = getSelectedDateOffset(fn, today, modifier); expect(modifiedDay.format()).to.equal(today.clone().format()); }); it('does not apply the modifier if function is undefined', () => { - const modifier = day => day.subtract(2, 'days'); + const modifier = (day) => day.subtract(2, 'days'); const modifiedDay = getSelectedDateOffset(undefined, today, modifier); expect(modifiedDay.format()).to.equal(today.format()); }); diff --git a/test/utils/getVisibleDays_spec.js b/test/utils/getVisibleDays_spec.js index ee91893554..2057835931 100644 --- a/test/utils/getVisibleDays_spec.js +++ b/test/utils/getVisibleDays_spec.js @@ -26,7 +26,7 @@ describe('getVisibleDays', () => { it('contains first arg day', () => { const visibleDays = getVisibleDays(today, 3, false); const containsToday = Object.values(visibleDays) - .filter(days => days.filter(day => isSameDay(day, today)).length > 0); + .filter((days) => days.filter((day) => isSameDay(day, today)).length > 0); expect(containsToday.length > 0).to.equal(true); }); }); From 906b4c694b39f9151d15db2a7ce371789a123cd7 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Wed, 14 Aug 2019 12:27:57 -0700 Subject: [PATCH 483/618] [Deps] update `@babel/runtime`, `airbnb-prop-types`, `object.values`, `prop-types`, `react-outside-click-handler`, `react-portal` --- package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index d3560961c7..693b1aea0d 100644 --- a/package.json +++ b/package.json @@ -111,22 +111,22 @@ "why-did-you-update": "^1.0.6" }, "dependencies": { - "@babel/runtime": "^7.4.5", - "airbnb-prop-types": "^2.10.0", + "@babel/runtime": "^7.5.5", + "airbnb-prop-types": "^2.15.0", "consolidated-events": "^1.1.1 || ^2.0.0", "enzyme-shallow-equal": "^1.0.0", "is-touch-device": "^1.0.1", "lodash": "^4.1.1", "object.assign": "^4.1.0", - "object.values": "^1.0.4", - "prop-types": "^15.6.1", + "object.values": "^1.1.0", + "prop-types": "^15.7.2", "raf": "^3.4.1", "react-moment-proptypes": "^1.6.0", - "react-outside-click-handler": "^1.2.0", - "react-portal": "^4.1.5", + "react-outside-click-handler": "^1.2.4", + "react-portal": "^4.2.0", "react-with-direction": "^1.3.0", "react-with-styles": "^3.2.3", - "react-with-styles-interface-css": "^4.0.2" + "react-with-styles-interface-css": "^4.0.3" }, "peerDependencies": { "moment": "^2.18.1", From 870534089c1366ffb42d0635a460812c9750fa94 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 13 Aug 2019 09:03:32 -0700 Subject: [PATCH 484/618] [Tests] on `node` `v12` --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index cb0705112c..c09a1f2747 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: node_js node_js: + - "12" - "10" - "8" - "6" From 965f395ad761fbebab9705af552c0a646155d53a Mon Sep 17 00:00:00 2001 From: "weiran.zsd" Date: Thu, 22 Aug 2019 09:37:38 +0800 Subject: [PATCH 485/618] build: fix linting (refs eslint/eslint#12119) --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index c09a1f2747..57583f8c53 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,8 @@ script: - 'if [ "${TEST-}" = true ]; then npm run tests-only ; fi' - 'if [ -n "${KARMA-}" ]; then npm run tests-karma ; fi' - 'if [ -n "${COVERAGE-}" ] && [ "${TRAVIS_BRANCH-}" = "master" ]; then npm run cover; cat ./coverage/lcov.info | ./node_modules/.bin/coveralls ; fi' +install: + - 'if [ -n "${LINT-}" ]; then npm install --legacy-bundling ; else npm install ; fi' env: global: - TEST=true From bc69b89ea5e91e7766d554abb7f58bee60fe8d37 Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Thu, 22 Aug 2019 18:07:08 -0700 Subject: [PATCH 486/618] [storybook:css] Fix addons webpack loader --- .storybook-css/webpack.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/.storybook-css/webpack.config.js b/.storybook-css/webpack.config.js index 88fcdd2cf2..f4a9a10970 100644 --- a/.storybook-css/webpack.config.js +++ b/.storybook-css/webpack.config.js @@ -8,6 +8,7 @@ module.exports = { use: ['style-loader', 'raw-loader', 'sass-loader'], include: [ path.resolve(__dirname, '../css/'), + /@storybook\/addon-info/, ], }, { From a7be6dfcc1305f8560155789c39bc6bc7eb97432 Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Mon, 26 Aug 2019 14:18:23 -0700 Subject: [PATCH 487/618] Fix moment date format --- test/utils/getCalendarDaySettings_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils/getCalendarDaySettings_spec.js b/test/utils/getCalendarDaySettings_spec.js index 63d6f752bb..6bd992725c 100644 --- a/test/utils/getCalendarDaySettings_spec.js +++ b/test/utils/getCalendarDaySettings_spec.js @@ -5,7 +5,7 @@ import sinon from 'sinon-sandbox'; import getCalendarDaySettings from '../../src/utils/getCalendarDaySettings'; import { BLOCKED_MODIFIER } from '../../src/constants'; -const testDay = moment('10/10/2017'); +const testDay = moment('10/10/2017', 'MM/DD/YYYY'); const expectedFormattedDay = { date: 'Tuesday, October 10, 2017' }; const testAriaLabelFormat = 'dddd, LL'; const testDaySize = 39; From f04dc59a1c4b8da28b96638370c096c61eb0665e Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Thu, 29 Aug 2019 02:02:12 -0400 Subject: [PATCH 488/618] Fix tests --- package.json | 4 ++-- test/mocha.opts | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 693b1aea0d..60137d0730 100644 --- a/package.json +++ b/package.json @@ -12,14 +12,14 @@ "build:css": "node scripts/buildCSS.js", "clean": "rimraf lib esm", "precover": "rimraf coverage && npm run react", - "cover": "cross-env NODE_ENV=test node --max-old-space-size=4096 $(which nyc) npm run mocha test", + "cover": "cross-env NODE_ENV=test node --max-old-space-size=4096 $(which nyc) npm run mocha", "lint": "eslint --ext .js,.jsx src test", "mocha": "mocha ./test/_helpers", "storybook:uninstall": "npm uninstall --no-save @storybook/react && rimraf node_modules/@storybook node_modules/react-modal node_modules/react-dom-factories", "react": "enzyme-adapter-react-install 16", "pretest": "npm run --silent lint", "pretests-only": "npm run react", - "tests-only": "npm run mocha --silent test", + "tests-only": "npm run mocha --silent", "pretests-karma": "npm run react", "tests-karma": "karma start", "test": "npm run build && npm run tests-only", diff --git a/test/mocha.opts b/test/mocha.opts index b2d2150ca3..6f1ab8e673 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,5 +1,3 @@ ---extension js ---extension jsx --require @babel/register --require airbnb-js-shims ---recursive +test/**/*.{js,jsx} From 760a72a3006ceee277011dbdb04a869c10880cb6 Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Thu, 29 Aug 2019 02:29:20 -0400 Subject: [PATCH 489/618] Fix moment formats --- test/components/CalendarDay_spec.jsx | 6 +++--- test/components/CustomizableCalendarDay_spec.jsx | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/components/CalendarDay_spec.jsx b/test/components/CalendarDay_spec.jsx index bc968fc746..79730d377b 100644 --- a/test/components/CalendarDay_spec.jsx +++ b/test/components/CalendarDay_spec.jsx @@ -56,7 +56,7 @@ describe('CalendarDay', () => { describe('aria-label', () => { const phrases = {}; - const day = moment('10/10/2017'); + const day = moment('10/10/2017', 'MM/DD/YYYY'); beforeEach(() => { phrases.chooseAvailableDate = sinon.stub().returns('chooseAvailableDate text'); @@ -166,7 +166,7 @@ describe('CalendarDay', () => { }); describe('event handlers', () => { - const day = moment('10/10/2017'); + const day = moment('10/10/2017', 'MM/DD/YYYY'); let wrapper; beforeEach(() => { @@ -204,7 +204,7 @@ describe('CalendarDay', () => { }); describe('#onKeyDown', () => { - const day = moment('10/10/2017'); + const day = moment('10/10/2017', 'MM/DD/YYYY'); let onDayClick; let wrapper; diff --git a/test/components/CustomizableCalendarDay_spec.jsx b/test/components/CustomizableCalendarDay_spec.jsx index 9096e7129f..0870c97783 100644 --- a/test/components/CustomizableCalendarDay_spec.jsx +++ b/test/components/CustomizableCalendarDay_spec.jsx @@ -57,7 +57,7 @@ describe('CustomizableCalendarDay', () => { describe('aria-label', () => { const phrases = {}; - const day = moment('10/10/2017'); + const day = moment('10/10/2017', 'MM/DD/YYYY'); beforeEach(() => { phrases.chooseAvailableDate = sinon.stub().returns('chooseAvailableDate text'); @@ -153,7 +153,7 @@ describe('CustomizableCalendarDay', () => { }); describe('event handlers', () => { - const day = moment('10/10/2017'); + const day = moment('10/10/2017', 'MM/DD/YYYY'); let wrapper; beforeEach(() => { @@ -208,7 +208,7 @@ describe('CustomizableCalendarDay', () => { }); describe('#onKeyDown', () => { - const day = moment('10/10/2017'); + const day = moment('10/10/2017', 'MM/DD/YYYY'); let onDayClick; let wrapper; From 962d5bf537e2440ea7a6acf3e99426450b7e01e9 Mon Sep 17 00:00:00 2001 From: mmarkelov Date: Thu, 29 Aug 2019 19:31:14 +0300 Subject: [PATCH 490/618] [New] `DayPickerSingleDateController`: Add onMultiplyScrollableMonths --- .../DayPickerSingleDateController.jsx | 22 ++++++++++++++++- .../DayPickerSingleDateController_spec.jsx | 24 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 36b9dd512e..6e004bb824 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -182,7 +182,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { this.onNextMonthClick = this.onNextMonthClick.bind(this); this.onMonthChange = this.onMonthChange.bind(this); this.onYearChange = this.onYearChange.bind(this); - + this.onMultiplyScrollableMonths = this.onMultiplyScrollableMonths.bind(this); this.getFirstFocusableDay = this.getFirstFocusableDay.bind(this); } @@ -449,6 +449,22 @@ export default class DayPickerSingleDateController extends React.PureComponent { }); } + onMultiplyScrollableMonths() { + const { numberOfMonths, enableOutsideDays } = this.props; + const { currentMonth, visibleDays } = this.state; + + const numberOfVisibleMonths = Object.keys(visibleDays).length; + const nextMonth = currentMonth.clone().add(numberOfVisibleMonths, 'month'); + const newVisibleDays = getVisibleDays(nextMonth, numberOfMonths, enableOutsideDays, true); + + this.setState({ + visibleDays: { + ...visibleDays, + ...this.getModifiers(newVisibleDays), + }, + }); + } + getFirstFocusableDay(newMonth) { const { date, numberOfMonths } = this.props; @@ -496,14 +512,17 @@ export default class DayPickerSingleDateController extends React.PureComponent { initialVisibleMonth, date, numberOfMonths, + orientation, enableOutsideDays, } = nextProps; const initialVisibleMonthThunk = initialVisibleMonth || (date ? () => date : () => this.today); const currentMonth = initialVisibleMonthThunk(); + const withoutTransitionMonths = orientation === VERTICAL_SCROLLABLE; const visibleDays = this.getModifiers(getVisibleDays( currentMonth, numberOfMonths, enableOutsideDays, + withoutTransitionMonths, )); return { currentMonth, visibleDays }; } @@ -596,6 +615,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { onNextMonthClick={this.onNextMonthClick} onMonthChange={this.onMonthChange} onYearChange={this.onYearChange} + onMultiplyScrollableMonths={this.onMultiplyScrollableMonths} monthFormat={monthFormat} withPortal={withPortal} hidden={!focused} diff --git a/test/components/DayPickerSingleDateController_spec.jsx b/test/components/DayPickerSingleDateController_spec.jsx index 273eedd27e..6cd614a91c 100644 --- a/test/components/DayPickerSingleDateController_spec.jsx +++ b/test/components/DayPickerSingleDateController_spec.jsx @@ -1177,6 +1177,30 @@ describe('DayPickerSingleDateController', () => { const modifiers = wrapper.instance().addModifier(updatedDays, nextMonth, modifierToAdd); expect(Array.from(modifiers[nextMonthISO][nextMonthDayISO])).to.contain(modifierToAdd); }); + + it('return value now has modifier arg for day after multiplying number of months', () => { + const modifierToAdd = 'foo'; + const numberOfMonths = 2; + const nextMonth = today.clone().add(numberOfMonths, 'month'); + const nextMonthISO = toISOMonthString(nextMonth); + const nextMonthDayISO = toISODateString(nextMonth); + const updatedDays = { + [nextMonthISO]: { [nextMonthDayISO]: new Set(['bar', 'baz']) }, + }; + const wrapper = shallow(( + + )).instance(); + let modifiers = wrapper.addModifier(updatedDays, nextMonth, modifierToAdd); + expect(Array.from(modifiers[nextMonthISO][nextMonthDayISO])).to.not.contain(modifierToAdd); + wrapper.onMultiplyScrollableMonths(); + modifiers = wrapper.addModifier(updatedDays, nextMonth, modifierToAdd); + expect(Array.from(modifiers[nextMonthISO][nextMonthDayISO])).to.contain(modifierToAdd); + }); }); describe('#deleteModifier', () => { From 038bc761a6276f6fba460cdbb3dc52f57f8df153 Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Thu, 5 Sep 2019 16:52:39 -0700 Subject: [PATCH 491/618] [new][deps] Update dependencies related to react-with-styles To enable the react-with-styles upgrade to version 4 in isolation. --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 60137d0730..7351bcd90c 100644 --- a/package.json +++ b/package.json @@ -98,8 +98,8 @@ "raw-loader": "^0.5.1", "react": "^0.14 || ^15.5.4 || ^16.1.1", "react-dom": "^0.14 || ^15.5.4 || ^16.1.1", - "react-with-styles-interface-aphrodite": "^5.0.1", - "react-with-styles-interface-css-compiler": "^2.0.0", + "react-with-styles-interface-aphrodite": "^6.0.0", + "react-with-styles-interface-css-compiler": "^2.1.0", "rimraf": "^2.6.3", "safe-publish-latest": "^1.1.3", "sass-loader": "^7.2.0", @@ -124,9 +124,9 @@ "react-moment-proptypes": "^1.6.0", "react-outside-click-handler": "^1.2.4", "react-portal": "^4.2.0", - "react-with-direction": "^1.3.0", + "react-with-direction": "^1.3.1", "react-with-styles": "^3.2.3", - "react-with-styles-interface-css": "^4.0.3" + "react-with-styles-interface-css": "^5.0.0" }, "peerDependencies": { "moment": "^2.18.1", From 48b523d083937e7c55fee8ea46d3751f2e3e23d2 Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Thu, 29 Aug 2019 20:29:51 -0400 Subject: [PATCH 492/618] [breaking][deps] Update all dependencies - Drop support for node < 6 - Update: @storybook, webpack, react-with-styles, react-with-direction, react-with-styles-interface-aphrodite, react-with-styles-interface-css, react-with-styles-interface-css-compiler --- .storybook/config.js | 5 +++-- package.json | 11 ++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.storybook/config.js b/.storybook/config.js index 84d2cd28e9..3882b60251 100644 --- a/.storybook/config.js +++ b/.storybook/config.js @@ -1,7 +1,8 @@ import React from 'react'; + if (process.env.NODE_ENV !== 'production') { - const { whyDidYouUpdate } = require('why-did-you-update'); - whyDidYouUpdate(React); + const whyDidYouRender = require('@welldone-software/why-did-you-render'); + whyDidYouRender(React); } import moment from 'moment'; diff --git a/package.json b/package.json index 7351bcd90c..f3d30ce2fc 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,9 @@ "@storybook/addon-info": "^5.1.11", "@storybook/addon-links": "^5.1.11", "@storybook/addon-options": "^5.1.11", + "@storybook/addons": "^5.1.11", "@storybook/react": "^5.1.11", + "@welldone-software/why-did-you-render": "^3.3.1", "airbnb-js-shims": "^2.2.0", "aphrodite": "^2.3.1", "babel-loader": "^8.0.6", @@ -99,7 +101,7 @@ "react": "^0.14 || ^15.5.4 || ^16.1.1", "react-dom": "^0.14 || ^15.5.4 || ^16.1.1", "react-with-styles-interface-aphrodite": "^6.0.0", - "react-with-styles-interface-css-compiler": "^2.1.0", + "react-with-styles-interface-css-compiler": "^2.2.0", "rimraf": "^2.6.3", "safe-publish-latest": "^1.1.3", "sass-loader": "^7.2.0", @@ -107,8 +109,7 @@ "sinon-sandbox": "^2.0.5", "style-loader": "^0.20.3", "typescript": "*", - "webpack": "^4.31.0", - "why-did-you-update": "^1.0.6" + "webpack": "^4.31.0" }, "dependencies": { "@babel/runtime": "^7.5.5", @@ -125,8 +126,8 @@ "react-outside-click-handler": "^1.2.4", "react-portal": "^4.2.0", "react-with-direction": "^1.3.1", - "react-with-styles": "^3.2.3", - "react-with-styles-interface-css": "^5.0.0" + "react-with-styles": "^4.0.0", + "react-with-styles-interface-css": "^6.0.0" }, "peerDependencies": { "moment": "^2.18.1", From a22b2981eb8e2aed58aca9dd2d615b6c0eac7b21 Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Wed, 4 Sep 2019 16:14:49 -0700 Subject: [PATCH 493/618] [breaking] Set peer dep react-with-direction ^1.3.0 -> ^1.3.1 This is required to use react-with-styles@4.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f3d30ce2fc..4f9a11e3e2 100644 --- a/package.json +++ b/package.json @@ -133,7 +133,7 @@ "moment": "^2.18.1", "react": "^0.14 || ^15.5.4 || ^16.1.1", "react-dom": "^0.14 || ^15.5.4 || ^16.1.1", - "react-with-direction": "^1.3.0" + "react-with-direction": "^1.3.1" }, "greenkeeper": { "ignore": [ From 9782a08b53617e2c22da66732340065279b3c241 Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Mon, 9 Sep 2019 16:40:53 -0700 Subject: [PATCH 494/618] [breaking][deps] Move @babel/runtime to peer deps --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 4f9a11e3e2..d50fbdaa4b 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "@babel/cli": "^7.5.5", "@babel/core": "^7.5.5", "@babel/register": "^7.5.5", + "@babel/runtime": "^7.0.0", "@storybook/addon-actions": "^5.1.11", "@storybook/addon-info": "^5.1.11", "@storybook/addon-links": "^5.1.11", @@ -112,7 +113,6 @@ "webpack": "^4.31.0" }, "dependencies": { - "@babel/runtime": "^7.5.5", "airbnb-prop-types": "^2.15.0", "consolidated-events": "^1.1.1 || ^2.0.0", "enzyme-shallow-equal": "^1.0.0", @@ -130,6 +130,7 @@ "react-with-styles-interface-css": "^6.0.0" }, "peerDependencies": { + "@babel/runtime": "^7.0.0", "moment": "^2.18.1", "react": "^0.14 || ^15.5.4 || ^16.1.1", "react-dom": "^0.14 || ^15.5.4 || ^16.1.1", From 43086e2fabce47d56d1ed66d8de324ec866d1c25 Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Mon, 9 Sep 2019 16:41:22 -0700 Subject: [PATCH 495/618] [dev] Fix linting --- .eslintrc | 2 ++ package.json | 1 + 2 files changed, 3 insertions(+) diff --git a/.eslintrc b/.eslintrc index cd5524f872..454e2a80d0 100644 --- a/.eslintrc +++ b/.eslintrc @@ -15,6 +15,8 @@ "node": true }, + "parser": "babel-eslint", + "rules": { "react/forbid-foreign-prop-types": 2, // For babel-plugin-transform-react-remove-prop-types diff --git a/package.json b/package.json index d50fbdaa4b..d7c4724721 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "@welldone-software/why-did-you-render": "^3.3.1", "airbnb-js-shims": "^2.2.0", "aphrodite": "^2.3.1", + "babel-eslint": "^10.0.3", "babel-loader": "^8.0.6", "babel-plugin-inline-react-svg": "^1.1.0", "babel-plugin-inline-svg": "^1.0.1", From db4e194b65736d36d8baa7c1610596bae030bfee Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Mon, 9 Sep 2019 16:42:00 -0700 Subject: [PATCH 496/618] [fix][compile] Ensure root component is always present --- scripts/buildCSS.js | 6 ++++++ scripts/renderAllComponents.jsx | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/scripts/buildCSS.js b/scripts/buildCSS.js index ef3ab0a769..fc77611196 100644 --- a/scripts/buildCSS.js +++ b/scripts/buildCSS.js @@ -20,6 +20,12 @@ registerCSSInterfaceWithDefaultTheme(); const path = './scripts/renderAllComponents.jsx'; const CSS = compileCSS(path); +if (CSS === '') { + throw new Error('Failed to compile CSS'); +} else { + console.log('CSS compilation complete.'); +} + const format = new CleanCSS({ level: optimizeForProduction ? 2 : 0, format: 'beautify', diff --git a/scripts/renderAllComponents.jsx b/scripts/renderAllComponents.jsx index a93b079b95..4da9dedba6 100644 --- a/scripts/renderAllComponents.jsx +++ b/scripts/renderAllComponents.jsx @@ -5,6 +5,13 @@ import DateRangePickerWrapper from '../examples/DateRangePickerWrapper'; import SingleDatePickerWrapper from '../examples/SingleDatePickerWrapper'; import PresetDateRangePickerWrapper from '../examples/PresetDateRangePicker'; +if (!document.getElementById('root')) { + // Make sure the #root element is defined + const root = document.createElement('div'); + root.id = 'root'; + document.body.appendChild(root); +} + function App() { return (
From e365eb3542f04517a3b069f9de7edfe967f2c158 Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Mon, 9 Sep 2019 17:44:16 -0700 Subject: [PATCH 497/618] Version 21.0.0 --- CHANGELOG.md | 14 ++++++++++++++ package.json | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d443c6a37..a9d88ab127 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## Unreleased + +## 21.0.0 + +- [breaking] [deps] Update react-with-styles and other deps ([#1761](https://github.com/airbnb/react-dates/pull/1761) +- [new] [deps] Update dependencies related to react-with-styles ([#1775](https://github.com/airbnb/react-dates/pull/1775)) +- [new] `DayPickerSingleDateController`: Add onMultiplyScrollableMonths ([#1770](https://github.com/airbnb/react-dates/pull/1770)) +- [dev] Fix moment date formats ([#1767](https://github.com/airbnb/react-dates/pull/1767)) +- [dev] Fix addons webpack loader, Fix tests ([#1764](https://github.com/airbnb/react-dates/pull/1764)) +- [dev] build: fix linting (refs eslint/eslint#12119) +- [new] [deps] update `@babel/runtime`, `airbnb-prop-types`, `object.values`, `prop-types`, `react-outside-click-handler`, `react-portal` +- [dev] [deps] update `@babel/*`, `@storybook/*`, `babel-plugin-inline-svg`, `babel-plugin-istanbul`, `babel-preset-airbnb`, `eslint-config-airbnb` to v18 (plus peer deps), `eslint` to v6; `eslint-plugin-react-with-styles`, `karma`, `karma-firefox-launcher`, `mocha`, `safe-publish-latest`, `sass-loader`, `sinon`, `sinon-sandbox`, `coveralls`, `enzyme-adapter-react-helper` +- [fix] Remove all direct imports of css in favor of injected prop ([#1758](https://github.com/airbnb/react-dates/pull/1758)) + ## 20.3.0 - [fix] Optimize setState dayPickerContainerStyles in responsivizePickerPosition ([#1735](https://github.com/airbnb/react-dates/pull/1735)) - [fix] Stop calendar blinking on DateRangePickerInput focus switch (fixes #1523) ([#1553](https://github.com/airbnb/react-dates/pull/1553)) diff --git a/package.json b/package.json index d7c4724721..6db4f3d781 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "20.3.0", + "version": "21.0.0", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From 47ca2f943ab80cf679200045562af68a8e3a6645 Mon Sep 17 00:00:00 2001 From: Conor Fischer Date: Mon, 26 Aug 2019 13:50:35 -0500 Subject: [PATCH 498/618] Move FontWeight style in DateInput to DefaultTheme --- src/components/DateInput.jsx | 2 +- src/theme/DefaultTheme.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index a119ac91c2..59d0acef95 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -305,7 +305,7 @@ export default withStyles(({ }, DateInput_input: { - fontWeight: 200, + fontWeight: font.input.weight, fontSize: font.input.size, lineHeight: font.input.lineHeight, color: color.text, diff --git a/src/theme/DefaultTheme.js b/src/theme/DefaultTheme.js index d5071efd6e..d839391497 100644 --- a/src/theme/DefaultTheme.js +++ b/src/theme/DefaultTheme.js @@ -184,6 +184,7 @@ export default { captionSize: 18, input: { size: 19, + weight: 200, lineHeight: '24px', size_small: 15, lineHeight_small: '18px', From e6ced9d5a55c6eedf95baeb2707f0945eb0117ed Mon Sep 17 00:00:00 2001 From: Andrew Huth Date: Mon, 16 Sep 2019 10:16:38 -0700 Subject: [PATCH 499/618] Combine labelled DayPicker container elements This is to fix an issue [1] where TalkBack (the screen reader for Android devices) reads out the entire contents of the calendar when focused. This includes every single day in the displayed calendar, which is super painful for users using TalkBack. What's happening is that there is a wrapping div with a tabindex of -1 and no label or title that the screen reader can read. Because there's no label, TalkBack reads out its content, which happens to be all of the calendar days. VoiceOver on iOS doesn't have this problem because it can't (currently) focus on div containers that have children with content. To fix, I've moved the current accessibility labelling, which was on a wrapping div higher up the DOM tree, onto the "focusable" wrapping div. VoiceOver on iOS still won't focus on and read it, but TalkBack on Android will read it correctly. There is one downside to this, however. When Talkback reads out the labe, it ends with "double tap to activate". That doesn't do anything, though. 1. https://jira.airbnb.biz/browse/PRODUCT-94500 --- src/components/DayPicker.jsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index 24f8a53298..1b7e3fae3d 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -1036,9 +1036,6 @@ class DayPicker extends React.PureComponent { return (
{ e.stopPropagation(); }} onKeyDown={this.onKeyDown} onMouseUp={() => { this.setState({ withMouseInteractions: true }); }} - role="region" tabIndex={-1} + role="application" + aria-roledescription={phrases.roleDescription} + aria-label={phrases.calendarLabel} > {!verticalScrollable && this.renderNavigation()} From 21ec280f7714ee836a8d81482e6a9569c76b1e3c Mon Sep 17 00:00:00 2001 From: casey_klimkowsky Date: Tue, 17 Sep 2019 12:32:13 -0700 Subject: [PATCH 500/618] Add a render function for customizable week header text --- src/components/DateRangePicker.jsx | 3 +++ src/components/DayPicker.jsx | 8 +++++++- src/components/DayPickerRangeController.jsx | 4 ++++ .../DayPickerSingleDateController.jsx | 4 ++++ src/components/SingleDatePicker.jsx | 3 +++ src/shapes/DateRangePickerShape.js | 1 + src/shapes/SingleDatePickerShape.js | 1 + stories/DayPicker.js | 7 +++++++ test/components/DayPicker_spec.jsx | 17 +++++++++++++++++ 9 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index d1af316264..1f3bd61b6e 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -73,6 +73,7 @@ const defaultProps = { // calendar presentation and interaction related props renderMonthText: null, + renderWeekHeaderElement: null, orientation: HORIZONTAL_ORIENTATION, anchorDirection: ANCHOR_LEFT, openDirection: OPEN_DOWN, @@ -401,6 +402,7 @@ class DateRangePicker extends React.PureComponent { orientation, monthFormat, renderMonthText, + renderWeekHeaderElement, navPrev, navNext, onPrevMonthClick, @@ -497,6 +499,7 @@ class DateRangePicker extends React.PureComponent { endDateOffset={endDateOffset} monthFormat={monthFormat} renderMonthText={renderMonthText} + renderWeekHeaderElement={renderWeekHeaderElement} withPortal={withAnyPortal} daySize={daySize} initialVisibleMonth={initialVisibleMonthThunk} diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index 1b7e3fae3d..55c13507d9 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -88,6 +88,7 @@ const propTypes = forbidExtraProps({ // month props renderMonthText: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), renderMonthElement: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), + renderWeekHeaderElement: PropTypes.func, // day props modifiers: PropTypes.objectOf(PropTypes.objectOf(ModifiersShape)), @@ -149,6 +150,7 @@ export const defaultProps = { // month props renderMonthText: null, renderMonthElement: null, + renderWeekHeaderElement: null, // day props modifiers: {}, @@ -867,11 +869,15 @@ class DayPicker extends React.PureComponent { daySize, horizontalMonthPadding, orientation, + renderWeekHeaderElement, styles, css, } = this.props; + const { calendarMonthWidth } = this.state; + const verticalScrollable = orientation === VERTICAL_SCROLLABLE; + const horizontalStyle = { left: index * calendarMonthWidth, }; @@ -889,7 +895,7 @@ class DayPicker extends React.PureComponent { const weekHeaders = this.getWeekHeaders(); const header = weekHeaders.map((day) => (
  • - {day} + {renderWeekHeaderElement ? renderWeekHeaderElement(day) : {day}}
  • )); diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 42b0729e45..3e6f1cd2f2 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -65,6 +65,7 @@ const propTypes = forbidExtraProps({ // DayPicker props renderMonthText: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), renderMonthElement: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), + renderWeekHeaderElement: PropTypes.func, enableOutsideDays: PropTypes.bool, numberOfMonths: PropTypes.number, orientation: ScrollableOrientationShape, @@ -131,6 +132,7 @@ const defaultProps = { // DayPicker props renderMonthText: null, + renderWeekHeaderElement: null, enableOutsideDays: false, numberOfMonths: 1, orientation: HORIZONTAL_ORIENTATION, @@ -1131,6 +1133,7 @@ export default class DayPickerRangeController extends React.PureComponent { orientation, monthFormat, renderMonthText, + renderWeekHeaderElement, navPrev, navNext, noNavButtons, @@ -1188,6 +1191,7 @@ export default class DayPickerRangeController extends React.PureComponent { onMultiplyScrollableMonths={this.onMultiplyScrollableMonths} monthFormat={monthFormat} renderMonthText={renderMonthText} + renderWeekHeaderElement={renderWeekHeaderElement} withPortal={withPortal} hidden={!focusedInput} initialVisibleMonth={() => currentMonth} diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 6e004bb824..8d7f6b2c0c 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -47,6 +47,7 @@ const propTypes = forbidExtraProps({ // DayPicker props renderMonthText: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), renderMonthElement: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), + renderWeekHeaderElement: PropTypes.func, enableOutsideDays: PropTypes.bool, numberOfMonths: PropTypes.number, orientation: ScrollableOrientationShape, @@ -103,6 +104,7 @@ const defaultProps = { // DayPicker props renderMonthText: null, + renderWeekHeaderElement: null, enableOutsideDays: false, numberOfMonths: 1, orientation: HORIZONTAL_ORIENTATION, @@ -570,6 +572,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { orientation, monthFormat, renderMonthText, + renderWeekHeaderElement, navPrev, navNext, onOutsideClick, @@ -626,6 +629,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { navPrev={navPrev} navNext={navNext} renderMonthText={renderMonthText} + renderWeekHeaderElement={renderWeekHeaderElement} renderCalendarDay={renderCalendarDay} renderDayContents={renderDayContents} renderCalendarInfo={renderCalendarInfo} diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index 6e4b40109f..6de560edb4 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -98,6 +98,7 @@ const defaultProps = { // month presentation and interaction related props renderMonthText: null, + renderWeekHeaderElement: null, // day presentation and interaction related props renderCalendarDay: undefined, @@ -404,6 +405,7 @@ class SingleDatePicker extends React.PureComponent { keepOpenOnDateSelect, initialVisibleMonth, renderMonthText, + renderWeekHeaderElement, renderCalendarDay, renderDayContents, renderCalendarInfo, @@ -480,6 +482,7 @@ class SingleDatePicker extends React.PureComponent { onNextMonthClick={onNextMonthClick} onClose={onClose} renderMonthText={renderMonthText} + renderWeekHeaderElement={renderWeekHeaderElement} renderCalendarDay={renderCalendarDay} renderDayContents={renderDayContents} renderCalendarInfo={renderCalendarInfo} diff --git a/src/shapes/DateRangePickerShape.js b/src/shapes/DateRangePickerShape.js index c35f3db742..16787084ad 100644 --- a/src/shapes/DateRangePickerShape.js +++ b/src/shapes/DateRangePickerShape.js @@ -53,6 +53,7 @@ export default { // calendar presentation and interaction related props renderMonthText: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), renderMonthElement: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), + renderWeekHeaderElement: PropTypes.func, orientation: OrientationShape, anchorDirection: anchorDirectionShape, openDirection: openDirectionShape, diff --git a/src/shapes/SingleDatePickerShape.js b/src/shapes/SingleDatePickerShape.js index e90fb2e9f1..181f56b301 100644 --- a/src/shapes/SingleDatePickerShape.js +++ b/src/shapes/SingleDatePickerShape.js @@ -43,6 +43,7 @@ export default { // calendar presentation and interaction related props renderMonthText: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), renderMonthElement: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), + renderWeekHeaderElement: PropTypes.func, orientation: OrientationShape, anchorDirection: anchorDirectionShape, openDirection: openDirectionShape, diff --git a/stories/DayPicker.js b/stories/DayPicker.js index c0c53b1ab0..4cf84eaa26 100644 --- a/stories/DayPicker.js +++ b/stories/DayPicker.js @@ -153,6 +153,13 @@ storiesOf('DayPicker', module) )} /> ))) + .add('with custom week header text', withInfo()(() => ( + ( + {day.toUpperCase()} + )} + /> + ))) .add('with custom week day format', withInfo()(() => ( { }); }); + describe('props.renderWeekHeaderElement', () => { + it('there are 7 custom elements on each .DayPicker__week-header class', () => { + const testWeekHeaderClassName = 'test-week-header'; + const wrapper = shallow( + ({day}) + } + />, + ).dive(); + const weekHeaders = wrapper.find('.DayPicker__week-header'); + weekHeaders.forEach((weekHeader) => { + expect(weekHeader.find(`.${testWeekHeaderClassName}`)).to.have.lengthOf(7); + }); + }); + }); + describe('props.orientation === HORIZONTAL_ORIENTATION', () => { it('props.numberOfMonths ul (week header) elements exists', () => { const NUM_OF_MONTHS = 3; From c297469ddc69ae1313dc13dbbedf46665d6b05a7 Mon Sep 17 00:00:00 2001 From: Andrew Huth Date: Wed, 18 Sep 2019 11:39:49 -0700 Subject: [PATCH 501/618] Version 21.1.0 --- CHANGELOG.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 937e26553e..ddd64c4789 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ ## Unreleased +## 21.1.0 + +- [fix] `DayPicker`: week headers: use the passed-in moment object’s instance, to support localy ([#1577](https://github.com/airbnb/react-dates/pull/1577)) +- [fix] Combine labelled DayPicker container elements ([#1783](https://github.com/airbnb/react-dates/pull/1783)) +- [new] Add a render function for customizable week header text ([#1787](https://github.com/airbnb/react-dates/pull/1787)) + ## 21.0.1 - [fix] [deps] Update react-with-styles ^4.0.0 -> ^4.0.1 ([#1781](https://github.com/airbnb/react-dates/pull/1781)) diff --git a/package.json b/package.json index d5c606043d..9cafa75eeb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "21.0.1", + "version": "21.1.0", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From b8456dca9f249978fd825f014f56ef416900bfa0 Mon Sep 17 00:00:00 2001 From: v47 Date: Thu, 19 Sep 2019 02:22:56 +0200 Subject: [PATCH 502/618] Add minDate and maxDate props to DateRangePicker --- README.md | 2 ++ src/components/DateRangePicker.jsx | 6 ++++++ src/shapes/DateRangePickerShape.js | 2 ++ stories/DateRangePicker.js | 7 +++++++ 4 files changed, 17 insertions(+) diff --git a/README.md b/README.md index c79b204f1e..524783951e 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,8 @@ transitionDuration: nonNegativeInteger, // milliseconds renderCalendarDay: PropTypes.func, renderDayContents: PropTypes.func, minimumNights: PropTypes.number, +minDate: momentPropTypes.momentObj, +maxDate: momentPropTypes.momentObj, enableOutsideDays: PropTypes.bool, isDayBlocked: PropTypes.func, isOutsideRange: PropTypes.func, diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 1f3bd61b6e..f76ea5463c 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -115,6 +115,8 @@ const defaultProps = { isDayBlocked: () => false, isOutsideRange: (day) => !isInclusivelyAfterDay(day, moment()), isDayHighlighted: () => false, + minDate: undefined, + maxDate: undefined, // internationalization displayFormat: () => moment.localeData().longDateFormat('L'), @@ -418,6 +420,8 @@ class DateRangePicker extends React.PureComponent { startDateOffset, endDate, endDateOffset, + minDate, + maxDate, minimumNights, keepOpenOnDateSelect, renderCalendarDay, @@ -497,6 +501,8 @@ class DateRangePicker extends React.PureComponent { startDateOffset={startDateOffset} endDate={endDate} endDateOffset={endDateOffset} + minDate={minDate} + maxDate={maxDate} monthFormat={monthFormat} renderMonthText={renderMonthText} renderWeekHeaderElement={renderWeekHeaderElement} diff --git a/src/shapes/DateRangePickerShape.js b/src/shapes/DateRangePickerShape.js index 16787084ad..69eb5ad780 100644 --- a/src/shapes/DateRangePickerShape.js +++ b/src/shapes/DateRangePickerShape.js @@ -87,6 +87,8 @@ export default { renderCalendarDay: PropTypes.func, renderDayContents: PropTypes.func, minimumNights: PropTypes.number, + minDate: momentPropTypes.momentObj, + maxDate: momentPropTypes.momentObj, enableOutsideDays: PropTypes.bool, isDayBlocked: PropTypes.func, isOutsideRange: PropTypes.func, diff --git a/stories/DateRangePicker.js b/stories/DateRangePicker.js index 732320f66a..0353260f59 100644 --- a/stories/DateRangePicker.js +++ b/stories/DateRangePicker.js @@ -117,4 +117,11 @@ storiesOf('DateRangePicker (DRP)', module) orientation={VERTICAL_ORIENTATION} verticalHeight={568} /> + ))) + .add('with navigation blocked (minDate and maxDate)', withInfo()(() => ( + ))); From 4c29575b093081bf31c957579846e0b7b97a5867 Mon Sep 17 00:00:00 2001 From: v47 Date: Thu, 19 Sep 2019 23:08:50 +0200 Subject: [PATCH 503/618] Add tests for minDate and maxDate props in DateRangePicker --- test/components/DateRangePicker_spec.jsx | 30 ++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/components/DateRangePicker_spec.jsx b/test/components/DateRangePicker_spec.jsx index 645b7e482a..deadee3453 100644 --- a/test/components/DateRangePicker_spec.jsx +++ b/test/components/DateRangePicker_spec.jsx @@ -741,4 +741,34 @@ describe('DateRangePicker', () => { }); }); }); + + describe('minDate and maxDate props', () => { + describe('minDate is passed in', () => { + it('Should pass minDate to DayPickerRangeController', () => { + const minDate = moment('2018-10-19'); + const wrapper = shallow(( + + )).dive(); + expect(wrapper.find(DayPickerRangeController).props().minDate).to.equal(minDate); + }); + }); + + describe('maxDate is passed in', () => { + it('Should pass maxDate to DayPickerRangeController', () => { + const maxDate = moment('2018-12-19'); + const wrapper = shallow(( + + )).dive(); + expect(wrapper.find(DayPickerRangeController).props().maxDate).to.equal(maxDate); + }); + }); + }); }); From 5e569b52870fdeb6d511cdf253736e2f3288ec91 Mon Sep 17 00:00:00 2001 From: v47 Date: Fri, 20 Sep 2019 15:48:04 +0200 Subject: [PATCH 504/618] fix for getWeekHeaders(), prevents it from changing state.currentMonth --- src/components/DayPicker.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index 55c13507d9..8c295390bc 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -591,7 +591,7 @@ class DayPicker extends React.PureComponent { const weekHeaders = []; for (let i = 0; i < 7; i += 1) { - weekHeaders.push(currentMonth.day((i + firstDayOfWeek) % 7).format(weekDayFormat)); + weekHeaders.push(currentMonth.clone().day((i + firstDayOfWeek) % 7).format(weekDayFormat)); } return weekHeaders; From 5584f822fdcc3fa266deff62756ac887b4c0d2af Mon Sep 17 00:00:00 2001 From: v47 Date: Mon, 23 Sep 2019 12:06:36 +0200 Subject: [PATCH 505/618] [Tests] `DayPicker`: unit test for getWeekHeaders() --- test/components/DayPicker_spec.jsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/components/DayPicker_spec.jsx b/test/components/DayPicker_spec.jsx index 63fb8e506c..f3df0e776b 100644 --- a/test/components/DayPicker_spec.jsx +++ b/test/components/DayPicker_spec.jsx @@ -3,6 +3,7 @@ import moment from 'moment/min/moment-with-locales'; import { expect } from 'chai'; import sinon from 'sinon-sandbox'; import { mount, shallow } from 'enzyme'; +import { cloneDeep } from 'lodash'; import * as isDayVisible from '../../src/utils/isDayVisible'; @@ -871,6 +872,23 @@ describe('DayPicker', () => { }); }); + describe('#getWeekHeaders', () => { + it('returns unmutated weekday headers for currentMonth in a future', () => { + sinon.stub(PureDayPicker.prototype, 'render'); + + const getWeekHeadersSpy = sinon.spy(PureDayPicker.prototype, 'getWeekHeaders'); + const INITIAL_MONTH = moment().add(2, 'Months').week(3).weekday(3); + const wrapper = shallow( INITIAL_MONTH} />).dive(); + const instance = wrapper.instance(); + const state = cloneDeep(wrapper.state()); + + expect(instance.getWeekHeaders()).to.be.eql(INITIAL_MONTH.localeData().weekdaysMin()); + expect(instance.state).not.to.equal(state); + expect(instance.state).to.eql(state); + expect(getWeekHeadersSpy).to.have.property('callCount', 1); + }); + }); + describe.skip('life cycle methods', () => { let adjustDayPickerHeightSpy; beforeEach(() => { From 465818267162952794e30e4745955ca174de3b54 Mon Sep 17 00:00:00 2001 From: casey_klimkowsky Date: Tue, 17 Sep 2019 13:05:58 -0700 Subject: [PATCH 506/618] Add a render function for keyboard shortcuts panel --- examples/DayPickerRangeControllerWrapper.jsx | 9 +- src/components/DayPicker.jsx | 4 + src/components/DayPickerKeyboardShortcuts.jsx | 96 +++++++------- src/components/DayPickerRangeController.jsx | 4 + stories/DayPickerRangeController.js | 118 +++++++++++++++--- .../DayPickerKeyboardShortcuts_spec.jsx | 11 ++ 6 files changed, 180 insertions(+), 62 deletions(-) diff --git a/examples/DayPickerRangeControllerWrapper.jsx b/examples/DayPickerRangeControllerWrapper.jsx index 340f419201..fdf16ce9cf 100644 --- a/examples/DayPickerRangeControllerWrapper.jsx +++ b/examples/DayPickerRangeControllerWrapper.jsx @@ -50,6 +50,7 @@ const propTypes = forbidExtraProps({ renderCalendarDay: PropTypes.func, renderDayContents: PropTypes.func, renderKeyboardShortcutsButton: PropTypes.func, + renderKeyboardShortcutsPanel: PropTypes.func, // i18n monthFormat: PropTypes.string, @@ -73,7 +74,7 @@ const defaultProps = { renderDayContents: null, minimumNights: 1, isDayBlocked: () => false, - isOutsideRange: day => !isInclusivelyAfterDay(day, moment()), + isOutsideRange: (day) => !isInclusivelyAfterDay(day, moment()), isDayHighlighted: () => false, enableOutsideDays: false, @@ -90,6 +91,7 @@ const defaultProps = { renderMonthText: null, renderMonthElement: null, renderKeyboardShortcutsButton: undefined, + renderKeyboardShortcutsPanel: undefined, // navigation related props navPrev: null, @@ -143,12 +145,13 @@ class DayPickerRangeControllerWrapper extends React.Component { return (
    - {showInputs && + {showInputs + && (
    - } + )} )}
    diff --git a/src/components/DayPickerKeyboardShortcuts.jsx b/src/components/DayPickerKeyboardShortcuts.jsx index e257e2097e..cbef8297f7 100644 --- a/src/components/DayPickerKeyboardShortcuts.jsx +++ b/src/components/DayPickerKeyboardShortcuts.jsx @@ -23,6 +23,7 @@ const propTypes = forbidExtraProps({ closeKeyboardShortcutsPanel: PropTypes.func, phrases: PropTypes.shape(getPhrasePropTypes(DayPickerKeyboardShortcutsPhrases)), renderKeyboardShortcutsButton: PropTypes.func, + renderKeyboardShortcutsPanel: PropTypes.func, }); const defaultProps = { @@ -33,6 +34,7 @@ const defaultProps = { closeKeyboardShortcutsPanel() {}, phrases: DayPickerKeyboardShortcutsPhrases, renderKeyboardShortcutsButton: undefined, + renderKeyboardShortcutsPanel: undefined, }; function getKeyboardShortcuts(phrases) { @@ -168,6 +170,7 @@ class DayPickerKeyboardShortcuts extends React.PureComponent { styles, phrases, renderKeyboardShortcutsButton, + renderKeyboardShortcutsPanel, } = this.props; const toggleButtonText = showKeyboardShortcutsPanel @@ -216,51 +219,60 @@ class DayPickerKeyboardShortcuts extends React.PureComponent { )} - {showKeyboardShortcutsPanel && ( -
    + {showKeyboardShortcutsPanel + && (renderKeyboardShortcutsPanel ? ( + renderKeyboardShortcutsPanel({ + closeButtonAriaLabel: phrases.hideKeyboardShortcutsPanel, + keyboardShortcuts: this.keyboardShortcuts, + onCloseButtonClick: closeKeyboardShortcutsPanel, + onKeyDown: this.onKeyDown, + title: phrases.keyboardShortcuts, + }) + ) : ( - - - -
      - {this.keyboardShortcuts.map(({ unicode, label, action }) => ( - - ))} -
    -
    - )} + ))}
    ); } diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 3e6f1cd2f2..483358c6dd 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -88,6 +88,7 @@ const propTypes = forbidExtraProps({ renderDayContents: PropTypes.func, renderCalendarInfo: PropTypes.func, renderKeyboardShortcutsButton: PropTypes.func, + renderKeyboardShortcutsPanel: PropTypes.func, calendarInfoPosition: CalendarInfoPositionShape, firstDayOfWeek: DayOfWeekShape, verticalHeight: nonNegativeInteger, @@ -154,6 +155,7 @@ const defaultProps = { renderCalendarInfo: null, renderMonthElement: null, renderKeyboardShortcutsButton: undefined, + renderKeyboardShortcutsPanel: undefined, calendarInfoPosition: INFO_POSITION_BOTTOM, firstDayOfWeek: null, verticalHeight: null, @@ -1142,6 +1144,7 @@ export default class DayPickerRangeController extends React.PureComponent { enableOutsideDays, firstDayOfWeek, renderKeyboardShortcutsButton, + renderKeyboardShortcutsPanel, hideKeyboardShortcutsPanel, daySize, focusedInput, @@ -1207,6 +1210,7 @@ export default class DayPickerRangeController extends React.PureComponent { renderCalendarInfo={renderCalendarInfo} renderMonthElement={renderMonthElement} renderKeyboardShortcutsButton={renderKeyboardShortcutsButton} + renderKeyboardShortcutsPanel={renderKeyboardShortcutsPanel} calendarInfoPosition={calendarInfoPosition} firstDayOfWeek={firstDayOfWeek} hideKeyboardShortcutsPanel={hideKeyboardShortcutsPanel} diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index fa189f7ef4..1aa35dcb81 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -9,6 +9,9 @@ import InfoPanelDecorator, { monospace } from './InfoPanelDecorator'; import isSameDay from '../src/utils/isSameDay'; import isInclusivelyAfterDay from '../src/utils/isInclusivelyAfterDay'; +import CloseButton from '../src/components/CloseButton'; +import KeyboardShortcutRow from '../src/components/KeyboardShortcutRow'; + import { VERTICAL_ORIENTATION, VERTICAL_SCROLLABLE } from '../src/constants'; import DayPickerRangeControllerWrapper from '../examples/DayPickerRangeControllerWrapper'; @@ -41,7 +44,7 @@ const TestPrevIcon = () => ( position: 'absolute', top: '20px', }} - tabindex="0" + tabIndex="0" > Prev
    @@ -58,7 +61,7 @@ const TestNextIcon = () => ( right: '22px', top: '20px', }} - tabindex="0" + tabIndex="0" > Next
    @@ -119,6 +122,80 @@ function renderKeyboardShortcutsButton(buttonProps) { ); } +function renderKeyboardShortcutsPanel(panelProps) { + const { + closeButtonAriaLabel, + keyboardShortcuts, + onCloseButtonClick, + onKeyDown, + title, + } = panelProps; + + const PANEL_PADDING_PX = 50; + + return ( +
    +
    +
    + +
    + {title} +
    + {keyboardShortcuts.map(({ action: keyboardShortcutAction, label, unicode }) => ( + + ))} +
    +
    + ); +} + const datesList = [ moment(), moment().add(1, 'days'), @@ -144,8 +221,8 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - startDateOffset={day => day.subtract(3, 'days')} - endDateOffset={day => day.add(3, 'days')} + startDateOffset={(day) => day.subtract(3, 'days')} + endDateOffset={(day) => day.add(3, 'days')} /> ))) .add('with 45 days range selection', withInfo()(() => ( @@ -153,8 +230,8 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - startDateOffset={day => day.subtract(22, 'days')} - endDateOffset={day => day.add(22, 'days')} + startDateOffset={(day) => day.subtract(22, 'days')} + endDateOffset={(day) => day.add(22, 'days')} /> ))) .add('with 4 days after today range selection', withInfo()(() => ( @@ -162,7 +239,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - endDateOffset={day => day.add(4, 'days')} + endDateOffset={(day) => day.add(4, 'days')} /> ))) .add('with current week range selection', withInfo()(() => ( @@ -170,8 +247,8 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - startDateOffset={day => day.startOf('week')} - endDateOffset={day => day.endOf('week')} + startDateOffset={(day) => day.startOf('week')} + endDateOffset={(day) => day.endOf('week')} /> ))) .add('with custom inputs', withInfo()(() => ( @@ -360,9 +437,8 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - isOutsideRange={day => !isInclusivelyAfterDay(day, moment()) - || isInclusivelyAfterDay(day, moment().add(2, 'weeks')) - } + isOutsideRange={(day) => !isInclusivelyAfterDay(day, moment()) + || isInclusivelyAfterDay(day, moment().add(2, 'weeks'))} /> ))) .add('with some blocked dates', withInfo()(() => ( @@ -370,7 +446,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - isDayBlocked={day1 => datesList.some(day2 => isSameDay(day1, day2))} + isDayBlocked={(day1) => datesList.some((day2) => isSameDay(day1, day2))} /> ))) .add('with some highlighted dates', withInfo()(() => ( @@ -378,7 +454,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - isDayHighlighted={day1 => datesList.some(day2 => isSameDay(day1, day2))} + isDayHighlighted={(day1) => datesList.some((day2) => isSameDay(day1, day2))} /> ))) .add('blocks fridays', withInfo()(() => ( @@ -386,7 +462,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - isDayBlocked={day => moment.weekdays(day.weekday()) === 'Friday'} + isDayBlocked={(day) => moment.weekdays(day.weekday()) === 'Friday'} /> ))) .add('with navigation blocked (minDate and maxDate)', withInfo()(() => ( @@ -403,7 +479,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - renderDayContents={day => day.format('ddd')} + renderDayContents={(day) => day.format('ddd')} /> ))) .add('with info panel', withInfo()(() => ( @@ -465,7 +541,7 @@ storiesOf('DayPickerRangeController', module) onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} getMinNightsForHoverDate={() => 2} - isDayBlocked={day1 => datesList.some(day2 => isSameDay(day1, day2))} + isDayBlocked={(day1) => datesList.some((day2) => isSameDay(day1, day2))} /> ))) .add('with custom keyboard shortcuts button', withInfo()(() => ( @@ -475,4 +551,12 @@ storiesOf('DayPickerRangeController', module) onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} renderKeyboardShortcutsButton={renderKeyboardShortcutsButton} /> + ))) + .add('with custom keyboard shortcuts panel', withInfo()(() => ( + ))); diff --git a/test/components/DayPickerKeyboardShortcuts_spec.jsx b/test/components/DayPickerKeyboardShortcuts_spec.jsx index 5321a32fcf..c68822dadb 100644 --- a/test/components/DayPickerKeyboardShortcuts_spec.jsx +++ b/test/components/DayPickerKeyboardShortcuts_spec.jsx @@ -146,6 +146,17 @@ describe('DayPickerKeyboardShortcuts', () => { expect(wrapper.children().find(Button)).to.have.lengthOf(1); }); }); + + describe('renderKeyboardShortcutsPanel', () => { + it('renders the provided keyboard shortcuts panel', () => { + const props = { + renderKeyboardShortcutsPanel: () => (
    Keyboard shortcuts here!
    ), + showKeyboardShortcutsPanel: true, + }; + const wrapper = shallow().dive(); + expect(wrapper.children().contains('Keyboard shortcuts here!')); + }); + }); }); describe('#DayPickerKeyboardShortcuts_panel', () => { From 6784598d45419ed9e33b8c0a3f6301de166b198d Mon Sep 17 00:00:00 2001 From: casey_klimkowsky Date: Wed, 18 Sep 2019 13:55:47 -0700 Subject: [PATCH 507/618] Fix ESLint auto-fixes and add tests --- examples/DayPickerRangeControllerWrapper.jsx | 7 +++---- src/components/DayPickerKeyboardShortcuts.jsx | 7 ++++--- stories/DayPickerRangeController.js | 20 +++++++++---------- .../DayPickerRangeController_spec.jsx | 16 +++++++++++++++ test/components/DayPicker_spec.jsx | 12 +++++++++++ 5 files changed, 45 insertions(+), 17 deletions(-) diff --git a/examples/DayPickerRangeControllerWrapper.jsx b/examples/DayPickerRangeControllerWrapper.jsx index fdf16ce9cf..08c5547f03 100644 --- a/examples/DayPickerRangeControllerWrapper.jsx +++ b/examples/DayPickerRangeControllerWrapper.jsx @@ -74,7 +74,7 @@ const defaultProps = { renderDayContents: null, minimumNights: 1, isDayBlocked: () => false, - isOutsideRange: (day) => !isInclusivelyAfterDay(day, moment()), + isOutsideRange: day => !isInclusivelyAfterDay(day, moment()), isDayHighlighted: () => false, enableOutsideDays: false, @@ -145,13 +145,12 @@ class DayPickerRangeControllerWrapper extends React.Component { return (
    - {showInputs - && ( + {showInputs && (
    - )} + )} )} - {showKeyboardShortcutsPanel - && (renderKeyboardShortcutsPanel ? ( + {showKeyboardShortcutsPanel && ( + renderKeyboardShortcutsPanel ? ( renderKeyboardShortcutsPanel({ closeButtonAriaLabel: phrases.hideKeyboardShortcutsPanel, keyboardShortcuts: this.keyboardShortcuts, @@ -272,7 +272,8 @@ class DayPickerKeyboardShortcuts extends React.PureComponent { ))}
    - ))} + ) + )}
    ); } diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index 1aa35dcb81..970e2c01f1 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -221,8 +221,8 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - startDateOffset={(day) => day.subtract(3, 'days')} - endDateOffset={(day) => day.add(3, 'days')} + startDateOffset={day => day.subtract(3, 'days')} + endDateOffset={day => day.add(3, 'days')} /> ))) .add('with 45 days range selection', withInfo()(() => ( @@ -230,8 +230,8 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - startDateOffset={(day) => day.subtract(22, 'days')} - endDateOffset={(day) => day.add(22, 'days')} + startDateOffset={day => day.subtract(22, 'days')} + endDateOffset={day => day.add(22, 'days')} /> ))) .add('with 4 days after today range selection', withInfo()(() => ( @@ -239,7 +239,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - endDateOffset={(day) => day.add(4, 'days')} + endDateOffset={day => day.add(4, 'days')} /> ))) .add('with current week range selection', withInfo()(() => ( @@ -247,8 +247,8 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - startDateOffset={(day) => day.startOf('week')} - endDateOffset={(day) => day.endOf('week')} + startDateOffset={day => day.startOf('week')} + endDateOffset={day => day.endOf('week')} /> ))) .add('with custom inputs', withInfo()(() => ( @@ -437,7 +437,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - isOutsideRange={(day) => !isInclusivelyAfterDay(day, moment()) + isOutsideRange={day => !isInclusivelyAfterDay(day, moment()) || isInclusivelyAfterDay(day, moment().add(2, 'weeks'))} /> ))) @@ -462,7 +462,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - isDayBlocked={(day) => moment.weekdays(day.weekday()) === 'Friday'} + isDayBlocked={day => moment.weekdays(day.weekday()) === 'Friday'} /> ))) .add('with navigation blocked (minDate and maxDate)', withInfo()(() => ( @@ -479,7 +479,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - renderDayContents={(day) => day.format('ddd')} + renderDayContents={day => day.format('ddd')} /> ))) .add('with info panel', withInfo()(() => ( diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index f12d496b7d..d8432f3655 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -4491,5 +4491,21 @@ describe('DayPickerRangeController', () => { .eql(testRenderKeyboardShortcutsButton); }); }); + + describe.only('renderKeyboardShortcutsPanel prop', () => { + it('passes down custom panel render function', () => { + const testRenderKeyboardShortcutsPanel = () => {}; + const wrapper = shallow( + , + ); + const dayPicker = wrapper.find(DayPicker); + expect(dayPicker).to.have.lengthOf(1); + expect(dayPicker.prop('renderKeyboardShortcutsPanel')) + .to + .eql(testRenderKeyboardShortcutsPanel); + }); + }); }); }); diff --git a/test/components/DayPicker_spec.jsx b/test/components/DayPicker_spec.jsx index 675fbd3d2c..2a2b670ac7 100644 --- a/test/components/DayPicker_spec.jsx +++ b/test/components/DayPicker_spec.jsx @@ -135,6 +135,18 @@ describe('DayPicker', () => { .to .eql(testRenderKeyboardShortcutsButton); }); + + it('component exists with custom panel render function if renderKeyboardShortcutsPanel is passed down', () => { + const testRenderKeyboardShortcutsPanel = () => {}; + const wrapper = shallow( + , + ).dive(); + const dayPickerKeyboardShortcuts = wrapper.find(DayPickerKeyboardShortcuts); + expect(dayPickerKeyboardShortcuts).to.have.lengthOf(1); + expect(dayPickerKeyboardShortcuts.prop('renderKeyboardShortcutsPanel')) + .to + .eql(testRenderKeyboardShortcutsPanel); + }); }); }); From 0444bcf50b4170cc5609111e850d905b53b50a09 Mon Sep 17 00:00:00 2001 From: casey_klimkowsky Date: Wed, 18 Sep 2019 13:58:23 -0700 Subject: [PATCH 508/618] Undo more ESLint auto-fixes --- stories/DayPickerRangeController.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index 970e2c01f1..7f61e84d34 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -438,7 +438,8 @@ storiesOf('DayPickerRangeController', module) onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} isOutsideRange={day => !isInclusivelyAfterDay(day, moment()) - || isInclusivelyAfterDay(day, moment().add(2, 'weeks'))} + || isInclusivelyAfterDay(day, moment().add(2, 'weeks')) + } /> ))) .add('with some blocked dates', withInfo()(() => ( @@ -446,7 +447,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - isDayBlocked={(day1) => datesList.some((day2) => isSameDay(day1, day2))} + isDayBlocked={day1 => datesList.some(day2 => isSameDay(day1, day2))} /> ))) .add('with some highlighted dates', withInfo()(() => ( @@ -454,7 +455,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - isDayHighlighted={(day1) => datesList.some((day2) => isSameDay(day1, day2))} + isDayHighlighted={day1 => datesList.some(day2 => isSameDay(day1, day2))} /> ))) .add('blocks fridays', withInfo()(() => ( @@ -541,7 +542,7 @@ storiesOf('DayPickerRangeController', module) onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} getMinNightsForHoverDate={() => 2} - isDayBlocked={(day1) => datesList.some((day2) => isSameDay(day1, day2))} + isDayBlocked={day1 => datesList.some(day2 => isSameDay(day1, day2))} /> ))) .add('with custom keyboard shortcuts button', withInfo()(() => ( From a10e1424d780638f4786e2b2bdada0adc71660fc Mon Sep 17 00:00:00 2001 From: casey_klimkowsky Date: Mon, 16 Sep 2019 13:40:52 -0700 Subject: [PATCH 509/618] Add support for positioning month navigation under the calendar --- src/components/DateRangePicker.jsx | 2 + src/components/DayPicker.jsx | 12 ++- src/components/DayPickerNavigation.jsx | 25 ++++++ src/components/DayPickerRangeController.jsx | 6 ++ .../DayPickerSingleDateController.jsx | 6 ++ src/components/SingleDatePicker.jsx | 4 + src/constants.js | 3 + src/shapes/DateRangePickerShape.js | 2 + src/shapes/NavPositionShape.js | 8 ++ src/shapes/SingleDatePickerShape.js | 2 + stories/DayPickerRangeController.js | 79 +++++++++++++++---- 11 files changed, 132 insertions(+), 17 deletions(-) create mode 100644 src/shapes/NavPositionShape.js diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index f76ea5463c..25ab572983 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -35,6 +35,7 @@ import { INFO_POSITION_BOTTOM, FANG_HEIGHT_PX, DEFAULT_VERTICAL_SPACING, + NAV_POSITION_TOP, } from '../constants'; const propTypes = forbidExtraProps({ @@ -98,6 +99,7 @@ const defaultProps = { horizontalMonthPadding: undefined, // navigation related props + navPosition: NAV_POSITION_TOP, navPrev: null, navNext: null, diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index df2777d14a..888cd39b87 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -27,6 +27,7 @@ import getActiveElement from '../utils/getActiveElement'; import isDayVisible from '../utils/isDayVisible'; import ModifiersShape from '../shapes/ModifiersShape'; +import NavPositionShape from '../shapes/NavPositionShape'; import ScrollableOrientationShape from '../shapes/ScrollableOrientationShape'; import DayOfWeekShape from '../shapes/DayOfWeekShape'; import CalendarInfoPositionShape from '../shapes/CalendarInfoPositionShape'; @@ -41,6 +42,8 @@ import { INFO_POSITION_BEFORE, INFO_POSITION_AFTER, MODIFIER_KEY_NAMES, + NAV_POSITION_TOP, + NAV_POSITION_BOTTOM, } from '../constants'; const MONTH_PADDING = 23; @@ -77,6 +80,7 @@ const propTypes = forbidExtraProps({ // navigation props disablePrev: PropTypes.bool, disableNext: PropTypes.bool, + navPosition: NavPositionShape, navPrev: PropTypes.node, navNext: PropTypes.node, noNavButtons: PropTypes.bool, @@ -140,6 +144,7 @@ export const defaultProps = { // navigation props disablePrev: false, disableNext: false, + navPosition: NAV_POSITION_TOP, navPrev: null, navNext: null, noNavButtons: false, @@ -835,6 +840,7 @@ class DayPicker extends React.PureComponent { const { disablePrev, disableNext, + navPosition, navPrev, navNext, noNavButtons, @@ -857,6 +863,7 @@ class DayPicker extends React.PureComponent { disableNext={disableNext} onPrevMonthClick={this.onPrevMonthClick} onNextMonthClick={onNextMonthClick} + navPosition={navPosition} navPrev={navPrev} navNext={navNext} orientation={orientation} @@ -968,6 +975,7 @@ class DayPicker extends React.PureComponent { transitionDuration, verticalBorderSpacing, horizontalMonthPadding, + navPosition, } = this.props; const { reactDates: { spacing: { dayPickerHorizontalPadding } } } = theme; @@ -1088,7 +1096,7 @@ class DayPicker extends React.PureComponent { aria-roledescription={phrases.roleDescription} aria-label={phrases.calendarLabel} > - {!verticalScrollable && this.renderNavigation()} + {!verticalScrollable && navPosition === NAV_POSITION_TOP && this.renderNavigation()}
    + {!verticalScrollable && navPosition === NAV_POSITION_BOTTOM && this.renderNavigation()} + {!isTouch && !hideKeyboardShortcutsPanel && ( ({ zIndex: zIndex + 2, }, + DayPickerNavigation__bottom: { + display: 'flex', + justifyContent: 'space-between', + height: 'auto', + }, + DayPickerNavigation__horizontal: { height: 0, }, @@ -292,6 +309,14 @@ export default withStyles(({ reactDates: { color, zIndex } }) => ({ padding: '6px 9px', }, + DayPickerNavigation_bottomButton__horizontalDefault: { + position: 'static', + marginLeft: 22, + marginRight: 22, + marginBottom: 30, + marginTop: -10, + }, + DayPickerNavigation_leftButton__horizontalDefault: { left: noflip(22), }, diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 483358c6dd..2c01721365 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -28,6 +28,7 @@ import FocusedInputShape from '../shapes/FocusedInputShape'; import ScrollableOrientationShape from '../shapes/ScrollableOrientationShape'; import DayOfWeekShape from '../shapes/DayOfWeekShape'; import CalendarInfoPositionShape from '../shapes/CalendarInfoPositionShape'; +import NavPositionShape from '../shapes/NavPositionShape'; import { START_DATE, @@ -36,6 +37,7 @@ import { VERTICAL_SCROLLABLE, DAY_SIZE, INFO_POSITION_BOTTOM, + NAV_POSITION_TOP, } from '../constants'; import DayPicker from './DayPicker'; @@ -77,6 +79,7 @@ const propTypes = forbidExtraProps({ verticalBorderSpacing: nonNegativeInteger, horizontalMonthPadding: nonNegativeInteger, + navPosition: NavPositionShape, navPrev: PropTypes.node, navNext: PropTypes.node, noNavButtons: PropTypes.bool, @@ -142,6 +145,7 @@ const defaultProps = { initialVisibleMonth: null, daySize: DAY_SIZE, + navPosition: NAV_POSITION_TOP, navPrev: null, navNext: null, noNavButtons: false, @@ -1136,6 +1140,7 @@ export default class DayPickerRangeController extends React.PureComponent { monthFormat, renderMonthText, renderWeekHeaderElement, + navPosition, navPrev, navNext, noNavButtons, @@ -1202,6 +1207,7 @@ export default class DayPickerRangeController extends React.PureComponent { onOutsideClick={onOutsideClick} disablePrev={disablePrev} disableNext={disableNext} + navPosition={navPosition} navPrev={navPrev} navNext={navNext} noNavButtons={noNavButtons} diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 8d7f6b2c0c..2fae39a0f1 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -20,12 +20,14 @@ import { addModifier, deleteModifier } from '../utils/modifiers'; import ScrollableOrientationShape from '../shapes/ScrollableOrientationShape'; import DayOfWeekShape from '../shapes/DayOfWeekShape'; import CalendarInfoPositionShape from '../shapes/CalendarInfoPositionShape'; +import NavPositionShape from '../shapes/NavPositionShape'; import { HORIZONTAL_ORIENTATION, VERTICAL_SCROLLABLE, DAY_SIZE, INFO_POSITION_BOTTOM, + NAV_POSITION_TOP, } from '../constants'; import DayPicker from './DayPicker'; @@ -62,6 +64,7 @@ const propTypes = forbidExtraProps({ transitionDuration: nonNegativeInteger, horizontalMonthPadding: nonNegativeInteger, + navPosition: NavPositionShape, navPrev: PropTypes.node, navNext: PropTypes.node, @@ -119,6 +122,7 @@ const defaultProps = { transitionDuration: undefined, horizontalMonthPadding: 13, + navPosition: NAV_POSITION_TOP, navPrev: null, navNext: null, @@ -573,6 +577,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { monthFormat, renderMonthText, renderWeekHeaderElement, + navPosition, navPrev, navNext, onOutsideClick, @@ -626,6 +631,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { initialVisibleMonth={() => currentMonth} firstDayOfWeek={firstDayOfWeek} onOutsideClick={onOutsideClick} + navPosition={navPosition} navPrev={navPrev} navNext={navNext} renderMonthText={renderMonthText} diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index 6de560edb4..8fc0c346d4 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -33,6 +33,7 @@ import { INFO_POSITION_BOTTOM, FANG_HEIGHT_PX, DEFAULT_VERTICAL_SPACING, + NAV_POSITION_TOP, } from '../constants'; const propTypes = forbidExtraProps({ @@ -89,6 +90,7 @@ const defaultProps = { horizontalMonthPadding: 13, // navigation related props + navPosition: NAV_POSITION_TOP, navPrev: null, navNext: null, @@ -395,6 +397,7 @@ class SingleDatePicker extends React.PureComponent { numberOfMonths, orientation, monthFormat, + navPosition, navPrev, navNext, onPrevMonthClick, @@ -476,6 +479,7 @@ class SingleDatePicker extends React.PureComponent { keepOpenOnDateSelect={keepOpenOnDateSelect} hideKeyboardShortcutsPanel={hideKeyboardShortcutsPanel} initialVisibleMonth={initialVisibleMonth} + navPosition={navPosition} navPrev={navPrev} navNext={navNext} onPrevMonthClick={onPrevMonthClick} diff --git a/src/constants.js b/src/constants.js index 31eec9a200..f79893b6b2 100644 --- a/src/constants.js +++ b/src/constants.js @@ -9,6 +9,9 @@ export const HORIZONTAL_ORIENTATION = 'horizontal'; export const VERTICAL_ORIENTATION = 'vertical'; export const VERTICAL_SCROLLABLE = 'verticalScrollable'; +export const NAV_POSITION_BOTTOM = 'navPositionBottom'; +export const NAV_POSITION_TOP = 'navPositionTop'; + export const ICON_BEFORE_POSITION = 'before'; export const ICON_AFTER_POSITION = 'after'; diff --git a/src/shapes/DateRangePickerShape.js b/src/shapes/DateRangePickerShape.js index 69eb5ad780..666cf7148b 100644 --- a/src/shapes/DateRangePickerShape.js +++ b/src/shapes/DateRangePickerShape.js @@ -13,6 +13,7 @@ import anchorDirectionShape from './AnchorDirectionShape'; import openDirectionShape from './OpenDirectionShape'; import DayOfWeekShape from './DayOfWeekShape'; import CalendarInfoPositionShape from './CalendarInfoPositionShape'; +import NavPositionShape from './NavPositionShape'; export default { // required props for a functional interactive DateRangePicker @@ -78,6 +79,7 @@ export default { horizontalMonthPadding: nonNegativeInteger, // navigation related props + navPosition: NavPositionShape, navPrev: PropTypes.node, navNext: PropTypes.node, onPrevMonthClick: PropTypes.func, diff --git a/src/shapes/NavPositionShape.js b/src/shapes/NavPositionShape.js new file mode 100644 index 0000000000..6596cd94c7 --- /dev/null +++ b/src/shapes/NavPositionShape.js @@ -0,0 +1,8 @@ +import PropTypes from 'prop-types'; + +import { + NAV_POSITION_BOTTOM, + NAV_POSITION_TOP, +} from '../constants'; + +export default PropTypes.oneOf([NAV_POSITION_BOTTOM, NAV_POSITION_TOP]); diff --git a/src/shapes/SingleDatePickerShape.js b/src/shapes/SingleDatePickerShape.js index 181f56b301..d2e296d1d4 100644 --- a/src/shapes/SingleDatePickerShape.js +++ b/src/shapes/SingleDatePickerShape.js @@ -11,6 +11,7 @@ import anchorDirectionShape from './AnchorDirectionShape'; import openDirectionShape from './OpenDirectionShape'; import DayOfWeekShape from './DayOfWeekShape'; import CalendarInfoPositionShape from './CalendarInfoPositionShape'; +import NavPositionShape from './NavPositionShape'; export default { // required props for a functional interactive SingleDatePicker @@ -67,6 +68,7 @@ export default { horizontalMonthPadding: nonNegativeInteger, // navigation related props + navPosition: NavPositionShape, navPrev: PropTypes.node, navNext: PropTypes.node, diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index 7f61e84d34..34f3a7fd02 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -12,7 +12,7 @@ import isInclusivelyAfterDay from '../src/utils/isInclusivelyAfterDay'; import CloseButton from '../src/components/CloseButton'; import KeyboardShortcutRow from '../src/components/KeyboardShortcutRow'; -import { VERTICAL_ORIENTATION, VERTICAL_SCROLLABLE } from '../src/constants'; +import { NAV_POSITION_BOTTOM, VERTICAL_ORIENTATION, VERTICAL_SCROLLABLE } from '../src/constants'; import DayPickerRangeControllerWrapper from '../examples/DayPickerRangeControllerWrapper'; @@ -67,6 +67,36 @@ const TestNextIcon = () => (
    ); +const TestBottomPrevIcon = () => ( +
    + Prev +
    +); + +const TestBottomNextIcon = () => ( +
    + Next +
    +); + const TestCustomInfoPanel = () => (
    day.subtract(3, 'days')} - endDateOffset={day => day.add(3, 'days')} + startDateOffset={(day) => day.subtract(3, 'days')} + endDateOffset={(day) => day.add(3, 'days')} /> ))) .add('with 45 days range selection', withInfo()(() => ( @@ -230,8 +260,8 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - startDateOffset={day => day.subtract(22, 'days')} - endDateOffset={day => day.add(22, 'days')} + startDateOffset={(day) => day.subtract(22, 'days')} + endDateOffset={(day) => day.add(22, 'days')} /> ))) .add('with 4 days after today range selection', withInfo()(() => ( @@ -239,7 +269,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - endDateOffset={day => day.add(4, 'days')} + endDateOffset={(day) => day.add(4, 'days')} /> ))) .add('with current week range selection', withInfo()(() => ( @@ -247,8 +277,8 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - startDateOffset={day => day.startOf('week')} - endDateOffset={day => day.endOf('week')} + startDateOffset={(day) => day.startOf('week')} + endDateOffset={(day) => day.endOf('week')} /> ))) .add('with custom inputs', withInfo()(() => ( @@ -367,6 +397,24 @@ storiesOf('DayPickerRangeController', module) />
    ))) + .add('with month navigation positioned at the bottom', withInfo()(() => ( + + ))) + .add('with custom month navigation positioned at the bottom', withInfo()(() => ( + } + navNext={} + /> + ))) .add('with custom month navigation', withInfo()(() => ( !isInclusivelyAfterDay(day, moment()) - || isInclusivelyAfterDay(day, moment().add(2, 'weeks')) - } + isOutsideRange={(day) => !isInclusivelyAfterDay(day, moment()) + || isInclusivelyAfterDay(day, moment().add(2, 'weeks'))} /> ))) .add('with some blocked dates', withInfo()(() => ( @@ -447,7 +494,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - isDayBlocked={day1 => datesList.some(day2 => isSameDay(day1, day2))} + isDayBlocked={(day1) => datesList.some((day2) => isSameDay(day1, day2))} /> ))) .add('with some highlighted dates', withInfo()(() => ( @@ -455,7 +502,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - isDayHighlighted={day1 => datesList.some(day2 => isSameDay(day1, day2))} + isDayHighlighted={(day1) => datesList.some((day2) => isSameDay(day1, day2))} /> ))) .add('blocks fridays', withInfo()(() => ( @@ -463,7 +510,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - isDayBlocked={day => moment.weekdays(day.weekday()) === 'Friday'} + isDayBlocked={(day) => moment.weekdays(day.weekday()) === 'Friday'} /> ))) .add('with navigation blocked (minDate and maxDate)', withInfo()(() => ( @@ -480,7 +527,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - renderDayContents={day => day.format('ddd')} + renderDayContents={(day) => day.format('ddd')} /> ))) .add('with info panel', withInfo()(() => ( @@ -542,7 +589,7 @@ storiesOf('DayPickerRangeController', module) onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} getMinNightsForHoverDate={() => 2} - isDayBlocked={day1 => datesList.some(day2 => isSameDay(day1, day2))} + isDayBlocked={(day1) => datesList.some((day2) => isSameDay(day1, day2))} /> ))) .add('with custom keyboard shortcuts button', withInfo()(() => ( From 544ff7415a792eb4e89856154a3f881ee813f5d0 Mon Sep 17 00:00:00 2001 From: casey_klimkowsky Date: Mon, 16 Sep 2019 16:39:31 -0700 Subject: [PATCH 510/618] Fix lint errors --- .vscode/settings.json | 2 + stories/DayPickerRangeController.js | 61 +++++++++++++++-------------- 2 files changed, 33 insertions(+), 30 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..7a73a41bfd --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index 34f3a7fd02..0fa1185237 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -44,7 +44,7 @@ const TestPrevIcon = () => ( position: 'absolute', top: '20px', }} - tabIndex="0" + tabindex="0" > Prev
    @@ -61,7 +61,7 @@ const TestNextIcon = () => ( right: '22px', top: '20px', }} - tabIndex="0" + tabindex="0" > Next
    @@ -251,8 +251,8 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - startDateOffset={(day) => day.subtract(3, 'days')} - endDateOffset={(day) => day.add(3, 'days')} + startDateOffset={day => day.subtract(3, 'days')} + endDateOffset={day => day.add(3, 'days')} /> ))) .add('with 45 days range selection', withInfo()(() => ( @@ -260,8 +260,8 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - startDateOffset={(day) => day.subtract(22, 'days')} - endDateOffset={(day) => day.add(22, 'days')} + startDateOffset={day => day.subtract(22, 'days')} + endDateOffset={day => day.add(22, 'days')} /> ))) .add('with 4 days after today range selection', withInfo()(() => ( @@ -269,7 +269,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - endDateOffset={(day) => day.add(4, 'days')} + endDateOffset={day => day.add(4, 'days')} /> ))) .add('with current week range selection', withInfo()(() => ( @@ -277,8 +277,8 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - startDateOffset={(day) => day.startOf('week')} - endDateOffset={(day) => day.endOf('week')} + startDateOffset={day => day.startOf('week')} + endDateOffset={day => day.endOf('week')} /> ))) .add('with custom inputs', withInfo()(() => ( @@ -397,42 +397,42 @@ storiesOf('DayPickerRangeController', module) /> ))) - .add('with month navigation positioned at the bottom', withInfo()(() => ( + .add('with custom month navigation', withInfo()(() => ( } + navNext={} /> ))) - .add('with custom month navigation positioned at the bottom', withInfo()(() => ( + .add('with custom month navigation and blocked navigation (minDate and maxDate)', withInfo()(() => ( } - navNext={} + navPrev={} + navNext={} /> ))) - .add('with custom month navigation', withInfo()(() => ( + .add('with month navigation positioned at the bottom', withInfo()(() => ( } - navNext={} /> ))) - .add('with custom month navigation and blocked navigation (minDate and maxDate)', withInfo()(() => ( + .add('with custom month navigation positioned at the bottom', withInfo()(() => ( } - navNext={} + navPrev={} + navNext={} /> ))) .add('with outside days enabled', withInfo()(() => ( @@ -485,8 +485,9 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - isOutsideRange={(day) => !isInclusivelyAfterDay(day, moment()) - || isInclusivelyAfterDay(day, moment().add(2, 'weeks'))} + isOutsideRange={day => !isInclusivelyAfterDay(day, moment()) + || isInclusivelyAfterDay(day, moment().add(2, 'weeks')) + } /> ))) .add('with some blocked dates', withInfo()(() => ( @@ -494,7 +495,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - isDayBlocked={(day1) => datesList.some((day2) => isSameDay(day1, day2))} + isDayBlocked={day1 => datesList.some(day2 => isSameDay(day1, day2))} /> ))) .add('with some highlighted dates', withInfo()(() => ( @@ -502,7 +503,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - isDayHighlighted={(day1) => datesList.some((day2) => isSameDay(day1, day2))} + isDayHighlighted={day1 => datesList.some(day2 => isSameDay(day1, day2))} /> ))) .add('blocks fridays', withInfo()(() => ( @@ -510,7 +511,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - isDayBlocked={(day) => moment.weekdays(day.weekday()) === 'Friday'} + isDayBlocked={day => moment.weekdays(day.weekday()) === 'Friday'} /> ))) .add('with navigation blocked (minDate and maxDate)', withInfo()(() => ( @@ -527,7 +528,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - renderDayContents={(day) => day.format('ddd')} + renderDayContents={day => day.format('ddd')} /> ))) .add('with info panel', withInfo()(() => ( @@ -589,7 +590,7 @@ storiesOf('DayPickerRangeController', module) onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} getMinNightsForHoverDate={() => 2} - isDayBlocked={(day1) => datesList.some((day2) => isSameDay(day1, day2))} + isDayBlocked={day1 => datesList.some(day2 => isSameDay(day1, day2))} /> ))) .add('with custom keyboard shortcuts button', withInfo()(() => ( From 85ca643d5bec08a5b702665280362f7057441e37 Mon Sep 17 00:00:00 2001 From: casey_klimkowsky Date: Mon, 16 Sep 2019 16:43:44 -0700 Subject: [PATCH 511/618] Remove .vscode/settings.json --- .vscode/settings.json | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 7a73a41bfd..0000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file From 43b1c05a87cc924168650390fde69be0911f7d63 Mon Sep 17 00:00:00 2001 From: casey_klimkowsky Date: Tue, 17 Sep 2019 09:51:40 -0700 Subject: [PATCH 512/618] Fix lint error and add more Storybook variations --- examples/DateRangePickerWrapper.jsx | 9 ++++++++- src/components/DateRangePicker.jsx | 2 ++ src/components/DayPicker.jsx | 3 ++- stories/DateRangePicker_calendar.js | 9 ++++++++- stories/DayPickerSingleDateController.js | 10 +++++++++- 5 files changed, 29 insertions(+), 4 deletions(-) diff --git a/examples/DateRangePickerWrapper.jsx b/examples/DateRangePickerWrapper.jsx index ce78bb9bee..620a286cdd 100644 --- a/examples/DateRangePickerWrapper.jsx +++ b/examples/DateRangePickerWrapper.jsx @@ -8,7 +8,13 @@ import DateRangePicker from '../src/components/DateRangePicker'; import { DateRangePickerPhrases } from '../src/defaultPhrases'; import DateRangePickerShape from '../src/shapes/DateRangePickerShape'; -import { START_DATE, END_DATE, HORIZONTAL_ORIENTATION, ANCHOR_LEFT } from '../src/constants'; +import { + START_DATE, + END_DATE, + HORIZONTAL_ORIENTATION, + ANCHOR_LEFT, + NAV_POSITION_TOP, +} from '../src/constants'; import isInclusivelyAfterDay from '../src/utils/isInclusivelyAfterDay'; const propTypes = { @@ -66,6 +72,7 @@ const defaultProps = { isRTL: false, // navigation related props + navPosition: NAV_POSITION_TOP, navPrev: null, navNext: null, onPrevMonthClick() {}, diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 25ab572983..138783a29a 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -407,6 +407,7 @@ class DateRangePicker extends React.PureComponent { monthFormat, renderMonthText, renderWeekHeaderElement, + navPosition, navPrev, navNext, onPrevMonthClick, @@ -512,6 +513,7 @@ class DateRangePicker extends React.PureComponent { daySize={daySize} initialVisibleMonth={initialVisibleMonthThunk} hideKeyboardShortcutsPanel={hideKeyboardShortcutsPanel} + navPosition={navPosition} navPrev={navPrev} navNext={navNext} minimumNights={minimumNights} diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index 888cd39b87..b6236e6d50 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -1143,7 +1143,8 @@ class DayPicker extends React.PureComponent { {verticalScrollable && this.renderNavigation()} - {!verticalScrollable && navPosition === NAV_POSITION_BOTTOM && this.renderNavigation()} + {!verticalScrollable + && navPosition === NAV_POSITION_BOTTOM && this.renderNavigation()} {!isTouch && !hideKeyboardShortcutsPanel && ( ))) + .add('with month navigation positioned at the bottom', withInfo()(() => ( + + ))) .add('with outside days enabled', withInfo()(() => ( } /> ))) + .add('with month navigation positioned at the bottom', withInfo()(() => ( + + ))) .add('with outside days enabled', withInfo()(() => ( Date: Tue, 17 Sep 2019 11:23:31 -0700 Subject: [PATCH 513/618] Add unit tests and add support for customDayPickerNavigationStyles --- src/components/DateRangePicker.jsx | 3 +++ src/components/DayPicker.jsx | 7 ++++- src/components/DayPickerNavigation.jsx | 26 +++++++++++++----- src/components/DayPickerRangeController.jsx | 4 +++ .../DayPickerSingleDateController.jsx | 4 +++ src/components/SingleDatePicker.jsx | 3 +++ src/shapes/DateRangePickerShape.js | 1 + src/shapes/SingleDatePickerShape.js | 1 + stories/DayPickerRangeController.js | 4 +++ test/components/DayPicker_spec.jsx | 27 +++++++++++++++++++ 10 files changed, 72 insertions(+), 8 deletions(-) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 138783a29a..36e43648b2 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -99,6 +99,7 @@ const defaultProps = { horizontalMonthPadding: undefined, // navigation related props + customDayPickerNavigationStyles: null, navPosition: NAV_POSITION_TOP, navPrev: null, navNext: null, @@ -407,6 +408,7 @@ class DateRangePicker extends React.PureComponent { monthFormat, renderMonthText, renderWeekHeaderElement, + customDayPickerNavigationStyles, navPosition, navPrev, navNext, @@ -513,6 +515,7 @@ class DateRangePicker extends React.PureComponent { daySize={daySize} initialVisibleMonth={initialVisibleMonthThunk} hideKeyboardShortcutsPanel={hideKeyboardShortcutsPanel} + customDayPickerNavigationStyles={customDayPickerNavigationStyles} navPosition={navPosition} navPrev={navPrev} navNext={navNext} diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index b6236e6d50..c58c698fec 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -78,6 +78,7 @@ const propTypes = forbidExtraProps({ renderKeyboardShortcutsPanel: PropTypes.func, // navigation props + customDayPickerNavigationStyles: PropTypes.object, disablePrev: PropTypes.bool, disableNext: PropTypes.bool, navPosition: NavPositionShape, @@ -142,6 +143,7 @@ export const defaultProps = { renderKeyboardShortcutsPanel: undefined, // navigation props + customDayPickerNavigationStyles: null, disablePrev: false, disableNext: false, navPosition: NAV_POSITION_TOP, @@ -838,6 +840,7 @@ class DayPicker extends React.PureComponent { renderNavigation() { const { + customDayPickerNavigationStyles, disablePrev, disableNext, navPosition, @@ -859,6 +862,7 @@ class DayPicker extends React.PureComponent { return ( {!verticalScrollable - && navPosition === NAV_POSITION_BOTTOM && this.renderNavigation()} + && navPosition === NAV_POSITION_BOTTOM + && this.renderNavigation()} {!isTouch && !hideKeyboardShortcutsPanel && ( {!isVerticalScrollable && ( @@ -230,12 +239,6 @@ export default withStyles(({ reactDates: { color, zIndex } }) => ({ zIndex: zIndex + 2, }, - DayPickerNavigation__bottom: { - display: 'flex', - justifyContent: 'space-between', - height: 'auto', - }, - DayPickerNavigation__horizontal: { height: 0, }, @@ -255,6 +258,15 @@ export default withStyles(({ reactDates: { color, zIndex } }) => ({ position: 'relative', }, + DayPickerNavigation__bottom: { + height: 'auto', + }, + + DayPickerNavigation__bottomDefault: { + display: 'flex', + justifyContent: 'space-between', + }, + DayPickerNavigation_button: { cursor: 'pointer', userSelect: 'none', diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 2c01721365..ff845d8b20 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -79,6 +79,7 @@ const propTypes = forbidExtraProps({ verticalBorderSpacing: nonNegativeInteger, horizontalMonthPadding: nonNegativeInteger, + customDayPickerNavigationStyles: PropTypes.object, navPosition: NavPositionShape, navPrev: PropTypes.node, navNext: PropTypes.node, @@ -145,6 +146,7 @@ const defaultProps = { initialVisibleMonth: null, daySize: DAY_SIZE, + customDayPickerNavigationStyles: null, navPosition: NAV_POSITION_TOP, navPrev: null, navNext: null, @@ -1140,6 +1142,7 @@ export default class DayPickerRangeController extends React.PureComponent { monthFormat, renderMonthText, renderWeekHeaderElement, + customDayPickerNavigationStyles, navPosition, navPrev, navNext, @@ -1207,6 +1210,7 @@ export default class DayPickerRangeController extends React.PureComponent { onOutsideClick={onOutsideClick} disablePrev={disablePrev} disableNext={disableNext} + customDayPickerNavigationStyles={customDayPickerNavigationStyles} navPosition={navPosition} navPrev={navPrev} navNext={navNext} diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 2fae39a0f1..7699df0327 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -64,6 +64,7 @@ const propTypes = forbidExtraProps({ transitionDuration: nonNegativeInteger, horizontalMonthPadding: nonNegativeInteger, + customDayPickerNavigationStyles: PropTypes.object, navPosition: NavPositionShape, navPrev: PropTypes.node, navNext: PropTypes.node, @@ -122,6 +123,7 @@ const defaultProps = { transitionDuration: undefined, horizontalMonthPadding: 13, + customDayPickerNavigationStyles: null, navPosition: NAV_POSITION_TOP, navPrev: null, navNext: null, @@ -577,6 +579,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { monthFormat, renderMonthText, renderWeekHeaderElement, + customDayPickerNavigationStyles, navPosition, navPrev, navNext, @@ -631,6 +634,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { initialVisibleMonth={() => currentMonth} firstDayOfWeek={firstDayOfWeek} onOutsideClick={onOutsideClick} + customDayPickerNavigationStyles={customDayPickerNavigationStyles} navPosition={navPosition} navPrev={navPrev} navNext={navNext} diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index 8fc0c346d4..abb3c5686f 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -90,6 +90,7 @@ const defaultProps = { horizontalMonthPadding: 13, // navigation related props + customDayPickerNavigationStyles: null, navPosition: NAV_POSITION_TOP, navPrev: null, navNext: null, @@ -397,6 +398,7 @@ class SingleDatePicker extends React.PureComponent { numberOfMonths, orientation, monthFormat, + customDayPickerNavigationStyles, navPosition, navPrev, navNext, @@ -479,6 +481,7 @@ class SingleDatePicker extends React.PureComponent { keepOpenOnDateSelect={keepOpenOnDateSelect} hideKeyboardShortcutsPanel={hideKeyboardShortcutsPanel} initialVisibleMonth={initialVisibleMonth} + customDayPickerNavigationStyles={customDayPickerNavigationStyles} navPosition={navPosition} navPrev={navPrev} navNext={navNext} diff --git a/src/shapes/DateRangePickerShape.js b/src/shapes/DateRangePickerShape.js index 666cf7148b..f83dabaca7 100644 --- a/src/shapes/DateRangePickerShape.js +++ b/src/shapes/DateRangePickerShape.js @@ -79,6 +79,7 @@ export default { horizontalMonthPadding: nonNegativeInteger, // navigation related props + customDayPickerNavigationStyles: PropTypes.object, navPosition: NavPositionShape, navPrev: PropTypes.node, navNext: PropTypes.node, diff --git a/src/shapes/SingleDatePickerShape.js b/src/shapes/SingleDatePickerShape.js index d2e296d1d4..7144edc661 100644 --- a/src/shapes/SingleDatePickerShape.js +++ b/src/shapes/SingleDatePickerShape.js @@ -68,6 +68,7 @@ export default { horizontalMonthPadding: nonNegativeInteger, // navigation related props + customDayPickerNavigationStyles: PropTypes.object, navPosition: NavPositionShape, navPrev: PropTypes.node, navNext: PropTypes.node, diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index 0fa1185237..f1b01abdb7 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -433,6 +433,10 @@ storiesOf('DayPickerRangeController', module) onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} navPrev={} navNext={} + customDayPickerNavigationStyles={{ + display: 'flex', + justifyContent: 'space-between', + }} /> ))) .add('with outside days enabled', withInfo()(() => ( diff --git a/test/components/DayPicker_spec.jsx b/test/components/DayPicker_spec.jsx index 2a2b670ac7..63fb8e506c 100644 --- a/test/components/DayPicker_spec.jsx +++ b/test/components/DayPicker_spec.jsx @@ -14,6 +14,7 @@ import { HORIZONTAL_ORIENTATION, VERTICAL_ORIENTATION, VERTICAL_SCROLLABLE, + NAV_POSITION_BOTTOM, } from '../../src/constants'; @@ -106,6 +107,32 @@ describe('DayPicker', () => { }); }); + describe('DayPickerNavigation', () => { + it('is rendered before CalendarMonthGrid in DayPicker_focusRegion', () => { + const wrapper = shallow().dive(); + expect(wrapper.find(DayPickerNavigation)).to.have.lengthOf(1); + expect( + wrapper + .find('[className^="DayPicker_focusRegion"]') + .childAt(0) + .type(), + ).to.equal(DayPickerNavigation); + }); + + describe('navPosition === NAV_POSITION_BOTTOM', () => { + it('is rendered after CalendarMonthGrid in DayPicker_focusRegion', () => { + const wrapper = shallow().dive(); + expect(wrapper.find(DayPickerNavigation)).to.have.lengthOf(1); + expect( + wrapper + .find('[className^="DayPicker_focusRegion"]') + .childAt(1) + .type(), + ).to.equal(DayPickerNavigation); + }); + }); + }); + describe('DayPickerKeyboardShortcuts', () => { it('component exists if state.isTouchDevice is false and hideKeyboardShortcutsPanel is false', () => { const wrapper = shallow().dive(); From e986d8ab80dee785c6e8df348c8ef147b0d2834a Mon Sep 17 00:00:00 2001 From: casey_klimkowsky Date: Thu, 19 Sep 2019 14:01:36 -0700 Subject: [PATCH 514/618] Rename customStyles to inlineStyles --- src/components/DateRangePicker.jsx | 6 +++--- src/components/DayPicker.jsx | 8 ++++---- src/components/DayPickerNavigation.jsx | 10 +++++----- src/components/DayPickerRangeController.jsx | 8 ++++---- src/components/DayPickerSingleDateController.jsx | 8 ++++---- src/components/SingleDatePicker.jsx | 6 +++--- src/shapes/DateRangePickerShape.js | 2 +- src/shapes/SingleDatePickerShape.js | 2 +- stories/DayPickerRangeController.js | 6 +++--- 9 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 36e43648b2..e30827306e 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -99,7 +99,7 @@ const defaultProps = { horizontalMonthPadding: undefined, // navigation related props - customDayPickerNavigationStyles: null, + dayPickerNavigationInlineStyles: null, navPosition: NAV_POSITION_TOP, navPrev: null, navNext: null, @@ -408,7 +408,7 @@ class DateRangePicker extends React.PureComponent { monthFormat, renderMonthText, renderWeekHeaderElement, - customDayPickerNavigationStyles, + dayPickerNavigationInlineStyles, navPosition, navPrev, navNext, @@ -515,7 +515,7 @@ class DateRangePicker extends React.PureComponent { daySize={daySize} initialVisibleMonth={initialVisibleMonthThunk} hideKeyboardShortcutsPanel={hideKeyboardShortcutsPanel} - customDayPickerNavigationStyles={customDayPickerNavigationStyles} + dayPickerNavigationInlineStyles={dayPickerNavigationInlineStyles} navPosition={navPosition} navPrev={navPrev} navNext={navNext} diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index c58c698fec..4c0b1f9543 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -78,7 +78,7 @@ const propTypes = forbidExtraProps({ renderKeyboardShortcutsPanel: PropTypes.func, // navigation props - customDayPickerNavigationStyles: PropTypes.object, + dayPickerNavigationInlineStyles: PropTypes.object, disablePrev: PropTypes.bool, disableNext: PropTypes.bool, navPosition: NavPositionShape, @@ -143,7 +143,7 @@ export const defaultProps = { renderKeyboardShortcutsPanel: undefined, // navigation props - customDayPickerNavigationStyles: null, + dayPickerNavigationInlineStyles: null, disablePrev: false, disableNext: false, navPosition: NAV_POSITION_TOP, @@ -840,7 +840,7 @@ class DayPicker extends React.PureComponent { renderNavigation() { const { - customDayPickerNavigationStyles, + dayPickerNavigationInlineStyles, disablePrev, disableNext, navPosition, @@ -862,9 +862,9 @@ class DayPicker extends React.PureComponent { return ( {!isVerticalScrollable && ( diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index ff845d8b20..b96230f127 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -79,7 +79,7 @@ const propTypes = forbidExtraProps({ verticalBorderSpacing: nonNegativeInteger, horizontalMonthPadding: nonNegativeInteger, - customDayPickerNavigationStyles: PropTypes.object, + dayPickerNavigationInlineStyles: PropTypes.object, navPosition: NavPositionShape, navPrev: PropTypes.node, navNext: PropTypes.node, @@ -146,7 +146,7 @@ const defaultProps = { initialVisibleMonth: null, daySize: DAY_SIZE, - customDayPickerNavigationStyles: null, + dayPickerNavigationInlineStyles: null, navPosition: NAV_POSITION_TOP, navPrev: null, navNext: null, @@ -1142,7 +1142,7 @@ export default class DayPickerRangeController extends React.PureComponent { monthFormat, renderMonthText, renderWeekHeaderElement, - customDayPickerNavigationStyles, + dayPickerNavigationInlineStyles, navPosition, navPrev, navNext, @@ -1210,7 +1210,7 @@ export default class DayPickerRangeController extends React.PureComponent { onOutsideClick={onOutsideClick} disablePrev={disablePrev} disableNext={disableNext} - customDayPickerNavigationStyles={customDayPickerNavigationStyles} + dayPickerNavigationInlineStyles={dayPickerNavigationInlineStyles} navPosition={navPosition} navPrev={navPrev} navNext={navNext} diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 7699df0327..f9b9e1be4e 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -64,7 +64,7 @@ const propTypes = forbidExtraProps({ transitionDuration: nonNegativeInteger, horizontalMonthPadding: nonNegativeInteger, - customDayPickerNavigationStyles: PropTypes.object, + dayPickerNavigationInlineStyles: PropTypes.object, navPosition: NavPositionShape, navPrev: PropTypes.node, navNext: PropTypes.node, @@ -123,7 +123,7 @@ const defaultProps = { transitionDuration: undefined, horizontalMonthPadding: 13, - customDayPickerNavigationStyles: null, + dayPickerNavigationInlineStyles: null, navPosition: NAV_POSITION_TOP, navPrev: null, navNext: null, @@ -579,7 +579,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { monthFormat, renderMonthText, renderWeekHeaderElement, - customDayPickerNavigationStyles, + dayPickerNavigationInlineStyles, navPosition, navPrev, navNext, @@ -634,7 +634,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { initialVisibleMonth={() => currentMonth} firstDayOfWeek={firstDayOfWeek} onOutsideClick={onOutsideClick} - customDayPickerNavigationStyles={customDayPickerNavigationStyles} + dayPickerNavigationInlineStyles={dayPickerNavigationInlineStyles} navPosition={navPosition} navPrev={navPrev} navNext={navNext} diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index abb3c5686f..bbdb9d0127 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -90,7 +90,7 @@ const defaultProps = { horizontalMonthPadding: 13, // navigation related props - customDayPickerNavigationStyles: null, + dayPickerNavigationInlineStyles: null, navPosition: NAV_POSITION_TOP, navPrev: null, navNext: null, @@ -398,7 +398,7 @@ class SingleDatePicker extends React.PureComponent { numberOfMonths, orientation, monthFormat, - customDayPickerNavigationStyles, + dayPickerNavigationInlineStyles, navPosition, navPrev, navNext, @@ -481,7 +481,7 @@ class SingleDatePicker extends React.PureComponent { keepOpenOnDateSelect={keepOpenOnDateSelect} hideKeyboardShortcutsPanel={hideKeyboardShortcutsPanel} initialVisibleMonth={initialVisibleMonth} - customDayPickerNavigationStyles={customDayPickerNavigationStyles} + dayPickerNavigationInlineStyles={dayPickerNavigationInlineStyles} navPosition={navPosition} navPrev={navPrev} navNext={navNext} diff --git a/src/shapes/DateRangePickerShape.js b/src/shapes/DateRangePickerShape.js index f83dabaca7..24efb386b3 100644 --- a/src/shapes/DateRangePickerShape.js +++ b/src/shapes/DateRangePickerShape.js @@ -79,7 +79,7 @@ export default { horizontalMonthPadding: nonNegativeInteger, // navigation related props - customDayPickerNavigationStyles: PropTypes.object, + dayPickerNavigationInlineStyles: PropTypes.object, navPosition: NavPositionShape, navPrev: PropTypes.node, navNext: PropTypes.node, diff --git a/src/shapes/SingleDatePickerShape.js b/src/shapes/SingleDatePickerShape.js index 7144edc661..b477375ccb 100644 --- a/src/shapes/SingleDatePickerShape.js +++ b/src/shapes/SingleDatePickerShape.js @@ -68,7 +68,7 @@ export default { horizontalMonthPadding: nonNegativeInteger, // navigation related props - customDayPickerNavigationStyles: PropTypes.object, + dayPickerNavigationInlineStyles: PropTypes.object, navPosition: NavPositionShape, navPrev: PropTypes.node, navNext: PropTypes.node, diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index f1b01abdb7..cc2de68c20 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -44,7 +44,7 @@ const TestPrevIcon = () => ( position: 'absolute', top: '20px', }} - tabindex="0" + tabIndex="0" > Prev @@ -61,7 +61,7 @@ const TestNextIcon = () => ( right: '22px', top: '20px', }} - tabindex="0" + tabIndex="0" > Next @@ -433,7 +433,7 @@ storiesOf('DayPickerRangeController', module) onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} navPrev={} navNext={} - customDayPickerNavigationStyles={{ + dayPickerNavigationInlineStyles={{ display: 'flex', justifyContent: 'space-between', }} From c82f9b06e5b721f7057d22f9d0e04ee8faae5bf0 Mon Sep 17 00:00:00 2001 From: casey_klimkowsky Date: Thu, 26 Sep 2019 09:41:19 -0700 Subject: [PATCH 515/618] Add more test coverage --- test/components/DayPickerNavigation_spec.jsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/components/DayPickerNavigation_spec.jsx b/test/components/DayPickerNavigation_spec.jsx index f3ad841b95..8698161c84 100644 --- a/test/components/DayPickerNavigation_spec.jsx +++ b/test/components/DayPickerNavigation_spec.jsx @@ -5,6 +5,8 @@ import { shallow } from 'enzyme'; import DayPickerNavigation from '../../src/components/DayPickerNavigation'; import { VERTICAL_SCROLLABLE } from '../../src/constants'; +import RightArrow from '../../src/components/RightArrow'; +import LeftArrow from '../../src/components/LeftArrow'; describe('DayPickerNavigation', () => { describe('#render', () => { @@ -46,6 +48,16 @@ describe('DayPickerNavigation', () => { expect(prevMonthButton.prop('tabIndex')).to.equal('0'); expect(nextMonthButton.prop('tabIndex')).to.equal('0'); }); + + it('uses RightArrow as the default prev icon for RTL', () => { + const wrapper = shallow().dive(); + expect(wrapper.childAt(0).find(RightArrow)).to.have.lengthOf(1); + }); + + it('uses LeftArrow as the default next icon for RTL', () => { + const wrapper = shallow().dive(); + expect(wrapper.childAt(1).find(LeftArrow)).to.have.lengthOf(1); + }); }); describe('interactions', () => { From 08e9079e3cb74cb4fe1c4c772b5a5ae15d09c1d0 Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Mon, 30 Sep 2019 23:17:36 -0700 Subject: [PATCH 516/618] Revert "Merge pull request #1758 from airbnb/nora--remove-direct-imports-of-css" This reverts commit 3e66eba4ba5d0d95bc704e01a32a579e2b1ea5d1, reversing changes made to 8c0ba9886a9f8410f65e65153e5a02e8c987ba08. --- examples/PresetDateRangePicker.jsx | 4 ++-- src/components/CalendarDay.jsx | 3 +-- src/components/CalendarMonth.jsx | 3 +-- src/components/CalendarMonthGrid.jsx | 3 +-- src/components/CustomizableCalendarDay.jsx | 3 +-- src/components/DateInput.jsx | 3 +-- src/components/DateRangePicker.jsx | 4 +--- src/components/DateRangePickerInput.jsx | 3 +-- src/components/DayPicker.jsx | 4 +--- src/components/DayPickerKeyboardShortcuts.jsx | 3 +-- src/components/DayPickerNavigation.jsx | 3 +-- src/components/KeyboardShortcutRow.jsx | 3 +-- src/components/SingleDatePicker.jsx | 4 +--- src/components/SingleDatePickerInput.jsx | 3 +-- 14 files changed, 15 insertions(+), 31 deletions(-) diff --git a/examples/PresetDateRangePicker.jsx b/examples/PresetDateRangePicker.jsx index c02ea3e123..ce8640a79b 100644 --- a/examples/PresetDateRangePicker.jsx +++ b/examples/PresetDateRangePicker.jsx @@ -4,7 +4,7 @@ import momentPropTypes from 'react-moment-proptypes'; import moment from 'moment'; import omit from 'lodash/omit'; -import { withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes, css } from 'react-with-styles'; import DateRangePicker from '../src/components/DateRangePicker'; @@ -123,7 +123,7 @@ class DateRangePickerWrapper extends React.Component { } renderDatePresets() { - const { presets, styles, css } = this.props; + const { presets, styles } = this.props; const { startDate, endDate } = this.state; return ( diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index 836a26e0cf..7e630093bf 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import momentPropTypes from 'react-moment-proptypes'; import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { withStyles, withStylesPropTypes } from 'react-with-styles'; +import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; import moment from 'moment'; import raf from 'raf'; @@ -107,7 +107,6 @@ class CalendarDay extends React.PureComponent { tabIndex, styles, phrases, - css, } = this.props; if (!day) return ; diff --git a/src/components/CalendarMonth.jsx b/src/components/CalendarMonth.jsx index 910b963905..226918b433 100644 --- a/src/components/CalendarMonth.jsx +++ b/src/components/CalendarMonth.jsx @@ -4,7 +4,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import momentPropTypes from 'react-moment-proptypes'; import { forbidExtraProps, mutuallyExclusiveProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { withStyles, withStylesPropTypes } from 'react-with-styles'; +import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; import moment from 'moment'; import { CalendarDayPhrases } from '../defaultPhrases'; @@ -152,7 +152,6 @@ class CalendarMonth extends React.PureComponent { render() { const { - css, dayAriaLabelFormat, daySize, focusedDate, diff --git a/src/components/CalendarMonthGrid.jsx b/src/components/CalendarMonthGrid.jsx index 7d23d972c1..bb43f01309 100644 --- a/src/components/CalendarMonthGrid.jsx +++ b/src/components/CalendarMonthGrid.jsx @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import momentPropTypes from 'react-moment-proptypes'; import { forbidExtraProps, mutuallyExclusiveProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { withStyles, withStylesPropTypes } from 'react-with-styles'; +import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; import moment from 'moment'; import { addEventListener } from 'consolidated-events'; @@ -265,7 +265,6 @@ class CalendarMonthGrid extends React.PureComponent { transitionDuration, verticalBorderSpacing, setMonthTitleHeight, - css, } = this.props; const { months } = this.state; diff --git a/src/components/CustomizableCalendarDay.jsx b/src/components/CustomizableCalendarDay.jsx index 97b983af57..2a3ee15752 100644 --- a/src/components/CustomizableCalendarDay.jsx +++ b/src/components/CustomizableCalendarDay.jsx @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import momentPropTypes from 'react-moment-proptypes'; import { forbidExtraProps, nonNegativeInteger, or } from 'airbnb-prop-types'; -import { withStyles, withStylesPropTypes } from 'react-with-styles'; +import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; import moment from 'moment'; import raf from 'raf'; @@ -272,7 +272,6 @@ class CustomizableCalendarDay extends React.PureComponent { render() { const { - css, day, ariaLabelFormat, daySize, diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index a119ac91c2..d50ecd9411 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { withStyles, withStylesPropTypes } from 'react-with-styles'; +import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; import throttle from 'lodash/throttle'; import isTouchDevice from 'is-touch-device'; @@ -190,7 +190,6 @@ class DateInput extends React.PureComponent { block, styles, theme: { reactDates }, - css, } = this.props; const value = dateString || displayValue || ''; diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index e30827306e..c1b18b09ca 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -1,6 +1,6 @@ import React from 'react'; import moment from 'moment'; -import { withStyles, withStylesPropTypes } from 'react-with-styles'; +import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; import { Portal } from 'react-portal'; import { forbidExtraProps } from 'airbnb-prop-types'; import { addEventListener } from 'consolidated-events'; @@ -398,7 +398,6 @@ class DateRangePicker extends React.PureComponent { renderDayPicker() { const { anchorDirection, - css, openDirection, isDayBlocked, isDayHighlighted, @@ -559,7 +558,6 @@ class DateRangePicker extends React.PureComponent { render() { const { - css, startDate, startDateId, startDatePlaceholderText, diff --git a/src/components/DateRangePickerInput.jsx b/src/components/DateRangePickerInput.jsx index 5989b4f94f..2389595852 100644 --- a/src/components/DateRangePickerInput.jsx +++ b/src/components/DateRangePickerInput.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { withStyles, withStylesPropTypes } from 'react-with-styles'; +import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; import { DateRangePickerInputPhrases } from '../defaultPhrases'; import getPhrasePropTypes from '../utils/getPhrasePropTypes'; @@ -132,7 +132,6 @@ const defaultProps = { function DateRangePickerInput({ children, - css, startDate, startDateId, startDatePlaceholderText, diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index 4c0b1f9543..fc1cc7a726 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps, mutuallyExclusiveProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { withStyles, withStylesPropTypes } from 'react-with-styles'; +import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; import moment from 'moment'; import throttle from 'lodash/throttle'; @@ -884,7 +884,6 @@ class DayPicker extends React.PureComponent { orientation, renderWeekHeaderElement, styles, - css, } = this.props; const { calendarMonthWidth } = this.state; @@ -946,7 +945,6 @@ class DayPicker extends React.PureComponent { } = this.state; const { - css, enableOutsideDays, numberOfMonths, orientation, diff --git a/src/components/DayPickerKeyboardShortcuts.jsx b/src/components/DayPickerKeyboardShortcuts.jsx index f06028b73b..2861d3eda5 100644 --- a/src/components/DayPickerKeyboardShortcuts.jsx +++ b/src/components/DayPickerKeyboardShortcuts.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps } from 'airbnb-prop-types'; -import { withStyles, withStylesPropTypes } from 'react-with-styles'; +import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; import { DayPickerKeyboardShortcutsPhrases } from '../defaultPhrases'; import getPhrasePropTypes from '../utils/getPhrasePropTypes'; @@ -164,7 +164,6 @@ class DayPickerKeyboardShortcuts extends React.PureComponent { const { block, buttonLocation, - css, showKeyboardShortcutsPanel, closeKeyboardShortcutsPanel, styles, diff --git a/src/components/DayPickerNavigation.jsx b/src/components/DayPickerNavigation.jsx index 61a8b9ea33..ee4f367503 100644 --- a/src/components/DayPickerNavigation.jsx +++ b/src/components/DayPickerNavigation.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps } from 'airbnb-prop-types'; -import { withStyles, withStylesPropTypes } from 'react-with-styles'; +import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; import { DayPickerNavigationPhrases } from '../defaultPhrases'; import getPhrasePropTypes from '../utils/getPhrasePropTypes'; @@ -59,7 +59,6 @@ const defaultProps = { }; function DayPickerNavigation({ - css, inlineStyles, disablePrev, disableNext, diff --git a/src/components/KeyboardShortcutRow.jsx b/src/components/KeyboardShortcutRow.jsx index fa719c07ac..7e3d2401c4 100644 --- a/src/components/KeyboardShortcutRow.jsx +++ b/src/components/KeyboardShortcutRow.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps } from 'airbnb-prop-types'; -import { withStyles, withStylesPropTypes } from 'react-with-styles'; +import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; const propTypes = forbidExtraProps({ ...withStylesPropTypes, @@ -16,7 +16,6 @@ const defaultProps = { }; function KeyboardShortcutRow({ - css, unicode, label, action, diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index bbdb9d0127..7fb43c0dbc 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -1,6 +1,6 @@ import React from 'react'; import moment from 'moment'; -import { withStyles, withStylesPropTypes } from 'react-with-styles'; +import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; import { Portal } from 'react-portal'; import { forbidExtraProps } from 'airbnb-prop-types'; import { addEventListener } from 'consolidated-events'; @@ -388,7 +388,6 @@ class SingleDatePicker extends React.PureComponent { renderDayPicker() { const { anchorDirection, - css, openDirection, onDateChange, date, @@ -530,7 +529,6 @@ class SingleDatePicker extends React.PureComponent { render() { const { - css, id, placeholder, ariaLabel, diff --git a/src/components/SingleDatePickerInput.jsx b/src/components/SingleDatePickerInput.jsx index 88dbac23ff..af7b4d6731 100644 --- a/src/components/SingleDatePickerInput.jsx +++ b/src/components/SingleDatePickerInput.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { withStyles, withStylesPropTypes } from 'react-with-styles'; +import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; import { SingleDatePickerInputPhrases } from '../defaultPhrases'; import getPhrasePropTypes from '../utils/getPhrasePropTypes'; @@ -95,7 +95,6 @@ const defaultProps = { function SingleDatePickerInput({ id, children, - css, placeholder, ariaLabel, displayValue, From d5f747ece8fbd2710f4ee1b780e6bb8fd674defd Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Tue, 1 Oct 2019 11:28:57 -0700 Subject: [PATCH 517/618] Version 21.2.0 --- CHANGELOG.md | 7 ++++++- package.json | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ddd64c4789..b35c61fff6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,12 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> -## Unreleased +## 21.2.0 + +- [fix] Revert "Merge pull request [#1758](https://github.com/airbnb/react-dates/pull/1758): Remove all direct imports of css in favor of injected prop" ([#1818](https://github.com/airbnb/react-dates/pull/1818)) +- [fix] Fix for getWeekHeaders(), prevents it from changing state.currentMonth ([#1796](https://github.com/airbnb/react-dates/pull/1796)) +- [new] Add support for positioning month navigation under the calendar ([#1784](https://github.com/airbnb/react-dates/pull/1784)) +- [new] Add minDate and maxDate props to DateRangePicker ([#1793](https://github.com/airbnb/react-dates/pull/1793), [#1794](https://github.com/airbnb/react-dates/pull/1794)) ## 21.1.0 diff --git a/package.json b/package.json index 9cafa75eeb..b0dc9ee181 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "21.1.0", + "version": "21.2.0", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From defb7fd680cab4154d12e9416c9f28e4a47e9e5b Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Fri, 11 Oct 2019 20:16:34 -0700 Subject: [PATCH 518/618] Revert "[Tests] `DayPicker`: unit test for getWeekHeaders()" This reverts commit 5584f822fdcc3fa266deff62756ac887b4c0d2af. --- test/components/DayPicker_spec.jsx | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/test/components/DayPicker_spec.jsx b/test/components/DayPicker_spec.jsx index f3df0e776b..63fb8e506c 100644 --- a/test/components/DayPicker_spec.jsx +++ b/test/components/DayPicker_spec.jsx @@ -3,7 +3,6 @@ import moment from 'moment/min/moment-with-locales'; import { expect } from 'chai'; import sinon from 'sinon-sandbox'; import { mount, shallow } from 'enzyme'; -import { cloneDeep } from 'lodash'; import * as isDayVisible from '../../src/utils/isDayVisible'; @@ -872,23 +871,6 @@ describe('DayPicker', () => { }); }); - describe('#getWeekHeaders', () => { - it('returns unmutated weekday headers for currentMonth in a future', () => { - sinon.stub(PureDayPicker.prototype, 'render'); - - const getWeekHeadersSpy = sinon.spy(PureDayPicker.prototype, 'getWeekHeaders'); - const INITIAL_MONTH = moment().add(2, 'Months').week(3).weekday(3); - const wrapper = shallow( INITIAL_MONTH} />).dive(); - const instance = wrapper.instance(); - const state = cloneDeep(wrapper.state()); - - expect(instance.getWeekHeaders()).to.be.eql(INITIAL_MONTH.localeData().weekdaysMin()); - expect(instance.state).not.to.equal(state); - expect(instance.state).to.eql(state); - expect(getWeekHeadersSpy).to.have.property('callCount', 1); - }); - }); - describe.skip('life cycle methods', () => { let adjustDayPickerHeightSpy; beforeEach(() => { From a449b9af9c5539a445bd361f8afede68ce9286c0 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Fri, 11 Oct 2019 19:50:18 -0700 Subject: [PATCH 519/618] Remove erroneous .only and turn off react/no-deprecated rule --- .eslintrc | 2 ++ test/components/DayPickerRangeController_spec.jsx | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index 454e2a80d0..bcb6102a29 100644 --- a/.eslintrc +++ b/.eslintrc @@ -27,6 +27,8 @@ "no-restricted-imports": 0, // TODO: enable with full RTL support "react/jsx-props-no-spreading": 0, // TODO: re-evaluate + + "react/no-deprecated": 1, // TODO: update to UNSAFE_componentWillReceiveProps }, "overrides": [ diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index d8432f3655..ef57e6d461 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -4492,7 +4492,7 @@ describe('DayPickerRangeController', () => { }); }); - describe.only('renderKeyboardShortcutsPanel prop', () => { + describe('renderKeyboardShortcutsPanel prop', () => { it('passes down custom panel render function', () => { const testRenderKeyboardShortcutsPanel = () => {}; const wrapper = shallow( From 862c99f7d346e0e02e77c79bbc90bf202c64c002 Mon Sep 17 00:00:00 2001 From: casey_klimkowsky Date: Fri, 11 Oct 2019 16:50:53 -0700 Subject: [PATCH 520/618] Call getStateForNewMonth when date/startDate/endDate is set to a date that is not visible --- src/components/DayPickerRangeController.jsx | 20 +++- .../DayPickerSingleDateController.jsx | 19 ++-- .../DayPickerRangeController_spec.jsx | 99 ++++++++++++++----- .../DayPickerSingleDateController_spec.jsx | 24 +++++ 4 files changed, 127 insertions(+), 35 deletions(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index b96230f127..c665c6afbb 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -18,6 +18,7 @@ import isBeforeDay from '../utils/isBeforeDay'; import getVisibleDays from '../utils/getVisibleDays'; import isDayVisible from '../utils/isDayVisible'; +import getPooledMoment from '../utils/getPooledMoment'; import getSelectedDateOffset from '../utils/getSelectedDateOffset'; import toISODateString from '../utils/toISODateString'; @@ -41,7 +42,6 @@ import { } from '../constants'; import DayPicker from './DayPicker'; -import getPooledMoment from '../utils/getPooledMoment'; const propTypes = forbidExtraProps({ startDate: momentPropTypes.momentObj, @@ -284,6 +284,8 @@ export default class DayPickerRangeController extends React.PureComponent { } = this.props; const { hoverDate } = this.state; + + let { currentMonth } = this.state; let { visibleDays } = this.state; let recomputeOutsideRange = false; @@ -313,6 +315,18 @@ export default class DayPickerRangeController extends React.PureComponent { const didEndDateChange = endDate !== prevEndDate; const didFocusChange = focusedInput !== prevFocusedInput; + let isDateVisible = true; + + if (didStartDateChange || didEndDateChange) { + if (didStartDateChange) { + isDateVisible = isDayVisible(startDate, currentMonth, numberOfMonths, enableOutsideDays); + } + + if (didEndDateChange) { + isDateVisible = isDayVisible(endDate, currentMonth, numberOfMonths, enableOutsideDays); + } + } + if ( numberOfMonths !== prevNumberOfMonths || enableOutsideDays !== prevEnableOutsideDays @@ -321,10 +335,10 @@ export default class DayPickerRangeController extends React.PureComponent { && !prevFocusedInput && didFocusChange ) + || !isDateVisible ) { const newMonthState = this.getStateForNewMonth(nextProps); - const { currentMonth } = newMonthState; - ({ visibleDays } = newMonthState); + ({ currentMonth, visibleDays } = newMonthState); this.setState({ currentMonth, visibleDays, diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index f9b9e1be4e..4c65c66707 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -13,6 +13,7 @@ import isSameDay from '../utils/isSameDay'; import isAfterDay from '../utils/isAfterDay'; import getVisibleDays from '../utils/getVisibleDays'; +import isDayVisible from '../utils/isDayVisible'; import toISODateString from '../utils/toISODateString'; import { addModifier, deleteModifier } from '../utils/modifiers'; @@ -219,6 +220,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { focused: prevFocused, date: prevDate, } = this.props; + let { currentMonth } = this.state; let { visibleDays } = this.state; let recomputeOutsideRange = false; @@ -244,6 +246,15 @@ export default class DayPickerSingleDateController extends React.PureComponent { recomputeOutsideRange || recomputeDayBlocked || recomputeDayHighlighted ); + const didDateChange = date !== prevDate; + const didFocusChange = focused !== prevFocused; + + let isDateVisible = true; + + if (didDateChange) { + isDateVisible = isDayVisible(date, currentMonth, numberOfMonths, enableOutsideDays); + } + if ( numberOfMonths !== prevNumberOfMonths || enableOutsideDays !== prevEnableOutsideDays @@ -251,20 +262,16 @@ export default class DayPickerSingleDateController extends React.PureComponent { initialVisibleMonth !== prevInitialVisibleMonth && !prevFocused && focused - ) + ) || !isDateVisible ) { const newMonthState = this.getStateForNewMonth(nextProps); - const { currentMonth } = newMonthState; - ({ visibleDays } = newMonthState); + ({ currentMonth, visibleDays } = newMonthState); this.setState({ currentMonth, visibleDays, }); } - const didDateChange = date !== prevDate; - const didFocusChange = focused !== prevFocused; - let modifiers = {}; if (didDateChange) { diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index ef57e6d461..462380e9eb 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -297,33 +297,80 @@ describe('DayPickerRangeController', () => { }); expect(wrapper.instance().state.visibleDays).to.equal(visibleDays); }); - describe('startDate changed from one date to another', () => { - it('removes previous `after-hovered-start` range', () => { - const minimumNights = 5; - const startDate = moment().add(7, 'days'); - const dayAfterStartDate = startDate.clone().add(1, 'day'); - const firstAvailableDate = startDate.clone().add(minimumNights + 1, 'days'); - const deleteModifierFromRangeSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifierFromRange'); - const nextStartDate = moment().add(4, 'days'); - const wrapper = shallow(( - - )); - deleteModifierFromRangeSpy.resetHistory(); - wrapper.instance().componentWillReceiveProps({ - ...props, - startDate: nextStartDate, - }); - const afterHoverStartCalls = getCallsByModifier(deleteModifierFromRangeSpy, 'after-hovered-start'); - expect(afterHoverStartCalls.length).to.equal(1); - expect(isSameDay(afterHoverStartCalls[0].args[1], dayAfterStartDate)).to.equal(true); - expect(isSameDay(afterHoverStartCalls[0].args[2], firstAvailableDate)).to.equal(true); + }); + + describe('startDate changed from one date to another', () => { + it('removes previous `after-hovered-start` range', () => { + const minimumNights = 5; + const startDate = moment().add(7, 'days'); + const dayAfterStartDate = startDate.clone().add(1, 'day'); + const firstAvailableDate = startDate.clone().add(minimumNights + 1, 'days'); + const deleteModifierFromRangeSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifierFromRange'); + const nextStartDate = moment().add(4, 'days'); + const wrapper = shallow(( + + )); + deleteModifierFromRangeSpy.resetHistory(); + wrapper.instance().componentWillReceiveProps({ + ...props, + startDate: nextStartDate, }); + const afterHoverStartCalls = getCallsByModifier(deleteModifierFromRangeSpy, 'after-hovered-start'); + expect(afterHoverStartCalls.length).to.equal(1); + expect(isSameDay(afterHoverStartCalls[0].args[1], dayAfterStartDate)).to.equal(true); + expect(isSameDay(afterHoverStartCalls[0].args[2], firstAvailableDate)).to.equal(true); + }); + + it('calls getStateForNewMonth with nextProps when date is not visible', () => { + const getStateForNewMonthSpy = sinon.spy( + DayPickerRangeController.prototype, + 'getStateForNewMonth', + ); + const startDate = moment(); + const nextStartDate = startDate.clone().add(2, 'months'); + + const wrapper = shallow(( + + )); + + getStateForNewMonthSpy.resetHistory(); + + wrapper.instance().componentWillReceiveProps({ + ...props, + startDate: nextStartDate, + }); + + expect(getStateForNewMonthSpy.callCount).to.equal(1); + }); + }); + + describe('endDate changed from one date to another', () => { + it('calls getStateForNewMonth with nextProps when date is not visible', () => { + const getStateForNewMonthSpy = sinon.spy( + DayPickerRangeController.prototype, + 'getStateForNewMonth', + ); + const endDate = moment(); + const nextEndDate = endDate.clone().add(2, 'months'); + + const wrapper = shallow(( + + )); + + getStateForNewMonthSpy.resetHistory(); + + wrapper.instance().componentWillReceiveProps({ + ...props, + endDate: nextEndDate, + }); + + expect(getStateForNewMonthSpy.callCount).to.equal(1); }); }); }); diff --git a/test/components/DayPickerSingleDateController_spec.jsx b/test/components/DayPickerSingleDateController_spec.jsx index 6cd614a91c..e7154ccef8 100644 --- a/test/components/DayPickerSingleDateController_spec.jsx +++ b/test/components/DayPickerSingleDateController_spec.jsx @@ -46,6 +46,30 @@ describe('DayPickerSingleDateController', () => { onFocusChange() {}, }; + describe('date changed from one date to another', () => { + it('calls getStateForNewMonth with nextProps when date is not visible', () => { + const getStateForNewMonthSpy = sinon.spy( + DayPickerSingleDateController.prototype, + 'getStateForNewMonth', + ); + const date = moment(); + const nextDate = date.clone().add(2, 'months'); + + const wrapper = shallow(( + + )); + + getStateForNewMonthSpy.resetHistory(); + + wrapper.instance().componentWillReceiveProps({ + ...props, + date: nextDate, + }); + + expect(getStateForNewMonthSpy.callCount).to.equal(1); + }); + }); + describe('modifiers', () => { describe('selected modifier', () => { describe('props.date did not change', () => { From c262f67fb32148e26d858733fb9a550da580b0ef Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Fri, 11 Oct 2019 20:50:47 -0700 Subject: [PATCH 521/618] Version 21.2.1 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b35c61fff6..0ec4332b3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## 21.2.1 + +- [fix] Call getStateForNewMonth when date/startDate/endDate is set to a date that is not visible ([#1834](https://github.com/airbnb/react-dates/pull/1834)) + ## 21.2.0 - [fix] Revert "Merge pull request [#1758](https://github.com/airbnb/react-dates/pull/1758): Remove all direct imports of css in favor of injected prop" ([#1818](https://github.com/airbnb/react-dates/pull/1818)) diff --git a/package.json b/package.json index b0dc9ee181..a07fc5348b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "21.2.0", + "version": "21.2.1", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From efaf200acb0cda403754d3fdf37448670ce3514d Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Fri, 18 Oct 2019 17:11:43 -0700 Subject: [PATCH 522/618] Update react-with-styles 4.0.1 -> 4.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a07fc5348b..80137cb9d3 100644 --- a/package.json +++ b/package.json @@ -127,7 +127,7 @@ "react-outside-click-handler": "^1.2.4", "react-portal": "^4.2.0", "react-with-direction": "^1.3.1", - "react-with-styles": "^4.0.1", + "react-with-styles": "^4.1.0-alpha.1", "react-with-styles-interface-css": "^6.0.0" }, "peerDependencies": { From e80c45e340d00830575156f6af1e2c9de11e6d35 Mon Sep 17 00:00:00 2001 From: Kris Salvador Date: Mon, 21 Oct 2019 11:56:38 -0400 Subject: [PATCH 523/618] Fix tests --- src/components/DayPickerRangeController.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index ff4153be68..734f87cfa6 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -223,11 +223,11 @@ export default class DayPickerRangeController extends React.PureComponent { 'last-day-of-week': (day) => this.isLastDayOfWeek(day), 'hovered-start-first-possible-end': (day, hoverDate) => this.isFirstPossibleEndDateForHoveredStartDate(day, hoverDate), 'hovered-start-blocked-minimum-nights': (day, hoverDate) => this.doesNotMeetMinNightsForHoveredStartDate(day, hoverDate), - 'before-hovered-end': day => this.isDayBeforeHoveredEndDate(day), - 'no-selected-start-before-selected-end': day => this.beforeSelectedEnd(day) && !props.startDate, + 'before-hovered-end': (day) => this.isDayBeforeHoveredEndDate(day), + 'no-selected-start-before-selected-end': (day) => this.beforeSelectedEnd(day) && !props.startDate, 'selected-start-in-hovered-span': (day, hoverDate) => this.isStartDate(day) && isAfterDay(hoverDate, day), - 'selected-start-no-selected-end': day => this.isStartDate(day) && !props.endDate, - 'selected-end-no-selected-start': day => this.isEndDate(day) && !props.startDate, + 'selected-start-no-selected-end': (day) => this.isStartDate(day) && !props.endDate, + 'selected-end-no-selected-start': (day) => this.isEndDate(day) && !props.startDate, }; const { currentMonth, visibleDays } = this.getStateForNewMonth(props); From d3ad5ce1d12cd5a3ec113ff834d5ceb0ff1d9366 Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Mon, 21 Oct 2019 17:54:29 -0700 Subject: [PATCH 524/618] Version 21.3.0 --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ec4332b3c..1a2aff8675 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## 21.3.0 + +- [new] Update react-with-styles 4.0.1 -> 4.1.0 ([#1843](https://github.com/airbnb/react-dates/pull/1843)) +- [new] Add Input Font-Weight to Default Theme ([#1765](https://github.com/airbnb/react-dates/pull/#1765)) + ## 21.2.1 - [fix] Call getStateForNewMonth when date/startDate/endDate is set to a date that is not visible ([#1834](https://github.com/airbnb/react-dates/pull/1834)) diff --git a/package.json b/package.json index 80137cb9d3..65f2f826d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "21.2.1", + "version": "21.3.0", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From bcd1a09e76c65479156a7e78e8ed6f8853ea2f41 Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Mon, 21 Oct 2019 18:00:24 -0700 Subject: [PATCH 525/618] Update react-with-styles version to non-alpha release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 65f2f826d3..0dddc47a72 100644 --- a/package.json +++ b/package.json @@ -127,7 +127,7 @@ "react-outside-click-handler": "^1.2.4", "react-portal": "^4.2.0", "react-with-direction": "^1.3.1", - "react-with-styles": "^4.1.0-alpha.1", + "react-with-styles": "^4.1.0", "react-with-styles-interface-css": "^6.0.0" }, "peerDependencies": { From 0362c92889fe517ebd559fccd2ad85e76a8b3e85 Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Mon, 21 Oct 2019 18:04:19 -0700 Subject: [PATCH 526/618] Version 21.3.1 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a2aff8675..433a704764 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## 21.3.1 + +- [fix] Update react-with-styles 4.1.0-alpha.1 -> 4.1.0 + ## 21.3.0 - [new] Update react-with-styles 4.0.1 -> 4.1.0 ([#1843](https://github.com/airbnb/react-dates/pull/1843)) diff --git a/package.json b/package.json index 0dddc47a72..21c74736d5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "21.3.0", + "version": "21.3.1", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From f07dbfae22f8c879c1b44127b374932f0c689d2e Mon Sep 17 00:00:00 2001 From: Kris Salvador Date: Mon, 21 Oct 2019 17:38:34 -0400 Subject: [PATCH 527/618] Add tests for modifiers --- src/components/DayPickerRangeController.jsx | 9 +- .../DayPickerRangeController_spec.jsx | 375 +++++++++++++++++- 2 files changed, 373 insertions(+), 11 deletions(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 734f87cfa6..b932092af2 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -385,7 +385,7 @@ export default class DayPickerRangeController extends React.PureComponent { modifiers = this.addModifier(modifiers, endDate, 'selected-end'); if (prevEndDate && (!startDate || !prevStartDate)) { - modifiers = this.deleteModifier(modifiers, prevEndDate, 'selected-end-no-start-date'); + modifiers = this.deleteModifier(modifiers, prevEndDate, 'selected-end-no-selected-start'); } } @@ -756,11 +756,10 @@ export default class DayPickerRangeController extends React.PureComponent { if (isSameDay(day, endDate)) { const newStartSpan = endDate.clone().subtract(minimumNights, 'days'); - const newEndSpan = endDate.clone(); modifiers = this.addModifierToRange( modifiers, newStartSpan, - newEndSpan, + endDate, 'before-hovered-end', ); } @@ -823,6 +822,7 @@ export default class DayPickerRangeController extends React.PureComponent { minimumNights, } = this.props; const { hoverDate, visibleDays, dateOffset } = this.state; + if (this.isTouchDevice || !hoverDate) return; let modifiers = {}; @@ -861,8 +861,7 @@ export default class DayPickerRangeController extends React.PureComponent { if (endDate && isSameDay(day, endDate)) { const startSpan = endDate.clone().subtract(minimumNights, 'days'); - const endSpan = endDate.clone(); - modifiers = this.deleteModifierFromRange(modifiers, startSpan, endSpan, 'before-hovered-end'); + modifiers = this.deleteModifierFromRange(modifiers, startSpan, endDate, 'before-hovered-end'); } if (!this.isBlocked(hoverDate)) { diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index 1b77d7a9d6..37c2ca33cc 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -13,6 +13,7 @@ import toISODateString from '../../src/utils/toISODateString'; import toISOMonthString from '../../src/utils/toISOMonthString'; import isInclusivelyAfterDay from '../../src/utils/isInclusivelyAfterDay'; import isSameDay from '../../src/utils/isSameDay'; +import isBeforeDay from '../../src/utils/isBeforeDay'; import * as isDayVisible from '../../src/utils/isDayVisible'; import getVisibleDays from '../../src/utils/getVisibleDays'; @@ -40,8 +41,8 @@ describe('DayPickerRangeController', () => { describe('#componentDidMount', () => { const props = { ...DayPickerRangeController.defaultProps, - onDatesChange() { }, - onFocusChange() { }, + onDatesChange() {}, + onFocusChange() {}, }; describe('phrases', () => { @@ -98,8 +99,8 @@ describe('DayPickerRangeController', () => { describe('#componentWillReceiveProps', () => { const props = { ...DayPickerRangeController.defaultProps, - onDatesChange() { }, - onFocusChange() { }, + onDatesChange() {}, + onFocusChange() {}, }; describe('rebuilding currentMonth/visibleDays', () => { @@ -351,6 +352,36 @@ describe('DayPickerRangeController', () => { }); describe('endDate changed from one date to another', () => { + it('removes previous `selected-end-no-start-date` when no start date selected', () => { + const minimumNights = 5; + const endDate = moment().add(7, 'days'); + const deleteModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifier'); + const addModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'addModifier'); + const nextEndDate = moment().add(4, 'days'); + const wrapper = shallow(( + + )); + deleteModifierSpy.resetHistory(); + addModifierSpy.resetHistory(); + wrapper.instance().componentWillReceiveProps({ + ...props, + endDate: nextEndDate, + }); + const selectedEndNoStartDateDelete = getCallsByModifier(deleteModifierSpy, 'selected-end-no-selected-start'); + expect(selectedEndNoStartDateDelete.length).to.equal(1); + expect(isSameDay(selectedEndNoStartDateDelete[0].args[1], endDate)).to.equal(true); + + const selectedEndNoStartDateAdd = getCallsByModifier(addModifierSpy, 'selected-end-no-selected-start'); + expect(selectedEndNoStartDateAdd.length).to.equal(1); + expect(isSameDay(selectedEndNoStartDateAdd[0].args[1], nextEndDate)).to.equal(true); + }); + it('calls getStateForNewMonth with nextProps when date is not visible', () => { const getStateForNewMonthSpy = sinon.spy( DayPickerRangeController.prototype, @@ -1576,6 +1607,100 @@ describe('DayPickerRangeController', () => { }); }); }); + + describe('no-selected-start-before-selected-end', () => { + describe('start date has changed, start date is falsey, and end date is truthy', () => { + it('calls addModifier with `no-selected-start-before-selected-end` if day is before selected end date', () => { + const addModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'addModifier'); + const startDate = moment().add(1, 'days'); + const endDate = startDate.clone().add(1, 'days'); + const wrapper = shallow(); + const newEndDate = endDate.clone().add(1, 'days'); + wrapper.instance().componentWillReceiveProps({ ...props, startDate: null, endDate: newEndDate }); + const noSelectedStartBeforeSelectedEndCalls = getCallsByModifier(addModifierSpy, 'no-selected-start-before-selected-end'); + noSelectedStartBeforeSelectedEndCalls.forEach((eachCall) => { + const day = eachCall.args[1]; + + expect(isBeforeDay(day, newEndDate)).to.equal(true); + }); + }); + }); + }); + + describe('selected-start-no-selected-end', () => { + describe('start date is truthy, and end date is falsey', () => { + it('calls addModifier with `selected-start-no-selected-end`', () => { + const addModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'addModifier'); + const wrapper = shallow(); + const startDate = moment(); + wrapper.instance().componentWillReceiveProps({ ...props, startDate }); + const selectedStartNoSelectedEndCalls = getCallsByModifier(addModifierSpy, 'selected-start-no-selected-end'); + expect(selectedStartNoSelectedEndCalls.length).to.equal(1); + expect(selectedStartNoSelectedEndCalls[0].args[1]).to.equal(startDate); + }); + }); + + + describe('start date has changed, and end date or previous end date are falsey', () => { + it('calls deleteModifier with `selected-start-no-selected-end`', () => { + const deleteModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifier'); + const startDate = moment(); + const wrapper = shallow(); + const newStartDate = startDate.clone().add(1, 'days'); + wrapper.instance().componentWillReceiveProps({ ...props, startDate: newStartDate }); + const selectedStartNoSelectedEndCalls = getCallsByModifier(deleteModifierSpy, 'selected-start-no-selected-end'); + expect(selectedStartNoSelectedEndCalls.length).to.equal(1); + expect(selectedStartNoSelectedEndCalls[0].args[1]).to.equal(startDate); + }); + }); + }); + + describe('selected-end-no-selected-start', () => { + describe('end date is truthy, and start date is falsey', () => { + it('calls addModifier with `selected-end-no-selected-start`', () => { + const addModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'addModifier'); + const wrapper = shallow(); + const endDate = moment(); + wrapper.instance().componentWillReceiveProps({ ...props, endDate }); + const selectedStartNoSelectedEndCalls = getCallsByModifier(addModifierSpy, 'selected-end-no-selected-start'); + expect(selectedStartNoSelectedEndCalls.length).to.equal(1); + expect(selectedStartNoSelectedEndCalls[0].args[1]).to.equal(endDate); + }); + }); + + describe('end date has changed, and start date and previous start date are falsey', () => { + it('calls deleteModifier with `selected-end-no-selected-start`', () => { + const deleteModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifier'); + const startDate = moment(); + const wrapper = shallow(); + const newStartDate = startDate.clone().add(1, 'days'); + wrapper.instance().componentWillReceiveProps({ ...props, startDate: newStartDate }); + const selectedStartNoSelectedEndCalls = getCallsByModifier(deleteModifierSpy, 'selected-end-no-selected-start'); + expect(selectedStartNoSelectedEndCalls.length).to.equal(1); + expect(selectedStartNoSelectedEndCalls[0].args[1]).to.equal(startDate); + }); + }); + }); + + describe('before-hovered-end', () => { + describe('end date changed, end date is truthy and start date is falsey', () => { + it('calls addModifierToRange with `before-hovered-end`', () => { + const minimumNights = 1; + const addModifierToRangeSpy = sinon.spy(DayPickerRangeController.prototype, 'addModifierToRange'); + const endDate = moment(); + const wrapper = shallow(); + const newEndDate = endDate.clone().add(1, 'days'); + addModifierToRangeSpy.resetHistory(); + wrapper.instance().componentWillReceiveProps({ ...props, endDate: newEndDate }); + const beforeHoveredEndCalls = getCallsByModifier(addModifierToRangeSpy, 'before-hovered-end'); + expect(beforeHoveredEndCalls.length).to.equal(1); + expect(toISODateString(beforeHoveredEndCalls[0].args[1])).to.equal(toISODateString(newEndDate.clone().subtract(minimumNights, 'days'))); + expect(toISODateString(beforeHoveredEndCalls[0].args[2])).to.equal(toISODateString(newEndDate)); + }); + }); + }); + + }); describe('phrases', () => { @@ -2770,6 +2895,124 @@ describe('DayPickerRangeController', () => { }); }); }); + + describe('selected-start-in-hovered-span modifier', () => { + describe('end date is falsey and focusedInput === `END_DATE`', () => { + describe('day is start date or before start date', () => { + it('calls deleteModifier with `selected-start-in-hovered-span` on start date', () => { + const deleteModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifier'); + const startDate = today; + const wrapper = shallow(); + const yesterday = today.clone().subtract(1, 'days'); + deleteModifierSpy.resetHistory(); + wrapper.instance().onDayMouseEnter(yesterday); + const deleteModifierCalls = getCallsByModifier(deleteModifierSpy, 'selected-start-in-hovered-span'); + expect(deleteModifierCalls.length).to.equal(1); + expect(deleteModifierCalls[0].args[1]).to.equal(startDate); + }); + }); + + describe('day is not blocked, and is after the start date', () => { + it('calls addModifier with `selected-start-in-hovered-span` on start date', () => { + const addModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'addModifier'); + const startDate = today; + const wrapper = shallow(); + const tomorrow = today.clone().add(1, 'days'); + addModifierSpy.resetHistory(); + wrapper.instance().onDayMouseEnter(tomorrow); + const addModifierCalls = getCallsByModifier(addModifierSpy, 'selected-start-in-hovered-span'); + expect(addModifierCalls.length).to.equal(1); + expect(addModifierCalls[0].args[1]).to.equal(startDate); + }); + }); + }); + }); + + describe('selected-end-in-hovered-span modifier', () => { + describe('start date is falsey and focusedInput === `START_DATE`', () => { + describe('day is end date or after start date', () => { + it('calls deleteModifier with `selected-end-in-hovered-span` on end date', () => { + const deleteModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifier'); + const endDate = today; + const wrapper = shallow(); + const tomorrow = today.clone().add(1, 'days'); + deleteModifierSpy.resetHistory(); + wrapper.instance().onDayMouseEnter(tomorrow); + const deleteModifierCalls = getCallsByModifier(deleteModifierSpy, 'selected-end-in-hovered-span'); + expect(deleteModifierCalls.length).to.equal(1); + expect(deleteModifierCalls[0].args[1]).to.equal(endDate); + }); + }); + + describe('day is not blocked, and is before the end date', () => { + it('calls addModifier with `selected-end-in-hovered-span`', () => { + const addModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'addModifier'); + const endDate = today; + const wrapper = shallow(); + const yesterday = today.clone().subtract(1, 'days'); + addModifierSpy.resetHistory(); + wrapper.instance().onDayMouseEnter(yesterday); + const addModifierCalls = getCallsByModifier(addModifierSpy, 'selected-end-in-hovered-span'); + expect(addModifierCalls.length).to.equal(1); + expect(addModifierCalls[0].args[1]).to.equal(today); + }); + }); + }); + }); + + describe('before-hovered-end modifier', () => { + describe('end date is truthy and focusedInput is truthy', () => { + it('calls deleteModifierFromRange with `before-hovered-end` on minimum nights days before end date', () => { + const deleteModifierFromRangeSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifierFromRange'); + const endDate = today; + const minimumNights = 5; + const wrapper = shallow(); + const minimumNightStartSpan = endDate.clone().subtract(minimumNights, 'days'); + deleteModifierFromRangeSpy.resetHistory(); + wrapper.instance().onDayMouseEnter(today); + const deleteModifierFromRangeCalls = getCallsByModifier(deleteModifierFromRangeSpy, 'before-hovered-end'); + expect(deleteModifierFromRangeCalls.length).to.equal(1); + expect(toISODateString(deleteModifierFromRangeCalls[0].args[1])).to.equal(toISODateString(minimumNightStartSpan)); + expect(deleteModifierFromRangeCalls[0].args[2]).to.equal(endDate); + }); + }); + + describe('day is equal to end date', () => { + it('calls addModifierToRange with `before-hovered-end`', () => { + const addModifierFromRangeSpy = sinon.spy(DayPickerRangeController.prototype, 'addModifierToRange'); + const endDate = today; + const minimumNights = 5; + const wrapper = shallow(); + const minimumNightStartSpan = endDate.clone().subtract(minimumNights, 'days'); + addModifierFromRangeSpy.resetHistory(); + wrapper.instance().onDayMouseEnter(today); + const addModifierFromRangeCalls = getCallsByModifier(addModifierFromRangeSpy, 'before-hovered-end'); + expect(addModifierFromRangeCalls.length).to.equal(1); + expect(toISODateString(addModifierFromRangeCalls[0].args[1])).to.equal(toISODateString(minimumNightStartSpan)); + expect(addModifierFromRangeCalls[0].args[2]).to.equal(endDate); + }); + }); + }); }); }); @@ -3004,7 +3247,7 @@ describe('DayPickerRangeController', () => { expect(isSameDay(hoveredStartBlockedMinNightsCalls[0].args[2], today.clone().add(2, 'days'))).to.equal(true); }); - it('does not call deleteModifier with `hovered-start-blocked-minimum-nights` if the hovered date is blocked', () => { + it('does not call deleteModifierFromRange with `hovered-start-blocked-minimum-nights` if the hovered date is blocked', () => { const deleteModifierFromRangeSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifierFromRange'); const getMinNightsForHoverDateStub = sinon.stub().returns(2); const wrapper = shallow( { }); }); }); + + describe('selected-start-in-hovered-span modifier', () => { + describe('start date is truthy, end date is falsey and day is after start date', () => { + it('calls deleteModifier with `selected-start-in-hovered-span` on start date', () => { + const startDate = today; + const dayAfterStartDate = startDate.clone().add(1, 'day'); + const hoverDate = today.clone().add(5, 'days'); + const deleteModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifier'); + const wrapper = shallow(( + + )); + wrapper.setState({ hoverDate }); + deleteModifierSpy.resetHistory(); + wrapper.instance().onDayMouseLeave(dayAfterStartDate); + const deleteModifierCalls = getCallsByModifier(deleteModifierSpy, 'selected-start-in-hovered-span'); + expect(deleteModifierCalls.length).to.equal(1); + expect(deleteModifierCalls[0].args[1]).to.equal(startDate); + }); + }) + }); + + describe('selected-end-in-hovered-span modifier', () => { + describe('end date is truthy, start date is falsey and day is before end date', () => { + it('calls deleteModifier with `selected-end-in-hovered-span` on end date', () => { + const endDate = today; + const dayBeforeEndDate = endDate.clone().subtract(1, 'day'); + const hoverDate = today.clone().add(5, 'days'); + const deleteModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifier'); + const wrapper = shallow(( + + )); + wrapper.setState({ hoverDate }); + deleteModifierSpy.resetHistory(); + wrapper.instance().onDayMouseLeave(dayBeforeEndDate); + const deleteModifierCalls = getCallsByModifier(deleteModifierSpy, 'selected-end-in-hovered-span'); + expect(deleteModifierCalls.length).to.equal(1); + expect(deleteModifierCalls[0].args[1]).to.equal(endDate); + }); + }) + }); + + describe('before-hovered-end modifier', () => { + describe('end date is truthy and day is end date', () => { + it('calls deleteModifierFromRange with `before-hovered-end` on span of end date to end date minus minimum nights', () => { + const endDate = today; + const hoverDate = today.clone().subtract(5, 'days'); + const deleteModifierFromRangeSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifierFromRange'); + const minimumNights = 5; + const minimumNightStartSpan = endDate.clone().subtract(minimumNights, 'days'); + const wrapper = shallow(( + + )); + deleteModifierFromRangeSpy.resetHistory(); + wrapper.setState({ hoverDate }); + wrapper.instance().onDayMouseLeave(endDate); + const deleteModifierFromRangeCalls = getCallsByModifier(deleteModifierFromRangeSpy, 'before-hovered-end'); + expect(deleteModifierFromRangeCalls.length).to.equal(1); + expect(toISODateString(deleteModifierFromRangeCalls[0].args[1])).to.equal(toISODateString(minimumNightStartSpan)); + expect(deleteModifierFromRangeCalls[0].args[2]).to.equal(endDate); + }); + }) + }); }); }); @@ -4514,6 +4835,48 @@ describe('DayPickerRangeController', () => { }); }); + describe('#beforeSelectedEnd', () => { + it('returns true if day is before end date', () => { + const endDate = today; + const dayBeforeEndDate = endDate.clone().subtract(1, 'days'); + const wrapper = shallow(); + expect(wrapper.instance().beforeSelectedEnd(dayBeforeEndDate)).to.equal(true); + }); + + it('returns false if day is after or equal to end date', () => { + const endDate = today; + const dayAfterEndDate = endDate.clone().add(1, 'days'); + const wrapper = shallow(); + expect(wrapper.instance().beforeSelectedEnd(dayAfterEndDate)).to.equal(false); + }); + }); + + describe('#isDayBeforeHoveredEndDate', () => { + it('returns false if day is after hovered end date', () => { + const endDate = today; + const dayAfterEndDate = endDate.clone().add(1, 'days'); + const wrapper = shallow(); + wrapper.setState({ hoverDate: endDate }); + expect(wrapper.instance().isDayBeforeHoveredEndDate(dayAfterEndDate)).to.equal(false); + }); + + it('returns true if day is before hovered end date', () => { + const endDate = today; + const dayBeforeEndDate = endDate.clone().subtract(1, 'days'); + const wrapper = shallow(); + wrapper.setState({ hoverDate: endDate }); + expect(wrapper.instance().isDayBeforeHoveredEndDate(dayBeforeEndDate)).to.equal(true); + }); + }); + describe('noNavButtons prop', () => { it('renders navigation button', () => { const wrapper = shallow().dive().dive(); @@ -4528,7 +4891,7 @@ describe('DayPickerRangeController', () => { describe('renderKeyboardShortcutsButton prop', () => { it('pass down custom button render function', () => { - const testRenderKeyboardShortcutsButton = () => { }; + const testRenderKeyboardShortcutsButton = () => {}; const wrapper = shallow( Date: Tue, 22 Oct 2019 11:50:03 -0400 Subject: [PATCH 528/618] Fix tests + linting --- .../DayPickerRangeController_spec.jsx | 69 +++++++++++++------ 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index 37c2ca33cc..cd6dde73b8 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -352,7 +352,7 @@ describe('DayPickerRangeController', () => { }); describe('endDate changed from one date to another', () => { - it('removes previous `selected-end-no-start-date` when no start date selected', () => { + it('removes previous `selected-end-no-selected-start` when no start date selected', () => { const minimumNights = 5; const endDate = moment().add(7, 'days'); const deleteModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifier'); @@ -1614,9 +1614,19 @@ describe('DayPickerRangeController', () => { const addModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'addModifier'); const startDate = moment().add(1, 'days'); const endDate = startDate.clone().add(1, 'days'); - const wrapper = shallow(); + const wrapper = shallow( + , + ); const newEndDate = endDate.clone().add(1, 'days'); - wrapper.instance().componentWillReceiveProps({ ...props, startDate: null, endDate: newEndDate }); + wrapper.instance().componentWillReceiveProps({ + ...props, + startDate: null, + endDate: newEndDate, + }); const noSelectedStartBeforeSelectedEndCalls = getCallsByModifier(addModifierSpy, 'no-selected-start-before-selected-end'); noSelectedStartBeforeSelectedEndCalls.forEach((eachCall) => { const day = eachCall.args[1]; @@ -1668,16 +1678,16 @@ describe('DayPickerRangeController', () => { }); }); - describe('end date has changed, and start date and previous start date are falsey', () => { + describe('end date has changed, and start date or previous start date are falsey', () => { it('calls deleteModifier with `selected-end-no-selected-start`', () => { const deleteModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifier'); - const startDate = moment(); - const wrapper = shallow(); - const newStartDate = startDate.clone().add(1, 'days'); - wrapper.instance().componentWillReceiveProps({ ...props, startDate: newStartDate }); + const endDate = moment(); + const wrapper = shallow(); + const newEndDate = endDate.clone().add(1, 'days'); + wrapper.instance().componentWillReceiveProps({ ...props, endDate: newEndDate }); const selectedStartNoSelectedEndCalls = getCallsByModifier(deleteModifierSpy, 'selected-end-no-selected-start'); expect(selectedStartNoSelectedEndCalls.length).to.equal(1); - expect(selectedStartNoSelectedEndCalls[0].args[1]).to.equal(startDate); + expect(selectedStartNoSelectedEndCalls[0].args[1]).to.equal(endDate); }); }); }); @@ -1688,19 +1698,27 @@ describe('DayPickerRangeController', () => { const minimumNights = 1; const addModifierToRangeSpy = sinon.spy(DayPickerRangeController.prototype, 'addModifierToRange'); const endDate = moment(); - const wrapper = shallow(); + const wrapper = shallow( + , + ); const newEndDate = endDate.clone().add(1, 'days'); addModifierToRangeSpy.resetHistory(); wrapper.instance().componentWillReceiveProps({ ...props, endDate: newEndDate }); const beforeHoveredEndCalls = getCallsByModifier(addModifierToRangeSpy, 'before-hovered-end'); expect(beforeHoveredEndCalls.length).to.equal(1); - expect(toISODateString(beforeHoveredEndCalls[0].args[1])).to.equal(toISODateString(newEndDate.clone().subtract(minimumNights, 'days'))); - expect(toISODateString(beforeHoveredEndCalls[0].args[2])).to.equal(toISODateString(newEndDate)); + expect(toISODateString(beforeHoveredEndCalls[0].args[1])).to.equal( + toISODateString(newEndDate.clone().subtract(minimumNights, 'days')), + ); + expect(toISODateString(beforeHoveredEndCalls[0].args[2])).to.equal( + toISODateString(newEndDate), + ); }); }); }); - - }); describe('phrases', () => { @@ -2986,9 +3004,14 @@ describe('DayPickerRangeController', () => { const minimumNightStartSpan = endDate.clone().subtract(minimumNights, 'days'); deleteModifierFromRangeSpy.resetHistory(); wrapper.instance().onDayMouseEnter(today); - const deleteModifierFromRangeCalls = getCallsByModifier(deleteModifierFromRangeSpy, 'before-hovered-end'); + const deleteModifierFromRangeCalls = getCallsByModifier( + deleteModifierFromRangeSpy, + 'before-hovered-end', + ); expect(deleteModifierFromRangeCalls.length).to.equal(1); - expect(toISODateString(deleteModifierFromRangeCalls[0].args[1])).to.equal(toISODateString(minimumNightStartSpan)); + expect(toISODateString(deleteModifierFromRangeCalls[0].args[1])).to.equal( + toISODateString(minimumNightStartSpan), + ); expect(deleteModifierFromRangeCalls[0].args[2]).to.equal(endDate); }); }); @@ -3008,7 +3031,9 @@ describe('DayPickerRangeController', () => { wrapper.instance().onDayMouseEnter(today); const addModifierFromRangeCalls = getCallsByModifier(addModifierFromRangeSpy, 'before-hovered-end'); expect(addModifierFromRangeCalls.length).to.equal(1); - expect(toISODateString(addModifierFromRangeCalls[0].args[1])).to.equal(toISODateString(minimumNightStartSpan)); + expect(toISODateString(addModifierFromRangeCalls[0].args[1])).to.equal( + toISODateString(minimumNightStartSpan), + ); expect(addModifierFromRangeCalls[0].args[2]).to.equal(endDate); }); }); @@ -3319,7 +3344,7 @@ describe('DayPickerRangeController', () => { expect(deleteModifierCalls.length).to.equal(1); expect(deleteModifierCalls[0].args[1]).to.equal(startDate); }); - }) + }); }); describe('selected-end-in-hovered-span modifier', () => { @@ -3344,7 +3369,7 @@ describe('DayPickerRangeController', () => { expect(deleteModifierCalls.length).to.equal(1); expect(deleteModifierCalls[0].args[1]).to.equal(endDate); }); - }) + }); }); describe('before-hovered-end modifier', () => { @@ -3369,10 +3394,12 @@ describe('DayPickerRangeController', () => { wrapper.instance().onDayMouseLeave(endDate); const deleteModifierFromRangeCalls = getCallsByModifier(deleteModifierFromRangeSpy, 'before-hovered-end'); expect(deleteModifierFromRangeCalls.length).to.equal(1); - expect(toISODateString(deleteModifierFromRangeCalls[0].args[1])).to.equal(toISODateString(minimumNightStartSpan)); + expect(toISODateString(deleteModifierFromRangeCalls[0].args[1])).to.equal( + toISODateString(minimumNightStartSpan), + ); expect(deleteModifierFromRangeCalls[0].args[2]).to.equal(endDate); }); - }) + }); }); }); }); From b8fab118abbe5885e5ef370d0dc81d175a7c2b17 Mon Sep 17 00:00:00 2001 From: Kris Salvador Date: Tue, 29 Oct 2019 16:47:45 -0400 Subject: [PATCH 529/618] Update logic and tests for modifiers --- src/components/DayPickerRangeController.jsx | 30 +++++--- .../DayPickerRangeController_spec.jsx | 72 +++++++++++++++++-- 2 files changed, 88 insertions(+), 14 deletions(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index b932092af2..c32a7cb48c 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -365,18 +365,18 @@ export default class DayPickerRangeController extends React.PureComponent { if (!endDate || !prevEndDate) { modifiers = this.deleteModifier(modifiers, prevStartDate, 'selected-start-no-selected-end'); } + } - if (!startDate && endDate) { - values(visibleDays).forEach((days) => { - Object.keys(days).forEach((day) => { - const momentObj = moment(day); + if (!prevStartDate && endDate && startDate) { + modifiers = this.deleteModifier(modifiers, endDate, 'selected-end-no-selected-start'); + modifiers = this.deleteModifier(modifiers, endDate, 'selected-end-in-hovered-span'); - if (isBeforeDay(momentObj, endDate)) { - modifiers = this.addModifier(modifiers, momentObj, 'no-selected-start-before-selected-end'); - } - }); + values(visibleDays).forEach((days) => { + Object.keys(days).forEach((day) => { + const momentObj = moment(day); + modifiers = this.deleteModifier(modifiers, momentObj, 'no-selected-start-before-selected-end'); }); - } + }); } } @@ -422,6 +422,18 @@ export default class DayPickerRangeController extends React.PureComponent { if (endDate && !startDate) { modifiers = this.addModifier(modifiers, endDate, 'selected-end-no-selected-start'); } + + if (!startDate && endDate) { + values(visibleDays).forEach((days) => { + Object.keys(days).forEach((day) => { + const momentObj = moment(day); + + if (isBeforeDay(momentObj, endDate)) { + modifiers = this.addModifier(modifiers, momentObj, 'no-selected-start-before-selected-end'); + } + }); + }); + } } if (!this.isTouchDevice && didStartDateChange && startDate && !endDate) { diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index cd6dde73b8..9b375dfae0 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -1609,22 +1609,20 @@ describe('DayPickerRangeController', () => { }); describe('no-selected-start-before-selected-end', () => { - describe('start date has changed, start date is falsey, and end date is truthy', () => { + describe('start or end date has changed, start date is falsey, and end date is truthy', () => { it('calls addModifier with `no-selected-start-before-selected-end` if day is before selected end date', () => { const addModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'addModifier'); - const startDate = moment().add(1, 'days'); - const endDate = startDate.clone().add(1, 'days'); + const endDate = today.clone(); const wrapper = shallow( , ); const newEndDate = endDate.clone().add(1, 'days'); wrapper.instance().componentWillReceiveProps({ ...props, - startDate: null, endDate: newEndDate, }); const noSelectedStartBeforeSelectedEndCalls = getCallsByModifier(addModifierSpy, 'no-selected-start-before-selected-end'); @@ -1635,6 +1633,29 @@ describe('DayPickerRangeController', () => { }); }); }); + + describe('start date has changed, previous start date is falsey, start and end date is truthy', () => { + it('calls deleteModifier with `no-selected-start-before-selected-end` if day is before selected end date', () => { + const deleteModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifier'); + const endDate = moment('1993-10-27'); + const wrapper = shallow( + , + ); + const newStartDate = endDate.clone().subtract(10, 'days'); + const numberVisibleDays = 91; + wrapper.instance().componentWillReceiveProps({ + ...props, + endDate, + startDate: newStartDate, + }); + const noSelectedStartBeforeSelectedEndCalls = getCallsByModifier(deleteModifierSpy, 'no-selected-start-before-selected-end'); + expect(noSelectedStartBeforeSelectedEndCalls.length).to.equal(numberVisibleDays); + }); + }); }); describe('selected-start-no-selected-end', () => { @@ -1690,6 +1711,19 @@ describe('DayPickerRangeController', () => { expect(selectedStartNoSelectedEndCalls[0].args[1]).to.equal(endDate); }); }); + + describe('start date has changed, and start date is truthy, and previous start date was falsey', () => { + it('calls deleteModifier with `selected-end-no-selected-start`', () => { + const deleteModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifier'); + const endDate = moment(); + const wrapper = shallow(); + const newStartDate = endDate.clone().subtract(1, 'days'); + wrapper.instance().componentWillReceiveProps({ ...props, startDate: newStartDate }); + const selectedStartNoSelectedEndCalls = getCallsByModifier(deleteModifierSpy, 'selected-end-no-selected-start'); + expect(selectedStartNoSelectedEndCalls.length).to.equal(1); + expect(selectedStartNoSelectedEndCalls[0].args[1]).to.equal(endDate); + }); + }); }); describe('before-hovered-end', () => { @@ -1719,6 +1753,34 @@ describe('DayPickerRangeController', () => { }); }); }); + + describe('selected-end-in-hovered-span', () => { + describe('start date has changed', () => { + describe('start and end date are truthy, and previous start date is falsey', () => { + it('calls deleteModifier with `selected-end-in-hovered-span`', () => { + const deleteModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifier'); + const endDate = today; + const startDate = endDate.clone().subtract(1, 'days'); + const wrapper = shallow( + , + ); + const newStartDate = endDate.clone().subtract(3, 'days'); + deleteModifierSpy.resetHistory(); + wrapper.instance().componentWillReceiveProps({ + ...props, + endDate, + startDate: newStartDate, + }); + const deleteModifierCalls = getCallsByModifier(deleteModifierSpy, 'selected-end-in-hovered-span'); + expect(deleteModifierCalls.length).to.equal(1); + expect(deleteModifierCalls[0].args[1]).to.equal(endDate); + }); + }); + }); + }); }); describe('phrases', () => { From 37803f4429d19fc92ab5296be5f022aae4fa3b53 Mon Sep 17 00:00:00 2001 From: Casey Klimkowsky Date: Wed, 30 Oct 2019 12:51:06 -0700 Subject: [PATCH 530/618] Revert "Call getStateForNewMonth when date/startDate/endDate is set to a date that is not visible" --- src/components/DayPickerRangeController.jsx | 20 +--- .../DayPickerSingleDateController.jsx | 19 ++-- .../DayPickerRangeController_spec.jsx | 99 +++++-------------- .../DayPickerSingleDateController_spec.jsx | 24 ----- 4 files changed, 35 insertions(+), 127 deletions(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index c665c6afbb..b96230f127 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -18,7 +18,6 @@ import isBeforeDay from '../utils/isBeforeDay'; import getVisibleDays from '../utils/getVisibleDays'; import isDayVisible from '../utils/isDayVisible'; -import getPooledMoment from '../utils/getPooledMoment'; import getSelectedDateOffset from '../utils/getSelectedDateOffset'; import toISODateString from '../utils/toISODateString'; @@ -42,6 +41,7 @@ import { } from '../constants'; import DayPicker from './DayPicker'; +import getPooledMoment from '../utils/getPooledMoment'; const propTypes = forbidExtraProps({ startDate: momentPropTypes.momentObj, @@ -284,8 +284,6 @@ export default class DayPickerRangeController extends React.PureComponent { } = this.props; const { hoverDate } = this.state; - - let { currentMonth } = this.state; let { visibleDays } = this.state; let recomputeOutsideRange = false; @@ -315,18 +313,6 @@ export default class DayPickerRangeController extends React.PureComponent { const didEndDateChange = endDate !== prevEndDate; const didFocusChange = focusedInput !== prevFocusedInput; - let isDateVisible = true; - - if (didStartDateChange || didEndDateChange) { - if (didStartDateChange) { - isDateVisible = isDayVisible(startDate, currentMonth, numberOfMonths, enableOutsideDays); - } - - if (didEndDateChange) { - isDateVisible = isDayVisible(endDate, currentMonth, numberOfMonths, enableOutsideDays); - } - } - if ( numberOfMonths !== prevNumberOfMonths || enableOutsideDays !== prevEnableOutsideDays @@ -335,10 +321,10 @@ export default class DayPickerRangeController extends React.PureComponent { && !prevFocusedInput && didFocusChange ) - || !isDateVisible ) { const newMonthState = this.getStateForNewMonth(nextProps); - ({ currentMonth, visibleDays } = newMonthState); + const { currentMonth } = newMonthState; + ({ visibleDays } = newMonthState); this.setState({ currentMonth, visibleDays, diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 4c65c66707..f9b9e1be4e 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -13,7 +13,6 @@ import isSameDay from '../utils/isSameDay'; import isAfterDay from '../utils/isAfterDay'; import getVisibleDays from '../utils/getVisibleDays'; -import isDayVisible from '../utils/isDayVisible'; import toISODateString from '../utils/toISODateString'; import { addModifier, deleteModifier } from '../utils/modifiers'; @@ -220,7 +219,6 @@ export default class DayPickerSingleDateController extends React.PureComponent { focused: prevFocused, date: prevDate, } = this.props; - let { currentMonth } = this.state; let { visibleDays } = this.state; let recomputeOutsideRange = false; @@ -246,15 +244,6 @@ export default class DayPickerSingleDateController extends React.PureComponent { recomputeOutsideRange || recomputeDayBlocked || recomputeDayHighlighted ); - const didDateChange = date !== prevDate; - const didFocusChange = focused !== prevFocused; - - let isDateVisible = true; - - if (didDateChange) { - isDateVisible = isDayVisible(date, currentMonth, numberOfMonths, enableOutsideDays); - } - if ( numberOfMonths !== prevNumberOfMonths || enableOutsideDays !== prevEnableOutsideDays @@ -262,16 +251,20 @@ export default class DayPickerSingleDateController extends React.PureComponent { initialVisibleMonth !== prevInitialVisibleMonth && !prevFocused && focused - ) || !isDateVisible + ) ) { const newMonthState = this.getStateForNewMonth(nextProps); - ({ currentMonth, visibleDays } = newMonthState); + const { currentMonth } = newMonthState; + ({ visibleDays } = newMonthState); this.setState({ currentMonth, visibleDays, }); } + const didDateChange = date !== prevDate; + const didFocusChange = focused !== prevFocused; + let modifiers = {}; if (didDateChange) { diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index 462380e9eb..ef57e6d461 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -297,80 +297,33 @@ describe('DayPickerRangeController', () => { }); expect(wrapper.instance().state.visibleDays).to.equal(visibleDays); }); - }); - - describe('startDate changed from one date to another', () => { - it('removes previous `after-hovered-start` range', () => { - const minimumNights = 5; - const startDate = moment().add(7, 'days'); - const dayAfterStartDate = startDate.clone().add(1, 'day'); - const firstAvailableDate = startDate.clone().add(minimumNights + 1, 'days'); - const deleteModifierFromRangeSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifierFromRange'); - const nextStartDate = moment().add(4, 'days'); - const wrapper = shallow(( - - )); - deleteModifierFromRangeSpy.resetHistory(); - wrapper.instance().componentWillReceiveProps({ - ...props, - startDate: nextStartDate, - }); - const afterHoverStartCalls = getCallsByModifier(deleteModifierFromRangeSpy, 'after-hovered-start'); - expect(afterHoverStartCalls.length).to.equal(1); - expect(isSameDay(afterHoverStartCalls[0].args[1], dayAfterStartDate)).to.equal(true); - expect(isSameDay(afterHoverStartCalls[0].args[2], firstAvailableDate)).to.equal(true); - }); - - it('calls getStateForNewMonth with nextProps when date is not visible', () => { - const getStateForNewMonthSpy = sinon.spy( - DayPickerRangeController.prototype, - 'getStateForNewMonth', - ); - const startDate = moment(); - const nextStartDate = startDate.clone().add(2, 'months'); - - const wrapper = shallow(( - - )); - - getStateForNewMonthSpy.resetHistory(); - - wrapper.instance().componentWillReceiveProps({ - ...props, - startDate: nextStartDate, - }); - - expect(getStateForNewMonthSpy.callCount).to.equal(1); - }); - }); - - describe('endDate changed from one date to another', () => { - it('calls getStateForNewMonth with nextProps when date is not visible', () => { - const getStateForNewMonthSpy = sinon.spy( - DayPickerRangeController.prototype, - 'getStateForNewMonth', - ); - const endDate = moment(); - const nextEndDate = endDate.clone().add(2, 'months'); - - const wrapper = shallow(( - - )); - - getStateForNewMonthSpy.resetHistory(); - - wrapper.instance().componentWillReceiveProps({ - ...props, - endDate: nextEndDate, + describe('startDate changed from one date to another', () => { + it('removes previous `after-hovered-start` range', () => { + const minimumNights = 5; + const startDate = moment().add(7, 'days'); + const dayAfterStartDate = startDate.clone().add(1, 'day'); + const firstAvailableDate = startDate.clone().add(minimumNights + 1, 'days'); + const deleteModifierFromRangeSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifierFromRange'); + const nextStartDate = moment().add(4, 'days'); + const wrapper = shallow(( + + )); + deleteModifierFromRangeSpy.resetHistory(); + wrapper.instance().componentWillReceiveProps({ + ...props, + startDate: nextStartDate, + }); + const afterHoverStartCalls = getCallsByModifier(deleteModifierFromRangeSpy, 'after-hovered-start'); + expect(afterHoverStartCalls.length).to.equal(1); + expect(isSameDay(afterHoverStartCalls[0].args[1], dayAfterStartDate)).to.equal(true); + expect(isSameDay(afterHoverStartCalls[0].args[2], firstAvailableDate)).to.equal(true); }); - - expect(getStateForNewMonthSpy.callCount).to.equal(1); }); }); }); diff --git a/test/components/DayPickerSingleDateController_spec.jsx b/test/components/DayPickerSingleDateController_spec.jsx index e7154ccef8..6cd614a91c 100644 --- a/test/components/DayPickerSingleDateController_spec.jsx +++ b/test/components/DayPickerSingleDateController_spec.jsx @@ -46,30 +46,6 @@ describe('DayPickerSingleDateController', () => { onFocusChange() {}, }; - describe('date changed from one date to another', () => { - it('calls getStateForNewMonth with nextProps when date is not visible', () => { - const getStateForNewMonthSpy = sinon.spy( - DayPickerSingleDateController.prototype, - 'getStateForNewMonth', - ); - const date = moment(); - const nextDate = date.clone().add(2, 'months'); - - const wrapper = shallow(( - - )); - - getStateForNewMonthSpy.resetHistory(); - - wrapper.instance().componentWillReceiveProps({ - ...props, - date: nextDate, - }); - - expect(getStateForNewMonthSpy.callCount).to.equal(1); - }); - }); - describe('modifiers', () => { describe('selected modifier', () => { describe('props.date did not change', () => { From 928d74c5da337d0fe74515eed4eb1f9c497dec0b Mon Sep 17 00:00:00 2001 From: Nora Tarano Date: Wed, 30 Oct 2019 14:27:35 -0700 Subject: [PATCH 531/618] Version 21.3.2 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 433a704764..e60a67f279 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## 21.3.2 + +- [fix] Revert "Call getStateForNewMonth when date/startDate/endDate is set to a date that is not visible" ([#1851](https://github.com/airbnb/react-dates/pull/1851)) + ## 21.3.1 - [fix] Update react-with-styles 4.1.0-alpha.1 -> 4.1.0 diff --git a/package.json b/package.json index 21c74736d5..65e411fe49 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "21.3.1", + "version": "21.3.2", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From e4427f38d3085f2998c17614ba511170833eeb83 Mon Sep 17 00:00:00 2001 From: Kris Salvador Date: Wed, 30 Oct 2019 17:38:58 -0400 Subject: [PATCH 532/618] Remove unused variable --- test/components/DayPickerRangeController_spec.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index 9b375dfae0..0d8bd8d1ee 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -1760,7 +1760,6 @@ describe('DayPickerRangeController', () => { it('calls deleteModifier with `selected-end-in-hovered-span`', () => { const deleteModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifier'); const endDate = today; - const startDate = endDate.clone().subtract(1, 'days'); const wrapper = shallow( Date: Sun, 3 Nov 2019 23:48:30 -0500 Subject: [PATCH 533/618] Update flakey test for no-selected-start-before-selected-end --- test/components/DayPickerRangeController_spec.jsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index 7b1b795914..402691db39 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -1613,7 +1613,7 @@ describe('DayPickerRangeController', () => { describe('start date has changed, previous start date is falsey, start and end date is truthy', () => { it('calls deleteModifier with `no-selected-start-before-selected-end` if day is before selected end date', () => { const deleteModifierSpy = sinon.spy(DayPickerRangeController.prototype, 'deleteModifier'); - const endDate = moment('1993-10-27'); + const endDate = today.clone().add(10, 'days'); const wrapper = shallow( { endDate={endDate} />, ); - const newStartDate = endDate.clone().subtract(10, 'days'); - const numberVisibleDays = 91; + const newStartDate = today; + const visibleDays = wrapper.instance().state.visibleDays; + const numberOfVisibleDays = Object.values(visibleDays).reduce((total, visibleDayArray) => { + return total + Object.keys(visibleDayArray).length; + }, 0); wrapper.instance().componentWillReceiveProps({ ...props, endDate, startDate: newStartDate, }); const noSelectedStartBeforeSelectedEndCalls = getCallsByModifier(deleteModifierSpy, 'no-selected-start-before-selected-end'); - expect(noSelectedStartBeforeSelectedEndCalls.length).to.equal(numberVisibleDays); + expect(noSelectedStartBeforeSelectedEndCalls.length).to.equal(numberOfVisibleDays); }); }); }); From 933b545c97fe6527be96e84907cfe157db0847a3 Mon Sep 17 00:00:00 2001 From: krissalvador27 Date: Mon, 4 Nov 2019 00:34:03 -0500 Subject: [PATCH 534/618] Eslint --- test/components/DayPickerRangeController_spec.jsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index 402691db39..6904f62e2b 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -1622,10 +1622,11 @@ describe('DayPickerRangeController', () => { />, ); const newStartDate = today; - const visibleDays = wrapper.instance().state.visibleDays; - const numberOfVisibleDays = Object.values(visibleDays).reduce((total, visibleDayArray) => { - return total + Object.keys(visibleDayArray).length; - }, 0); + const { visibleDays } = wrapper.instance().state; + const numberOfVisibleDays = Object.values(visibleDays).reduce( + (total, visibleDayArray) => total + Object.keys(visibleDayArray).length, + 0, + ); wrapper.instance().componentWillReceiveProps({ ...props, endDate, From 73e001a3f68acec8bfbd6fe28638cf2d3917b51c Mon Sep 17 00:00:00 2001 From: Kevin Pan Date: Tue, 5 Nov 2019 16:55:38 -0800 Subject: [PATCH 535/618] relaxing propTypes for CalendarWeek --- src/components/CalendarWeek.jsx | 8 +++----- test/components/CalendarWeek_spec.jsx | 9 +++++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/components/CalendarWeek.jsx b/src/components/CalendarWeek.jsx index ef691ac45f..037098a3fa 100644 --- a/src/components/CalendarWeek.jsx +++ b/src/components/CalendarWeek.jsx @@ -1,11 +1,9 @@ import React from 'react'; -import { forbidExtraProps, or, childrenOfType } from 'airbnb-prop-types'; - -import CalendarDay from './CalendarDay'; -import CustomizableCalendarDay from './CustomizableCalendarDay'; +import PropTypes from 'prop-types'; +import { forbidExtraProps } from 'airbnb-prop-types'; const propTypes = forbidExtraProps({ - children: or([childrenOfType(CalendarDay), childrenOfType(CustomizableCalendarDay)]).isRequired, + children: PropTypes.node.isRequired, }); export default function CalendarWeek({ children }) { diff --git a/test/components/CalendarWeek_spec.jsx b/test/components/CalendarWeek_spec.jsx index 91117cbf6d..c78b1a7b76 100644 --- a/test/components/CalendarWeek_spec.jsx +++ b/test/components/CalendarWeek_spec.jsx @@ -15,4 +15,13 @@ describe('CalendarWeek', () => { )); expect(wrapper.is('tr')).to.equal(true); }); + it('accepts a React node', () => { + const wrapper = shallow(( + + <> + + )); + expect(wrapper.is('tr')).to.equal(true); + expect(wrapper).to.not.throw(); + }); }); From 6c9dcca370a6bb200e0037eb3f95252ef69dcf08 Mon Sep 17 00:00:00 2001 From: Kevin Pan Date: Tue, 5 Nov 2019 17:13:25 -0800 Subject: [PATCH 536/618] test fix --- test/components/CalendarWeek_spec.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/components/CalendarWeek_spec.jsx b/test/components/CalendarWeek_spec.jsx index c78b1a7b76..11a2422fe0 100644 --- a/test/components/CalendarWeek_spec.jsx +++ b/test/components/CalendarWeek_spec.jsx @@ -15,10 +15,10 @@ describe('CalendarWeek', () => { )); expect(wrapper.is('tr')).to.equal(true); }); - it('accepts a React node', () => { + it('accepts a node', () => { const wrapper = shallow(( - <> +
    )); expect(wrapper.is('tr')).to.equal(true); From cb3edf170ee29da4c7ceaca16f1c38fc3f3fed4f Mon Sep 17 00:00:00 2001 From: Kevin Pan Date: Tue, 5 Nov 2019 17:34:39 -0800 Subject: [PATCH 537/618] removing test --- test/components/CalendarWeek_spec.jsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/components/CalendarWeek_spec.jsx b/test/components/CalendarWeek_spec.jsx index 11a2422fe0..91117cbf6d 100644 --- a/test/components/CalendarWeek_spec.jsx +++ b/test/components/CalendarWeek_spec.jsx @@ -15,13 +15,4 @@ describe('CalendarWeek', () => { )); expect(wrapper.is('tr')).to.equal(true); }); - it('accepts a node', () => { - const wrapper = shallow(( - -
    - - )); - expect(wrapper.is('tr')).to.equal(true); - expect(wrapper).to.not.throw(); - }); }); From d349515c2f290fc52d0812106dd3bcf9281fb296 Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Tue, 12 Nov 2019 15:58:36 -0800 Subject: [PATCH 538/618] Version 21.4.0 --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e60a67f279..7a25502c3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## 21.4.0 + +- [new] Expose 'after-hovered-start' modifier and add 'before-hovered-end', 'no-selected-start-before-selected-end', 'selected-start-in-hovered-span', 'selected-end-in-hovered-span', 'selected-start-no-selected-end', and 'selected-end-no-selected-start' modifiers [#1608](https://github.com/airbnb/react-dates/pull/1608) +- [fix] Loosen up CustomizeableCalendarDay restriction on children ([#1857](https://github.com/airbnb/react-dates/pull/1857)) + ## 21.3.2 - [fix] Revert "Call getStateForNewMonth when date/startDate/endDate is set to a date that is not visible" ([#1851](https://github.com/airbnb/react-dates/pull/1851)) diff --git a/package.json b/package.json index 65e411fe49..8f191c41d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "21.3.2", + "version": "21.4.0", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From 35effaac4f1f9d8c59c5614a196320198226686a Mon Sep 17 00:00:00 2001 From: casey_klimkowsky Date: Tue, 12 Nov 2019 17:12:54 -0800 Subject: [PATCH 539/618] Allow for custom month navigation buttons --- examples/DayPickerRangeControllerWrapper.jsx | 4 + .../DayPickerSingleDateControllerWrapper.jsx | 8 +- src/components/DateRangePicker.jsx | 12 +- src/components/DayPicker.jsx | 8 + src/components/DayPickerNavigation.jsx | 154 +++++++++++------- src/components/DayPickerRangeController.jsx | 8 + .../DayPickerSingleDateController.jsx | 8 + src/components/SingleDatePicker.jsx | 12 +- src/shapes/DateRangePickerShape.js | 2 + src/shapes/SingleDatePickerShape.js | 2 + stories/DayPicker.js | 62 ++++++- stories/DayPickerRangeController.js | 88 ++++++++-- stories/DayPickerSingleDateController.js | 79 +++++++-- test/components/DayPickerNavigation_spec.jsx | 90 ++++++++++ 14 files changed, 440 insertions(+), 97 deletions(-) diff --git a/examples/DayPickerRangeControllerWrapper.jsx b/examples/DayPickerRangeControllerWrapper.jsx index 08c5547f03..1975c098e8 100644 --- a/examples/DayPickerRangeControllerWrapper.jsx +++ b/examples/DayPickerRangeControllerWrapper.jsx @@ -43,6 +43,8 @@ const propTypes = forbidExtraProps({ navPrev: PropTypes.node, navNext: PropTypes.node, + renderNavPrevButton: PropTypes.func, + renderNavNextButton: PropTypes.func, onPrevMonthClick: PropTypes.func, onNextMonthClick: PropTypes.func, @@ -96,6 +98,8 @@ const defaultProps = { // navigation related props navPrev: null, navNext: null, + renderNavPrevButton: null, + renderNavNextButton: null, onPrevMonthClick() {}, onNextMonthClick() {}, diff --git a/examples/DayPickerSingleDateControllerWrapper.jsx b/examples/DayPickerSingleDateControllerWrapper.jsx index d9d703c19f..02d94e931b 100644 --- a/examples/DayPickerSingleDateControllerWrapper.jsx +++ b/examples/DayPickerSingleDateControllerWrapper.jsx @@ -34,6 +34,8 @@ const propTypes = forbidExtraProps({ navPrev: PropTypes.node, navNext: PropTypes.node, + renderNavPrevButton: PropTypes.func, + renderNavNextButton: PropTypes.func, onPrevMonthClick: PropTypes.func, onNextMonthClick: PropTypes.func, @@ -74,6 +76,8 @@ const defaultProps = { // navigation related props navPrev: null, navNext: null, + renderNavPrevButton: null, + renderNavNextButton: null, onPrevMonthClick() {}, onNextMonthClick() {}, @@ -117,11 +121,11 @@ class DayPickerSingleDateControllerWrapper extends React.Component { return (
    - {showInput && + {showInput && (
    - } + )} ); + /* eslint-enable jsx-a11y/no-static-element-interactions */ + /* eslint-enable jsx-a11y/click-events-have-key-events */ } render() { diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index fc1cc7a726..0f5b2f7162 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -84,6 +84,8 @@ const propTypes = forbidExtraProps({ navPosition: NavPositionShape, navPrev: PropTypes.node, navNext: PropTypes.node, + renderNavPrevButton: PropTypes.func, + renderNavNextButton: PropTypes.func, noNavButtons: PropTypes.bool, onPrevMonthClick: PropTypes.func, onNextMonthClick: PropTypes.func, @@ -149,6 +151,8 @@ export const defaultProps = { navPosition: NAV_POSITION_TOP, navPrev: null, navNext: null, + renderNavPrevButton: null, + renderNavNextButton: null, noNavButtons: false, onPrevMonthClick() {}, onNextMonthClick() {}, @@ -849,6 +853,8 @@ class DayPicker extends React.PureComponent { noNavButtons, orientation, phrases, + renderNavPrevButton, + renderNavNextButton, isRTL, } = this.props; @@ -870,6 +876,8 @@ class DayPicker extends React.PureComponent { navPosition={navPosition} navPrev={navPrev} navNext={navNext} + renderNavPrevButton={renderNavPrevButton} + renderNavNextButton={renderNavNextButton} orientation={orientation} phrases={phrases} isRTL={isRTL} diff --git a/src/components/DayPickerNavigation.jsx b/src/components/DayPickerNavigation.jsx index ee4f367503..a0103173ca 100644 --- a/src/components/DayPickerNavigation.jsx +++ b/src/components/DayPickerNavigation.jsx @@ -25,6 +25,8 @@ const propTypes = forbidExtraProps({ ...withStylesPropTypes, disablePrev: PropTypes.bool, disableNext: PropTypes.bool, + inlineStyles: PropTypes.object, + isRTL: PropTypes.bool, navPosition: NavPositionShape, navPrev: PropTypes.node, navNext: PropTypes.node, @@ -36,13 +38,15 @@ const propTypes = forbidExtraProps({ // internationalization phrases: PropTypes.shape(getPhrasePropTypes(DayPickerNavigationPhrases)), - inlineStyles: PropTypes.object, - isRTL: PropTypes.bool, + renderNavPrevButton: PropTypes.func, + renderNavNextButton: PropTypes.func, }); const defaultProps = { disablePrev: false, disableNext: false, + inlineStyles: null, + isRTL: false, navPosition: NAV_POSITION_TOP, navPrev: null, navNext: null, @@ -54,12 +58,13 @@ const defaultProps = { // internationalization phrases: DayPickerNavigationPhrases, - inlineStyles: null, - isRTL: false, + renderNavPrevButton: null, + renderNavNextButton: null, }; function DayPickerNavigation({ inlineStyles, + isRTL, disablePrev, disableNext, navPosition, @@ -69,7 +74,8 @@ function DayPickerNavigation({ onNextMonthClick, orientation, phrases, - isRTL, + renderNavPrevButton, + renderNavNextButton, styles, }) { const isHorizontal = orientation === HORIZONTAL_ORIENTATION; @@ -146,85 +152,111 @@ function DayPickerNavigation({ )} > {!isVerticalScrollable && ( + renderNavPrevButton ? renderNavPrevButton({ + ariaLabel: phrases.jumpToPrevMonth, + disabled: disablePrev, + onClick: disablePrev ? undefined : onPrevMonthClick, + onKeyUp: disablePrev ? undefined : (e) => { + const { key } = e; + if (key === 'Enter' || key === ' ') onPrevMonthClick(e); + }, + onMouseUp: disablePrev ? undefined : (e) => { + e.currentTarget.blur(); + }, + }) : ( +
    { + const { key } = e; + if (key === 'Enter' || key === ' ') onPrevMonthClick(e); + }} + onMouseUp={disablePrev ? undefined : (e) => { + e.currentTarget.blur(); + }} + > + {navPrevIcon} +
    + ) + )} + + {renderNavNextButton ? renderNavNextButton({ + ariaLabel: phrases.jumpToNextMonth, + disabled: disableNext, + onClick: disableNext ? undefined : onNextMonthClick, + onKeyUp: disableNext ? undefined : (e) => { + const { key } = e; + if (key === 'Enter' || key === ' ') onNextMonthClick(e); + }, + onMouseUp: disableNext ? undefined : (e) => { + e.currentTarget.blur(); + }, + }) : (
    { + aria-disabled={disableNext ? true : undefined} + aria-label={phrases.jumpToNextMonth} + onClick={disableNext ? undefined : onNextMonthClick} + onKeyUp={disableNext ? undefined : (e) => { const { key } = e; - if (key === 'Enter' || key === ' ') onPrevMonthClick(e); + if (key === 'Enter' || key === ' ') onNextMonthClick(e); }} - onMouseUp={disablePrev ? undefined : (e) => { + onMouseUp={disableNext ? undefined : (e) => { e.currentTarget.blur(); }} > - {navPrevIcon} + {navNextIcon}
    )} - -
    { - const { key } = e; - if (key === 'Enter' || key === ' ') onNextMonthClick(e); - }} - onMouseUp={disableNext ? undefined : (e) => { - e.currentTarget.blur(); - }} - > - {navNextIcon} -
    ); } diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 690e903aea..37a8dc250d 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -84,6 +84,8 @@ const propTypes = forbidExtraProps({ navPosition: NavPositionShape, navPrev: PropTypes.node, navNext: PropTypes.node, + renderNavPrevButton: PropTypes.func, + renderNavNextButton: PropTypes.func, noNavButtons: PropTypes.bool, onPrevMonthClick: PropTypes.func, @@ -151,6 +153,8 @@ const defaultProps = { navPosition: NAV_POSITION_TOP, navPrev: null, navNext: null, + renderNavPrevButton: null, + renderNavNextButton: null, noNavButtons: false, onPrevMonthClick() {}, @@ -1258,6 +1262,8 @@ export default class DayPickerRangeController extends React.PureComponent { navPosition, navPrev, navNext, + renderNavPrevButton, + renderNavNextButton, noNavButtons, onOutsideClick, withPortal, @@ -1326,6 +1332,8 @@ export default class DayPickerRangeController extends React.PureComponent { navPosition={navPosition} navPrev={navPrev} navNext={navNext} + renderNavPrevButton={renderNavPrevButton} + renderNavNextButton={renderNavNextButton} noNavButtons={noNavButtons} renderCalendarDay={renderCalendarDay} renderDayContents={renderDayContents} diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index f9b9e1be4e..2ff010b60e 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -68,6 +68,8 @@ const propTypes = forbidExtraProps({ navPosition: NavPositionShape, navPrev: PropTypes.node, navNext: PropTypes.node, + renderNavPrevButton: PropTypes.func, + renderNavNextButton: PropTypes.func, onPrevMonthClick: PropTypes.func, onNextMonthClick: PropTypes.func, @@ -127,6 +129,8 @@ const defaultProps = { navPosition: NAV_POSITION_TOP, navPrev: null, navNext: null, + renderNavPrevButton: null, + renderNavNextButton: null, onPrevMonthClick() {}, onNextMonthClick() {}, @@ -583,6 +587,8 @@ export default class DayPickerSingleDateController extends React.PureComponent { navPosition, navPrev, navNext, + renderNavPrevButton, + renderNavNextButton, onOutsideClick, onShiftTab, onTab, @@ -638,6 +644,8 @@ export default class DayPickerSingleDateController extends React.PureComponent { navPosition={navPosition} navPrev={navPrev} navNext={navNext} + renderNavPrevButton={renderNavPrevButton} + renderNavNextButton={renderNavNextButton} renderMonthText={renderMonthText} renderWeekHeaderElement={renderWeekHeaderElement} renderCalendarDay={renderCalendarDay} diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index 7fb43c0dbc..275736d1c9 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -94,6 +94,8 @@ const defaultProps = { navPosition: NAV_POSITION_TOP, navPrev: null, navNext: null, + renderNavPrevButton: null, + renderNavNextButton: null, onPrevMonthClick() {}, onNextMonthClick() {}, @@ -401,6 +403,8 @@ class SingleDatePicker extends React.PureComponent { navPosition, navPrev, navNext, + renderNavPrevButton, + renderNavNextButton, onPrevMonthClick, onNextMonthClick, onClose, @@ -443,8 +447,10 @@ class SingleDatePicker extends React.PureComponent { const withAnyPortal = withPortal || withFullScreenPortal; + /* eslint-disable jsx-a11y/no-static-element-interactions */ + /* eslint-disable jsx-a11y/click-events-have-key-events */ return ( -
    ); + /* eslint-enable jsx-a11y/no-static-element-interactions */ + /* eslint-enable jsx-a11y/click-events-have-key-events */ } render() { diff --git a/src/shapes/DateRangePickerShape.js b/src/shapes/DateRangePickerShape.js index 24efb386b3..99aca7ca06 100644 --- a/src/shapes/DateRangePickerShape.js +++ b/src/shapes/DateRangePickerShape.js @@ -83,6 +83,8 @@ export default { navPosition: NavPositionShape, navPrev: PropTypes.node, navNext: PropTypes.node, + renderNavPrevButton: PropTypes.func, + renderNavNextButton: PropTypes.func, onPrevMonthClick: PropTypes.func, onNextMonthClick: PropTypes.func, diff --git a/src/shapes/SingleDatePickerShape.js b/src/shapes/SingleDatePickerShape.js index b477375ccb..70a13f6e97 100644 --- a/src/shapes/SingleDatePickerShape.js +++ b/src/shapes/SingleDatePickerShape.js @@ -72,6 +72,8 @@ export default { navPosition: NavPositionShape, navPrev: PropTypes.node, navNext: PropTypes.node, + renderNavPrevButton: PropTypes.func, + renderNavNextButton: PropTypes.func, onPrevMonthClick: PropTypes.func, onNextMonthClick: PropTypes.func, diff --git a/stories/DayPicker.js b/stories/DayPicker.js index 4cf84eaa26..fe564feb03 100644 --- a/stories/DayPicker.js +++ b/stories/DayPicker.js @@ -21,7 +21,7 @@ const TestPrevIcon = () => ( top: '20px', width: '40px', }} - tabindex="0" + tabIndex="0" > Prev
    @@ -39,7 +39,7 @@ const TestNextIcon = () => ( top: '20px', width: '40px', }} - tabindex="0" + tabIndex="0" > Next
    @@ -57,6 +57,54 @@ const TestCustomInfoPanel = () => (
    ); +function renderNavPrevButton(buttonProps) { + const { + ariaLabel, + disabled, + onClick, + onKeyUp, + onMouseUp, + } = buttonProps; + + return ( + + ); +} + +function renderNavNextButton(buttonProps) { + const { + ariaLabel, + disabled, + onClick, + onKeyUp, + onMouseUp, + } = buttonProps; + + return ( + + ); +} + storiesOf('DayPicker', module) .add('default', withInfo()(() => ( @@ -133,9 +181,15 @@ storiesOf('DayPicker', module) navNext={} /> ))) + .add('with custom navigation buttons', withInfo()(() => ( + + ))) .add('with custom details', withInfo()(() => ( (day.day() % 6 === 5 ? '😻' : day.format('D'))} + renderDayContents={(day) => (day.day() % 6 === 5 ? '😻' : day.format('D'))} /> ))) .add('vertical with fixed-width container', withInfo()(() => ( @@ -155,7 +209,7 @@ storiesOf('DayPicker', module) ))) .add('with custom week header text', withInfo()(() => ( ( + renderWeekHeaderElement={(day) => ( {day.toUpperCase()} )} /> diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index cc2de68c20..62c3567940 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -109,6 +109,54 @@ const TestCustomInfoPanel = () => ( ); +function renderNavPrevButton(buttonProps) { + const { + ariaLabel, + disabled, + onClick, + onKeyUp, + onMouseUp, + } = buttonProps; + + return ( + + ); +} + +function renderNavNextButton(buttonProps) { + const { + ariaLabel, + disabled, + onClick, + onKeyUp, + onMouseUp, + } = buttonProps; + + return ( + + ); +} + function renderKeyboardShortcutsButton(buttonProps) { const { ref, onClick, ariaLabel } = buttonProps; @@ -251,8 +299,8 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - startDateOffset={day => day.subtract(3, 'days')} - endDateOffset={day => day.add(3, 'days')} + startDateOffset={(day) => day.subtract(3, 'days')} + endDateOffset={(day) => day.add(3, 'days')} /> ))) .add('with 45 days range selection', withInfo()(() => ( @@ -260,8 +308,8 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - startDateOffset={day => day.subtract(22, 'days')} - endDateOffset={day => day.add(22, 'days')} + startDateOffset={(day) => day.subtract(22, 'days')} + endDateOffset={(day) => day.add(22, 'days')} /> ))) .add('with 4 days after today range selection', withInfo()(() => ( @@ -269,7 +317,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - endDateOffset={day => day.add(4, 'days')} + endDateOffset={(day) => day.add(4, 'days')} /> ))) .add('with current week range selection', withInfo()(() => ( @@ -277,8 +325,8 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - startDateOffset={day => day.startOf('week')} - endDateOffset={day => day.endOf('week')} + startDateOffset={(day) => day.startOf('week')} + endDateOffset={(day) => day.endOf('week')} /> ))) .add('with custom inputs', withInfo()(() => ( @@ -397,7 +445,7 @@ storiesOf('DayPickerRangeController', module) /> ))) - .add('with custom month navigation', withInfo()(() => ( + .add('with custom month navigation icons', withInfo()(() => ( } /> ))) + .add('with custom month navigation buttons', withInfo()(() => ( + + ))) .add('with custom month navigation and blocked navigation (minDate and maxDate)', withInfo()(() => ( !isInclusivelyAfterDay(day, moment()) - || isInclusivelyAfterDay(day, moment().add(2, 'weeks')) - } + isOutsideRange={(day) => !isInclusivelyAfterDay(day, moment()) + || isInclusivelyAfterDay(day, moment().add(2, 'weeks'))} /> ))) .add('with some blocked dates', withInfo()(() => ( @@ -499,7 +555,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - isDayBlocked={day1 => datesList.some(day2 => isSameDay(day1, day2))} + isDayBlocked={(day1) => datesList.some((day2) => isSameDay(day1, day2))} /> ))) .add('with some highlighted dates', withInfo()(() => ( @@ -507,7 +563,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - isDayHighlighted={day1 => datesList.some(day2 => isSameDay(day1, day2))} + isDayHighlighted={(day1) => datesList.some((day2) => isSameDay(day1, day2))} /> ))) .add('blocks fridays', withInfo()(() => ( @@ -515,7 +571,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - isDayBlocked={day => moment.weekdays(day.weekday()) === 'Friday'} + isDayBlocked={(day) => moment.weekdays(day.weekday()) === 'Friday'} /> ))) .add('with navigation blocked (minDate and maxDate)', withInfo()(() => ( @@ -532,7 +588,7 @@ storiesOf('DayPickerRangeController', module) onOutsideClick={action('DayPickerRangeController::onOutsideClick')} onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} - renderDayContents={day => day.format('ddd')} + renderDayContents={(day) => day.format('ddd')} /> ))) .add('with info panel', withInfo()(() => ( @@ -594,7 +650,7 @@ storiesOf('DayPickerRangeController', module) onPrevMonthClick={action('DayPickerRangeController::onPrevMonthClick')} onNextMonthClick={action('DayPickerRangeController::onNextMonthClick')} getMinNightsForHoverDate={() => 2} - isDayBlocked={day1 => datesList.some(day2 => isSameDay(day1, day2))} + isDayBlocked={(day1) => datesList.some((day2) => isSameDay(day1, day2))} /> ))) .add('with custom keyboard shortcuts button', withInfo()(() => ( diff --git a/stories/DayPickerSingleDateController.js b/stories/DayPickerSingleDateController.js index ab141e105f..5752fef190 100644 --- a/stories/DayPickerSingleDateController.js +++ b/stories/DayPickerSingleDateController.js @@ -42,7 +42,7 @@ const TestPrevIcon = () => ( position: 'absolute', top: '20px', }} - tabindex="0" + tabIndex="0" > Prev @@ -59,7 +59,7 @@ const TestNextIcon = () => ( right: '22px', top: '20px', }} - tabindex="0" + tabIndex="0" > Next @@ -77,6 +77,54 @@ const TestCustomInfoPanel = () => ( ); +function renderNavPrevButton(buttonProps) { + const { + ariaLabel, + disabled, + onClick, + onKeyUp, + onMouseUp, + } = buttonProps; + + return ( + + ); +} + +function renderNavNextButton(buttonProps) { + const { + ariaLabel, + disabled, + onClick, + onKeyUp, + onMouseUp, + } = buttonProps; + + return ( + + ); +} + const datesList = [ moment(), moment().add(1, 'days'), @@ -172,7 +220,7 @@ storiesOf('DayPickerSingleDateController', module) orientation={VERTICAL_ORIENTATION} /> ))) - .add('with custom month navigation', withInfo()(() => ( + .add('with custom month navigation icons', withInfo()(() => ( } /> ))) + .add('with custom month navigation buttons', withInfo()(() => ( + + ))) .add('with month navigation positioned at the bottom', withInfo()(() => ( - !isInclusivelyAfterDay(day, moment()) || - isInclusivelyAfterDay(day, moment().add(2, 'weeks')) - } + isOutsideRange={(day) => !isInclusivelyAfterDay(day, moment()) + || isInclusivelyAfterDay(day, moment().add(2, 'weeks'))} /> ))) .add('with some blocked dates', withInfo()(() => ( @@ -230,7 +285,7 @@ storiesOf('DayPickerSingleDateController', module) onOutsideClick={action('DayPickerSingleDateController::onOutsideClick')} onPrevMonthClick={action('DayPickerSingleDateController::onPrevMonthClick')} onNextMonthClick={action('DayPickerSingleDateController::onNextMonthClick')} - isDayBlocked={day1 => datesList.some(day2 => isSameDay(day1, day2))} + isDayBlocked={(day1) => datesList.some((day2) => isSameDay(day1, day2))} /> ))) .add('with some highlighted dates', withInfo()(() => ( @@ -238,7 +293,7 @@ storiesOf('DayPickerSingleDateController', module) onOutsideClick={action('DayPickerSingleDateController::onOutsideClick')} onPrevMonthClick={action('DayPickerSingleDateController::onPrevMonthClick')} onNextMonthClick={action('DayPickerSingleDateController::onNextMonthClick')} - isDayHighlighted={day1 => datesList.some(day2 => isSameDay(day1, day2))} + isDayHighlighted={(day1) => datesList.some((day2) => isSameDay(day1, day2))} /> ))) .add('blocks fridays', withInfo()(() => ( @@ -246,7 +301,7 @@ storiesOf('DayPickerSingleDateController', module) onOutsideClick={action('DayPickerSingleDateController::onOutsideClick')} onPrevMonthClick={action('DayPickerSingleDateController::onPrevMonthClick')} onNextMonthClick={action('DayPickerSingleDateController::onNextMonthClick')} - isDayBlocked={day => moment.weekdays(day.weekday()) === 'Friday'} + isDayBlocked={(day) => moment.weekdays(day.weekday()) === 'Friday'} /> ))) .add('with custom daily details', withInfo()(() => ( @@ -254,7 +309,7 @@ storiesOf('DayPickerSingleDateController', module) onOutsideClick={action('DayPickerSingleDateController::onOutsideClick')} onPrevMonthClick={action('DayPickerSingleDateController::onPrevMonthClick')} onNextMonthClick={action('DayPickerSingleDateController::onNextMonthClick')} - renderDayContents={day => day.format('ddd')} + renderDayContents={(day) => day.format('ddd')} /> ))) .add('with custom day styles', withInfo()(() => { @@ -274,7 +329,7 @@ storiesOf('DayPickerSingleDateController', module) onOutsideClick={action('DayPickerSingleDateController::onOutsideClick')} onPrevMonthClick={action('DayPickerSingleDateController::onPrevMonthClick')} onNextMonthClick={action('DayPickerSingleDateController::onNextMonthClick')} - renderCalendarDay={props => ( + renderCalendarDay={(props) => ( { const wrapper = shallow().dive(); expect(wrapper.childAt(1).find(LeftArrow)).to.have.lengthOf(1); }); + + it('calls renderNavPrevButton when custom prev button is used', () => { + const renderNavPrevButtonStub = sinon.stub().returns(); + const wrapper = shallow( + , + ).dive(); + expect(wrapper.childAt(0).find('div[role="button"]')).to.have.lengthOf(0); + expect(renderNavPrevButtonStub).to.have.property('callCount', 1); + }); + + it('calls renderNavNextButton when custom next button is used', () => { + const renderNavNextButtonStub = sinon.stub().returns(); + const wrapper = shallow( + , + ).dive(); + expect(wrapper.childAt(1).find('div[role="button"]')).to.have.lengthOf(0); + expect(renderNavNextButtonStub).to.have.property('callCount', 1); + }); }); describe('interactions', () => { @@ -98,5 +120,73 @@ describe('DayPickerNavigation', () => { nextMonthButton.simulate('click'); expect(onNextMonthStub).to.have.property('callCount', 0); }); + + it('props.onPrevMonthClick is triggered by prev month button key up', () => { + const onPrevMonthStub = sinon.stub(); + const prevMonthButton = shallow().dive().find('[role="button"]').at(0); + prevMonthButton.simulate('keyup', { key: 'Enter' }); + expect(onPrevMonthStub).to.have.property('callCount', 1); + prevMonthButton.simulate('keyup', { key: ' ' }); + expect(onPrevMonthStub).to.have.property('callCount', 2); + }); + + it('props.onNextMonthClick is triggered by next month button key up', () => { + const onNextMonthStub = sinon.stub(); + const nextMonthButton = shallow().dive().find('[role="button"]').at(1); + nextMonthButton.simulate('keyup', { key: 'Enter' }); + expect(onNextMonthStub).to.have.property('callCount', 1); + nextMonthButton.simulate('keyup', { key: ' ' }); + expect(onNextMonthStub).to.have.property('callCount', 2); + }); + + it('props.onPrevMonthClick is triggered by custom prev month button click', () => { + const onPrevMonthStub = sinon.stub(); + const renderNavPrevButtonStub = sinon.stub().onCall(0).callsFake(({ onClick }) => ); + const prevMonthButton = shallow().dive().find('button').at(0); + prevMonthButton.simulate('click'); + expect(onPrevMonthStub).to.have.property('callCount', 1); + }); + + it('props.onNextMonthClick is triggered by custom next month button click', () => { + const onNextMonthStub = sinon.stub(); + const renderNavNextButtonStub = sinon.stub().onCall(0).callsFake(({ onClick }) => ); + const nextMonthButton = shallow().dive().find('button').at(0); + nextMonthButton.simulate('click'); + expect(onNextMonthStub).to.have.property('callCount', 1); + }); + + it('props.onPrevMonthClick is not triggered by custom prev month disabled click', () => { + const onPrevMonthStub = sinon.stub(); + const renderNavPrevButtonStub = sinon.stub().onCall(0).callsFake(({ disabled, onClick }) => ); + const prevMonthButton = shallow().dive().find('button').at(0); + prevMonthButton.simulate('click'); + expect(onPrevMonthStub).to.have.property('callCount', 0); + }); + + it('props.onNextMonthClick is not triggered by custom next month disabled click', () => { + const onNextMonthStub = sinon.stub(); + const renderNavNextButtonStub = sinon.stub().onCall(0).callsFake(({ disabled, onClick }) => ); + const nextMonthButton = shallow().dive().find('button').at(0); + nextMonthButton.simulate('click'); + expect(onNextMonthStub).to.have.property('callCount', 0); + }); }); }); From 7c58d6d6aadf0061980f1adeb52465c2606d58ac Mon Sep 17 00:00:00 2001 From: Kevin Pan Date: Wed, 13 Nov 2019 16:22:17 -0800 Subject: [PATCH 540/618] Version 21.5.0 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a25502c3f..2d7df98a6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - [] ([#](https://github.com/airbnb/react-dates/pull/)) --> +## 21.5.0 + +- [new] Add support for custom month navigation buttons ([#1859](https://github.com/airbnb/react-dates/pull/1859)) + ## 21.4.0 - [new] Expose 'after-hovered-start' modifier and add 'before-hovered-end', 'no-selected-start-before-selected-end', 'selected-start-in-hovered-span', 'selected-end-in-hovered-span', 'selected-start-no-selected-end', and 'selected-end-no-selected-start' modifiers [#1608](https://github.com/airbnb/react-dates/pull/1608) diff --git a/package.json b/package.json index 8f191c41d6..974e1a27ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "21.4.0", + "version": "21.5.0", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From 6363481cd38eb20317a2fa28c7f80fb221fb336e Mon Sep 17 00:00:00 2001 From: Kaeson Ho Date: Tue, 10 Dec 2019 12:46:24 -0800 Subject: [PATCH 541/618] Remove overflowY:scroll on CalendarMonthGrid --- src/components/CalendarMonthGrid.jsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/components/CalendarMonthGrid.jsx b/src/components/CalendarMonthGrid.jsx index bb43f01309..a86078c994 100644 --- a/src/components/CalendarMonthGrid.jsx +++ b/src/components/CalendarMonthGrid.jsx @@ -396,14 +396,6 @@ export default withStyles(({ CalendarMonthGrid__vertical_scrollable: { margin: '0 auto', - overflowY: 'scroll', - ...(noScrollBarOnVerticalScrollable && { - '-webkitOverflowScrolling': 'touch', - '::-webkit-scrollbar': { - '-webkit-appearance': 'none', - display: 'none', - }, - }), }, CalendarMonthGrid_month__horizontal: { From 1a4e252d2fac95832489b597ac450f6f4c69f6d9 Mon Sep 17 00:00:00 2001 From: Kaeson Ho Date: Tue, 10 Dec 2019 12:57:37 -0800 Subject: [PATCH 542/618] remove unused variable --- src/components/CalendarMonthGrid.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/CalendarMonthGrid.jsx b/src/components/CalendarMonthGrid.jsx index a86078c994..7ade948e44 100644 --- a/src/components/CalendarMonthGrid.jsx +++ b/src/components/CalendarMonthGrid.jsx @@ -370,7 +370,6 @@ CalendarMonthGrid.defaultProps = defaultProps; export default withStyles(({ reactDates: { color, - noScrollBarOnVerticalScrollable, spacing, zIndex, }, From 1fa929be7541bc13f549140b24b2604361ad9075 Mon Sep 17 00:00:00 2001 From: Kevin Pan Date: Tue, 10 Dec 2019 13:33:04 -0800 Subject: [PATCH 543/618] Version 21.5.1 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d7df98a6a..b4d3b8fee9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ +## 21.5.1 + +- [fix] Remove redundant overflowY:scroll on CalendarMonthGrid ([#1881](https://github.com/airbnb/react-dates/pull/1881)) ## 21.5.0 diff --git a/package.json b/package.json index 974e1a27ad..7cefa80754 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "21.5.0", + "version": "21.5.1", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From 8e8963cd05cd1d72faa00463f631158c773fee8e Mon Sep 17 00:00:00 2001 From: Nicole Kinser Date: Thu, 9 Jan 2020 16:31:22 -0800 Subject: [PATCH 544/618] Add functionality to see previous months for vertical scrollable calendar --- src/components/DayPicker.jsx | 54 ++++-- src/components/DayPickerNavigation.jsx | 157 ++++++++++-------- src/components/DayPickerRangeController.jsx | 24 ++- test/components/DayPickerNavigation_spec.jsx | 10 +- .../DayPickerRangeController_spec.jsx | 27 ++- test/components/DayPicker_spec.jsx | 44 ++++- 6 files changed, 217 insertions(+), 99 deletions(-) diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index 0f5b2f7162..34e91299e6 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -51,6 +51,8 @@ const PREV_TRANSITION = 'prev'; const NEXT_TRANSITION = 'next'; const MONTH_SELECTION_TRANSITION = 'month_selection'; const YEAR_SELECTION_TRANSITION = 'year_selection'; +const PREV_NAV = 'prev_nav'; +const NEXT_NAV = 'next_nav'; const propTypes = forbidExtraProps({ ...withStylesPropTypes, @@ -91,7 +93,8 @@ const propTypes = forbidExtraProps({ onNextMonthClick: PropTypes.func, onMonthChange: PropTypes.func, onYearChange: PropTypes.func, - onMultiplyScrollableMonths: PropTypes.func, // VERTICAL_SCROLLABLE daypickers only + onGetNextScrollableMonths: PropTypes.func, // VERTICAL_SCROLLABLE daypickers only + onGetPrevScrollableMonths: PropTypes.func, // VERTICAL_SCROLLABLE daypickers only // month props renderMonthText: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), @@ -158,7 +161,8 @@ export const defaultProps = { onNextMonthClick() {}, onMonthChange() {}, onYearChange() {}, - onMultiplyScrollableMonths() {}, + onGetNextScrollableMonths() {}, + onGetPrevScrollableMonths() {}, // month props renderMonthText: null, @@ -238,7 +242,8 @@ class DayPicker extends React.PureComponent { this.onMonthChange = this.onMonthChange.bind(this); this.onYearChange = this.onYearChange.bind(this); - this.multiplyScrollableMonths = this.multiplyScrollableMonths.bind(this); + this.getNextScrollableMonths = this.getNextScrollableMonths.bind(this); + this.getPrevScrollableMonths = this.getPrevScrollableMonths.bind(this); this.updateStateAfterMonthTransition = this.updateStateAfterMonthTransition.bind(this); this.openKeyboardShortcutsPanel = this.openKeyboardShortcutsPanel.bind(this); @@ -704,17 +709,29 @@ class DayPicker extends React.PureComponent { return false; } - multiplyScrollableMonths(e) { - const { onMultiplyScrollableMonths } = this.props; + getNextScrollableMonths(e) { + const { onGetNextScrollableMonths } = this.props; if (e) e.preventDefault(); - if (onMultiplyScrollableMonths) onMultiplyScrollableMonths(e); + if (onGetNextScrollableMonths) onGetNextScrollableMonths(e); this.setState(({ scrollableMonthMultiple }) => ({ scrollableMonthMultiple: scrollableMonthMultiple + 1, })); } + getPrevScrollableMonths(e) { + const { numberOfMonths, onGetPrevScrollableMonths } = this.props; + if (e) e.preventDefault(); + + if (onGetPrevScrollableMonths) onGetPrevScrollableMonths(e); + + this.setState(({ currentMonth, scrollableMonthMultiple }) => ({ + currentMonth: currentMonth.clone().subtract(numberOfMonths, 'month'), + scrollableMonthMultiple: scrollableMonthMultiple + 1, + })); + } + isHorizontal() { const { orientation } = this.props; return orientation === HORIZONTAL_ORIENTATION; @@ -842,7 +859,7 @@ class DayPicker extends React.PureComponent { }); } - renderNavigation() { + renderNavigation(navDirection = null) { const { dayPickerNavigationInlineStyles, disablePrev, @@ -862,16 +879,22 @@ class DayPicker extends React.PureComponent { return null; } - const onNextMonthClick = orientation === VERTICAL_SCROLLABLE - ? this.multiplyScrollableMonths - : this.onNextMonthClick; + const onPrevMonthClick = + orientation === VERTICAL_SCROLLABLE + ? this.getPrevScrollableMonths + : this.onPrevMonthClick; + + const onNextMonthClick = + orientation === VERTICAL_SCROLLABLE + ? this.getNextScrollableMonths + : this.onNextMonthClick; return ( ); } @@ -1118,6 +1147,7 @@ class DayPicker extends React.PureComponent { )} ref={this.setTransitionContainerRef} > + {verticalScrollable && this.renderNavigation(PREV_NAV)} - {verticalScrollable && this.renderNavigation()} + {verticalScrollable && this.renderNavigation(NEXT_NAV)} {!verticalScrollable diff --git a/src/components/DayPickerNavigation.jsx b/src/components/DayPickerNavigation.jsx index a0103173ca..cd769fec84 100644 --- a/src/components/DayPickerNavigation.jsx +++ b/src/components/DayPickerNavigation.jsx @@ -40,6 +40,8 @@ const propTypes = forbidExtraProps({ renderNavPrevButton: PropTypes.func, renderNavNextButton: PropTypes.func, + showNavPrevButton: PropTypes.bool, + showNavNextButton: PropTypes.bool, }); const defaultProps = { @@ -60,6 +62,8 @@ const defaultProps = { renderNavPrevButton: null, renderNavNextButton: null, + showNavPrevButton: true, + showNavNextButton: true, }; function DayPickerNavigation({ @@ -76,6 +80,8 @@ function DayPickerNavigation({ phrases, renderNavPrevButton, renderNavNextButton, + showNavPrevButton, + showNavNextButton, styles, }) { const isHorizontal = orientation === HORIZONTAL_ORIENTATION; @@ -151,19 +157,21 @@ function DayPickerNavigation({ hasInlineStyles && inlineStyles, )} > - {!isVerticalScrollable && ( - renderNavPrevButton ? renderNavPrevButton({ - ariaLabel: phrases.jumpToPrevMonth, - disabled: disablePrev, - onClick: disablePrev ? undefined : onPrevMonthClick, - onKeyUp: disablePrev ? undefined : (e) => { - const { key } = e; - if (key === 'Enter' || key === ' ') onPrevMonthClick(e); - }, - onMouseUp: disablePrev ? undefined : (e) => { - e.currentTarget.blur(); - }, - }) : ( + {showNavPrevButton && + (renderNavPrevButton ? ( + renderNavPrevButton({ + ariaLabel: phrases.jumpToPrevMonth, + disabled: disablePrev, + onClick: disablePrev ? undefined : onPrevMonthClick, + onKeyUp: disablePrev ? undefined : (e) => { + const { key } = e; + if (key === 'Enter' || key === ' ') onPrevMonthClick(e); + }, + onMouseUp: disablePrev ? undefined : (e) => { + e.currentTarget.blur(); + }, + }) + ) : (
    {navPrevIcon}
    - ) - )} - - {renderNavNextButton ? renderNavNextButton({ - ariaLabel: phrases.jumpToNextMonth, - disabled: disableNext, - onClick: disableNext ? undefined : onNextMonthClick, - onKeyUp: disableNext ? undefined : (e) => { - const { key } = e; - if (key === 'Enter' || key === ' ') onNextMonthClick(e); - }, - onMouseUp: disableNext ? undefined : (e) => { - e.currentTarget.blur(); - }, - }) : ( -
    { + const { key } = e; + if (key === 'Enter' || key === ' ') onNextMonthClick(e); + }, + onMouseUp: disableNext ? undefined : e => { + e.currentTarget.blur(); + }, + }) + ) : ( +
    { - const { key } = e; - if (key === 'Enter' || key === ' ') onNextMonthClick(e); - }} - onMouseUp={disableNext ? undefined : (e) => { - e.currentTarget.blur(); - }} - > - {navNextIcon} -
    - )} + )} + aria-disabled={disableNext ? true : undefined} + aria-label={phrases.jumpToNextMonth} + onClick={disableNext ? undefined : onNextMonthClick} + onKeyUp={ disableNext ? undefined : (e) => { + const { key } = e; + if (key === 'Enter' || key === ' ') onNextMonthClick(e); + }} + onMouseUp={disableNext ? undefined : (e) => { + e.currentTarget.blur(); + }} + > + {navNextIcon} +
    + ))} ); } @@ -341,8 +351,7 @@ export default withStyles(({ reactDates: { color, zIndex } }) => ({ }, }, - DayPickerNavigation_button__horizontal: { - }, + DayPickerNavigation_button__horizontal: {}, DayPickerNavigation_button__horizontalDefault: { position: 'absolute', @@ -368,8 +377,7 @@ export default withStyles(({ reactDates: { color, zIndex } }) => ({ right: noflip(22), }, - DayPickerNavigation_button__vertical: { - }, + DayPickerNavigation_button__vertical: {}, DayPickerNavigation_button__verticalDefault: { padding: 5, @@ -382,8 +390,7 @@ export default withStyles(({ reactDates: { color, zIndex } }) => ({ width: '50%', }, - DayPickerNavigation_prevButton__verticalDefault: { - }, + DayPickerNavigation_prevButton__verticalDefault: {}, DayPickerNavigation_nextButton__verticalDefault: { borderLeft: noflip(0), @@ -393,6 +400,10 @@ export default withStyles(({ reactDates: { color, zIndex } }) => ({ width: '100%', }, + DayPickerNavigation_prevButton__verticalScrollableDefault: { + width: '100%', + }, + DayPickerNavigation_svg__horizontal: { height: 19, width: 19, diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 37a8dc250d..6c197e37eb 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -259,7 +259,8 @@ export default class DayPickerRangeController extends React.PureComponent { this.onNextMonthClick = this.onNextMonthClick.bind(this); this.onMonthChange = this.onMonthChange.bind(this); this.onYearChange = this.onYearChange.bind(this); - this.onMultiplyScrollableMonths = this.onMultiplyScrollableMonths.bind(this); + this.onGetNextScrollableMonths = this.onGetNextScrollableMonths.bind(this); + this.onGetPrevScrollableMonths = this.onGetPrevScrollableMonths.bind(this); this.getFirstFocusableDay = this.getFirstFocusableDay.bind(this); } @@ -989,7 +990,7 @@ export default class DayPickerRangeController extends React.PureComponent { }); } - onMultiplyScrollableMonths() { + onGetNextScrollableMonths() { const { numberOfMonths, enableOutsideDays } = this.props; const { currentMonth, visibleDays } = this.state; @@ -1005,6 +1006,22 @@ export default class DayPickerRangeController extends React.PureComponent { }); } + onGetPrevScrollableMonths() { + const { numberOfMonths, enableOutsideDays } = this.props; + const { currentMonth, visibleDays } = this.state; + + const firstPreviousMonth = currentMonth.clone().subtract(numberOfMonths, 'month'); + const newVisibleDays = getVisibleDays(firstPreviousMonth, numberOfMonths, enableOutsideDays, true); + + this.setState({ + currentMonth: firstPreviousMonth.clone(), + visibleDays: { + ...visibleDays, + ...this.getModifiers(newVisibleDays), + }, + }); + } + getFirstFocusableDay(newMonth) { const { startDate, @@ -1317,7 +1334,8 @@ export default class DayPickerRangeController extends React.PureComponent { onTab={onTab} onShiftTab={onShiftTab} onYearChange={this.onYearChange} - onMultiplyScrollableMonths={this.onMultiplyScrollableMonths} + onGetNextScrollableMonths={this.onGetNextScrollableMonths} + onGetPrevScrollableMonths={this.onGetPrevScrollableMonths} monthFormat={monthFormat} renderMonthText={renderMonthText} renderWeekHeaderElement={renderWeekHeaderElement} diff --git a/test/components/DayPickerNavigation_spec.jsx b/test/components/DayPickerNavigation_spec.jsx index f61e5ff109..8343ae95ca 100644 --- a/test/components/DayPickerNavigation_spec.jsx +++ b/test/components/DayPickerNavigation_spec.jsx @@ -4,7 +4,6 @@ import sinon from 'sinon-sandbox'; import { shallow } from 'enzyme'; import DayPickerNavigation from '../../src/components/DayPickerNavigation'; -import { VERTICAL_SCROLLABLE } from '../../src/constants'; import RightArrow from '../../src/components/RightArrow'; import LeftArrow from '../../src/components/LeftArrow'; @@ -15,8 +14,13 @@ describe('DayPickerNavigation', () => { expect(wrapper.find('[role="button"]')).to.have.lengthOf(2); }); - it('renders one button when vertically scrollable', () => { - const wrapper = shallow().dive(); + it('renders one button when showNavNextButton is false', () => { + const wrapper = shallow().dive(); + expect(wrapper.find('[role="button"]')).to.have.lengthOf(1); + }); + + it('renders one button when showNavNextButton is false', () => { + const wrapper = shallow().dive(); expect(wrapper.find('[role="button"]')).to.have.lengthOf(1); }); diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index 6904f62e2b..7d09c1b45e 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -4142,7 +4142,7 @@ describe('DayPickerRangeController', () => { }); }); - it('return value now has modifier arg for day after multiplying number of months', () => { + it('return value now has modifier arg for day after getting next scrollable months', () => { const modifierToAdd = 'foo'; const futureDateAfterMultiply = today.clone().add(4, 'months'); const monthISO = toISOMonthString(futureDateAfterMultiply); @@ -4160,11 +4160,34 @@ describe('DayPickerRangeController', () => { )).instance(); let modifiers = wrapper.addModifier(updatedDays, futureDateAfterMultiply, modifierToAdd); expect(Array.from(modifiers[monthISO][todayISO])).to.not.contain(modifierToAdd); - wrapper.onMultiplyScrollableMonths(); + wrapper.onGetNextScrollableMonths(); modifiers = wrapper.addModifier(updatedDays, futureDateAfterMultiply, modifierToAdd); expect(Array.from(modifiers[monthISO][todayISO])).to.contain(modifierToAdd); }); + it('return value now has modifier arg for day after getting previous scrollable months', () => { + const modifierToAdd = 'foo'; + const pastDateAfterMultiply = today.clone().subtract(4, 'months'); + const monthISO = toISOMonthString(pastDateAfterMultiply); + const todayISO = toISODateString(pastDateAfterMultiply); + const updatedDays = { + [monthISO]: { [todayISO]: new Set(['bar', 'baz']) }, + }; + const wrapper = shallow(( + + )).instance(); + let modifiers = wrapper.addModifier(updatedDays, pastDateAfterMultiply, modifierToAdd); + expect(Array.from(modifiers[monthISO][todayISO])).to.not.contain(modifierToAdd); + wrapper.onGetPreviousScrollableMonths(); + modifiers = wrapper.addModifier(updatedDays, pastDateAfterMultiply, modifierToAdd); + expect(Array.from(modifiers[monthISO][todayISO])).to.contain(modifierToAdd); + }); + describe('#addModifierToRange', () => { let addModifierSpy; beforeEach(() => { diff --git a/test/components/DayPicker_spec.jsx b/test/components/DayPicker_spec.jsx index 63fb8e506c..984896a8ad 100644 --- a/test/components/DayPicker_spec.jsx +++ b/test/components/DayPicker_spec.jsx @@ -208,13 +208,30 @@ describe('DayPicker', () => { }); describe('props.orientation === VERTICAL_SCROLLABLE', () => { - it('uses multiplyScrollableMonths instead of onNextMonthClick', () => { + it('renders two DayPickerNavigations', () => { const wrapper = shallow( , { disableLifecycleMethods: false }, ).dive(); - const nav = wrapper.find(DayPickerNavigation); - expect(nav.prop('onNextMonthClick')).to.equal(wrapper.instance().multiplyScrollableMonths); + expect(wrapper.find(DayPickerNavigation)).to.have.length(2); + }); + + it('uses getNextScrollableMonths instead of onNextMonthClick', () => { + const wrapper = shallow( + , + { disableLifecycleMethods: false }, + ).dive(); + const nav = wrapper.find(DayPickerNavigation)[1]; + expect(nav.prop('onNextMonthClick')).to.equal(wrapper.instance().getNextScrollableMonths); + }); + + it('uses getPrevScrollableMonths instead of onNextMonthClick', () => { + const wrapper = shallow( + , + { disableLifecycleMethods: false }, + ).dive(); + const nav = wrapper.find(DayPickerNavigation)[0]; + expect(nav.prop('onPrevMonthClick')).to.equal(wrapper.instance().getPrevScrollableMonths); }); }); @@ -795,17 +812,32 @@ describe('DayPicker', () => { }); }); - describe('#multiplyScrollableMonths', () => { + describe('#getNextScrollableMonths', () => { + it('increments scrollableMonthMultiple', () => { + const wrapper = shallow().dive(); + wrapper.instance().getNextScrollableMonths(event); + expect(wrapper.state().scrollableMonthMultiple).to.equal(2); + }); + + it('increments scrollableMonthMultiple without an event', () => { + const wrapper = shallow().dive(); + wrapper.instance().getNextScrollableMonths(); + expect(wrapper.state().scrollableMonthMultiple).to.equal(2); + }); + }); + + describe('#getPrevScrollableMonths', () => { it('increments scrollableMonthMultiple', () => { const wrapper = shallow().dive(); - wrapper.instance().multiplyScrollableMonths(event); + wrapper.instance().getPrevScrollableMonths(event); expect(wrapper.state().scrollableMonthMultiple).to.equal(2); }); it('increments scrollableMonthMultiple without an event', () => { const wrapper = shallow().dive(); - wrapper.instance().multiplyScrollableMonths(); + wrapper.instance().getPrevScrollableMonths(); expect(wrapper.state().scrollableMonthMultiple).to.equal(2); + expect(wrapper.state().currentMonth).to.equal(moment().subtract(1, 'month')); }); }); From 1a68fa396429def841ac88de90307bc6d3fbf7c4 Mon Sep 17 00:00:00 2001 From: Rob Date: Fri, 10 Jan 2020 11:36:35 -0800 Subject: [PATCH 545/618] feature(DayPickerSingleDateController) Add support for noNavButtons (match DayPickerRangeController support) --- src/components/DayPickerSingleDateController.jsx | 4 ++++ stories/DayPickerSingleDateController.js | 6 ++++++ .../DayPickerSingleDateController_spec.jsx | 13 +++++++++++++ 3 files changed, 23 insertions(+) diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 2ff010b60e..a1df832e57 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -70,6 +70,7 @@ const propTypes = forbidExtraProps({ navNext: PropTypes.node, renderNavPrevButton: PropTypes.func, renderNavNextButton: PropTypes.func, + noNavButtons: PropTypes.bool, onPrevMonthClick: PropTypes.func, onNextMonthClick: PropTypes.func, @@ -131,6 +132,7 @@ const defaultProps = { navNext: null, renderNavPrevButton: null, renderNavNextButton: null, + noNavButtons: false, onPrevMonthClick() {}, onNextMonthClick() {}, @@ -589,6 +591,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { navNext, renderNavPrevButton, renderNavNextButton, + noNavButtons, onOutsideClick, onShiftTab, onTab, @@ -646,6 +649,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { navNext={navNext} renderNavPrevButton={renderNavPrevButton} renderNavNextButton={renderNavNextButton} + noNavButtons={noNavButtons} renderMonthText={renderMonthText} renderWeekHeaderElement={renderWeekHeaderElement} renderCalendarDay={renderCalendarDay} diff --git a/stories/DayPickerSingleDateController.js b/stories/DayPickerSingleDateController.js index 5752fef190..60c2728733 100644 --- a/stories/DayPickerSingleDateController.js +++ b/stories/DayPickerSingleDateController.js @@ -363,4 +363,10 @@ storiesOf('DayPickerSingleDateController', module) onNextMonthClick={action('DayPickerSingleDateController::onNextMonthClick')} verticalBorderSpacing={16} /> + ))) + .add('with no nav buttons', withInfo()(() => ( + ))); diff --git a/test/components/DayPickerSingleDateController_spec.jsx b/test/components/DayPickerSingleDateController_spec.jsx index 6cd614a91c..d60ef40e60 100644 --- a/test/components/DayPickerSingleDateController_spec.jsx +++ b/test/components/DayPickerSingleDateController_spec.jsx @@ -6,6 +6,7 @@ import moment from 'moment'; import DayPicker from '../../src/components/DayPicker'; import DayPickerSingleDateController from '../../src/components/DayPickerSingleDateController'; +import DayPickerNavigation from '../../src/components/DayPickerNavigation'; import toISODateString from '../../src/utils/toISODateString'; import toISOMonthString from '../../src/utils/toISOMonthString'; @@ -1540,5 +1541,17 @@ describe('DayPickerSingleDateController', () => { expect(dayPicker.props().initialVisibleMonth().isSame(today, 'day')).to.equal(true); }); }); + + describe('noNavButtons prop', () => { + it('renders navigation button', () => { + const wrapper = shallow().dive().dive(); + expect(wrapper.find(DayPickerNavigation)).to.have.lengthOf(1); + }); + + it('does not render navigation button when noNavButtons prop applied', () => { + const wrapper = shallow().dive().dive(); + expect(wrapper.find(DayPickerNavigation)).to.have.lengthOf(0); + }); + }); }); }); From 5071e9d08fcbc65df95892fb7cb0e867bc39bebf Mon Sep 17 00:00:00 2001 From: Nicole Kinser Date: Fri, 10 Jan 2020 14:00:52 -0800 Subject: [PATCH 546/618] style changes --- src/components/DayPicker.jsx | 2 +- src/components/DayPickerNavigation.jsx | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index 34e91299e6..9f5a5aacc1 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -859,7 +859,7 @@ class DayPicker extends React.PureComponent { }); } - renderNavigation(navDirection = null) { + renderNavigation(navDirection) { const { dayPickerNavigationInlineStyles, disablePrev, diff --git a/src/components/DayPickerNavigation.jsx b/src/components/DayPickerNavigation.jsx index cd769fec84..641cf1df24 100644 --- a/src/components/DayPickerNavigation.jsx +++ b/src/components/DayPickerNavigation.jsx @@ -165,7 +165,9 @@ function DayPickerNavigation({ onClick: disablePrev ? undefined : onPrevMonthClick, onKeyUp: disablePrev ? undefined : (e) => { const { key } = e; - if (key === 'Enter' || key === ' ') onPrevMonthClick(e); + if (key === 'Enter' || key === ' ') { + onPrevMonthClick(e); + } }, onMouseUp: disablePrev ? undefined : (e) => { e.currentTarget.blur(); @@ -202,7 +204,9 @@ function DayPickerNavigation({ onClick={disablePrev ? undefined : onPrevMonthClick} onKeyUp={disablePrev ? undefined : (e) => { const { key } = e; - if (key === 'Enter' || key === ' ') onPrevMonthClick(e); + if (key === 'Enter' || key === ' ') { + onPrevMonthClick(e); + } }} onMouseUp={disablePrev ? undefined : (e) => { e.currentTarget.blur(); @@ -220,7 +224,9 @@ function DayPickerNavigation({ onClick: disableNext ? undefined : onNextMonthClick, onKeyUp: disableNext ? undefined : (e) => { const { key } = e; - if (key === 'Enter' || key === ' ') onNextMonthClick(e); + if (key === 'Enter' || key === ' ') { + onNextMonthClick(e) + }; }, onMouseUp: disableNext ? undefined : e => { e.currentTarget.blur(); @@ -258,7 +264,9 @@ function DayPickerNavigation({ onClick={disableNext ? undefined : onNextMonthClick} onKeyUp={ disableNext ? undefined : (e) => { const { key } = e; - if (key === 'Enter' || key === ' ') onNextMonthClick(e); + if (key === 'Enter' || key === ' ') { + onNextMonthClick(e); + } }} onMouseUp={disableNext ? undefined : (e) => { e.currentTarget.blur(); From 0718c79d9a2474b73b8666a0dca8e3885c54aade Mon Sep 17 00:00:00 2001 From: Nicole Kinser Date: Fri, 10 Jan 2020 14:45:59 -0800 Subject: [PATCH 547/618] update tests --- src/components/DayPickerNavigation.jsx | 2 +- .../DayPickerRangeController_spec.jsx | 12 ++++---- test/components/DayPicker_spec.jsx | 29 +++++++------------ 3 files changed, 18 insertions(+), 25 deletions(-) diff --git a/src/components/DayPickerNavigation.jsx b/src/components/DayPickerNavigation.jsx index 641cf1df24..6a702138c4 100644 --- a/src/components/DayPickerNavigation.jsx +++ b/src/components/DayPickerNavigation.jsx @@ -228,7 +228,7 @@ function DayPickerNavigation({ onNextMonthClick(e) }; }, - onMouseUp: disableNext ? undefined : e => { + onMouseUp: disableNext ? undefined : (e) => { e.currentTarget.blur(); }, }) diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index 7d09c1b45e..d1efc1484d 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -4167,11 +4167,11 @@ describe('DayPickerRangeController', () => { it('return value now has modifier arg for day after getting previous scrollable months', () => { const modifierToAdd = 'foo'; - const pastDateAfterMultiply = today.clone().subtract(4, 'months'); + const pastDateAfterMultiply = today.clone().subtract(3, 'months'); const monthISO = toISOMonthString(pastDateAfterMultiply); - const todayISO = toISODateString(pastDateAfterMultiply); + const dayISO = toISODateString(pastDateAfterMultiply); const updatedDays = { - [monthISO]: { [todayISO]: new Set(['bar', 'baz']) }, + [monthISO]: { [dayISO]: new Set(['bar', 'baz']) }, }; const wrapper = shallow(( { /> )).instance(); let modifiers = wrapper.addModifier(updatedDays, pastDateAfterMultiply, modifierToAdd); - expect(Array.from(modifiers[monthISO][todayISO])).to.not.contain(modifierToAdd); - wrapper.onGetPreviousScrollableMonths(); + expect(Array.from(modifiers[monthISO][dayISO])).to.not.contain(modifierToAdd); + wrapper.onGetPrevScrollableMonths(); modifiers = wrapper.addModifier(updatedDays, pastDateAfterMultiply, modifierToAdd); - expect(Array.from(modifiers[monthISO][todayISO])).to.contain(modifierToAdd); + expect(Array.from(modifiers[monthISO][dayISO])).to.contain(modifierToAdd); }); describe('#addModifierToRange', () => { diff --git a/test/components/DayPicker_spec.jsx b/test/components/DayPicker_spec.jsx index 984896a8ad..6259c7ab4e 100644 --- a/test/components/DayPicker_spec.jsx +++ b/test/components/DayPicker_spec.jsx @@ -5,6 +5,7 @@ import sinon from 'sinon-sandbox'; import { mount, shallow } from 'enzyme'; import * as isDayVisible from '../../src/utils/isDayVisible'; +import isSameMonth from '../../src/utils/isSameMonth'; import DayPicker, { PureDayPicker } from '../../src/components/DayPicker'; import CalendarMonthGrid from '../../src/components/CalendarMonthGrid'; @@ -221,8 +222,10 @@ describe('DayPicker', () => { , { disableLifecycleMethods: false }, ).dive(); - const nav = wrapper.find(DayPickerNavigation)[1]; - expect(nav.prop('onNextMonthClick')).to.equal(wrapper.instance().getNextScrollableMonths); + expect(wrapper.find(DayPickerNavigation)).to.have.length(2); + const nav = wrapper.find(DayPickerNavigation).get(1); + console.log(nav); + expect(nav.props.onNextMonthClick).to.equal(wrapper.instance().getNextScrollableMonths); }); it('uses getPrevScrollableMonths instead of onNextMonthClick', () => { @@ -230,8 +233,10 @@ describe('DayPicker', () => { , { disableLifecycleMethods: false }, ).dive(); - const nav = wrapper.find(DayPickerNavigation)[0]; - expect(nav.prop('onPrevMonthClick')).to.equal(wrapper.instance().getPrevScrollableMonths); + expect(wrapper.find(DayPickerNavigation)).to.have.length(2); + const nav = wrapper.find(DayPickerNavigation).get(0); + console.log(nav); + expect(nav.props.onPrevMonthClick).to.equal(wrapper.instance().getPrevScrollableMonths); }); }); @@ -818,26 +823,14 @@ describe('DayPicker', () => { wrapper.instance().getNextScrollableMonths(event); expect(wrapper.state().scrollableMonthMultiple).to.equal(2); }); - - it('increments scrollableMonthMultiple without an event', () => { - const wrapper = shallow().dive(); - wrapper.instance().getNextScrollableMonths(); - expect(wrapper.state().scrollableMonthMultiple).to.equal(2); - }); }); describe('#getPrevScrollableMonths', () => { - it('increments scrollableMonthMultiple', () => { - const wrapper = shallow().dive(); - wrapper.instance().getPrevScrollableMonths(event); - expect(wrapper.state().scrollableMonthMultiple).to.equal(2); - }); - - it('increments scrollableMonthMultiple without an event', () => { + it('increments scrollableMonthMultiple and updates currentMonth', () => { const wrapper = shallow().dive(); wrapper.instance().getPrevScrollableMonths(); expect(wrapper.state().scrollableMonthMultiple).to.equal(2); - expect(wrapper.state().currentMonth).to.equal(moment().subtract(1, 'month')); + expect(isSameMonth(wrapper.state().currentMonth, moment().subtract(2, 'month'))).to.equal(true); }); }); From a3e76feb193d09af640e226bf33f7d98f628cad8 Mon Sep 17 00:00:00 2001 From: Nicole Kinser Date: Mon, 13 Jan 2020 10:35:11 -0800 Subject: [PATCH 548/618] changes for single date picker --- src/components/DayPicker.jsx | 60 +++++++++---------- src/components/DayPickerNavigation.jsx | 27 ++++----- src/components/DayPickerRangeController.jsx | 4 +- .../DayPickerSingleDateController.jsx | 26 +++++++- stories/DayPickerRangeController.js | 17 ++++++ stories/DayPickerSingleDateController.js | 11 +++- test/components/DayPickerNavigation_spec.jsx | 34 +++++++++++ .../DayPickerSingleDateController_spec.jsx | 28 ++++++++- test/components/DayPicker_spec.jsx | 2 - 9 files changed, 155 insertions(+), 54 deletions(-) diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index 9f5a5aacc1..86ebe0c9a8 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -679,6 +679,29 @@ class DayPicker extends React.PureComponent { this.transitionContainer = ref; } + getNextScrollableMonths(e) { + const { onGetNextScrollableMonths } = this.props; + if (e) e.preventDefault(); + + if (onGetNextScrollableMonths) onGetNextScrollableMonths(e); + + this.setState(({ scrollableMonthMultiple }) => ({ + scrollableMonthMultiple: scrollableMonthMultiple + 1, + })); + } + + getPrevScrollableMonths(e) { + const { numberOfMonths, onGetPrevScrollableMonths } = this.props; + if (e) e.preventDefault(); + + if (onGetPrevScrollableMonths) onGetPrevScrollableMonths(e); + + this.setState(({ currentMonth, scrollableMonthMultiple }) => ({ + currentMonth: currentMonth.clone().subtract(numberOfMonths, 'month'), + scrollableMonthMultiple: scrollableMonthMultiple + 1, + })); + } + maybeTransitionNextMonth(newFocusedDate) { const { numberOfMonths } = this.props; const { currentMonth, focusedDate } = this.state; @@ -709,29 +732,6 @@ class DayPicker extends React.PureComponent { return false; } - getNextScrollableMonths(e) { - const { onGetNextScrollableMonths } = this.props; - if (e) e.preventDefault(); - - if (onGetNextScrollableMonths) onGetNextScrollableMonths(e); - - this.setState(({ scrollableMonthMultiple }) => ({ - scrollableMonthMultiple: scrollableMonthMultiple + 1, - })); - } - - getPrevScrollableMonths(e) { - const { numberOfMonths, onGetPrevScrollableMonths } = this.props; - if (e) e.preventDefault(); - - if (onGetPrevScrollableMonths) onGetPrevScrollableMonths(e); - - this.setState(({ currentMonth, scrollableMonthMultiple }) => ({ - currentMonth: currentMonth.clone().subtract(numberOfMonths, 'month'), - scrollableMonthMultiple: scrollableMonthMultiple + 1, - })); - } - isHorizontal() { const { orientation } = this.props; return orientation === HORIZONTAL_ORIENTATION; @@ -879,15 +879,13 @@ class DayPicker extends React.PureComponent { return null; } - const onPrevMonthClick = - orientation === VERTICAL_SCROLLABLE - ? this.getPrevScrollableMonths - : this.onPrevMonthClick; + const onPrevMonthClick = orientation === VERTICAL_SCROLLABLE + ? this.getPrevScrollableMonths + : this.onPrevMonthClick; - const onNextMonthClick = - orientation === VERTICAL_SCROLLABLE - ? this.getNextScrollableMonths - : this.onNextMonthClick; + const onNextMonthClick = orientation === VERTICAL_SCROLLABLE + ? this.getNextScrollableMonths + : this.onNextMonthClick; return ( - {showNavPrevButton && - (renderNavPrevButton ? ( + {showNavPrevButton + && (renderNavPrevButton ? ( renderNavPrevButton({ ariaLabel: phrases.jumpToPrevMonth, disabled: disablePrev, @@ -195,7 +193,8 @@ function DayPickerNavigation({ ...(isDefaultNavPrev ? [ styles.DayPickerNavigation_button__verticalDefault, styles.DayPickerNavigation_prevButton__verticalDefault, - isVerticalScrollable && styles.DayPickerNavigation_prevButton__verticalScrollableDefault, + isVerticalScrollable + && styles.DayPickerNavigation_prevButton__verticalScrollableDefault, ] : []), ] : []), )} @@ -216,8 +215,8 @@ function DayPickerNavigation({ ))} - {showNavNextButton && - (renderNavNextButton ? ( + {showNavNextButton + && (renderNavNextButton ? ( renderNavNextButton({ ariaLabel: phrases.jumpToNextMonth, disabled: disableNext, @@ -225,8 +224,8 @@ function DayPickerNavigation({ onKeyUp: disableNext ? undefined : (e) => { const { key } = e; if (key === 'Enter' || key === ' ') { - onNextMonthClick(e) - }; + onNextMonthClick(e); + } }, onMouseUp: disableNext ? undefined : (e) => { e.currentTarget.blur(); @@ -234,7 +233,7 @@ function DayPickerNavigation({ }) ) : (
    { + onKeyUp={disableNext ? undefined : (e) => { const { key } = e; if (key === 'Enter' || key === ' ') { onNextMonthClick(e); diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 6c197e37eb..c657628666 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -1011,7 +1011,9 @@ export default class DayPickerRangeController extends React.PureComponent { const { currentMonth, visibleDays } = this.state; const firstPreviousMonth = currentMonth.clone().subtract(numberOfMonths, 'month'); - const newVisibleDays = getVisibleDays(firstPreviousMonth, numberOfMonths, enableOutsideDays, true); + const newVisibleDays = getVisibleDays( + firstPreviousMonth, numberOfMonths, enableOutsideDays, true, + ); this.setState({ currentMonth: firstPreviousMonth.clone(), diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 2ff010b60e..dd5e395000 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -194,7 +194,8 @@ export default class DayPickerSingleDateController extends React.PureComponent { this.onNextMonthClick = this.onNextMonthClick.bind(this); this.onMonthChange = this.onMonthChange.bind(this); this.onYearChange = this.onYearChange.bind(this); - this.onMultiplyScrollableMonths = this.onMultiplyScrollableMonths.bind(this); + this.onGetNextScrollableMonths = this.onGetNextScrollableMonths.bind(this); + this.onGetPrevScrollableMonths = this.onGetPrevScrollableMonths.bind(this); this.getFirstFocusableDay = this.getFirstFocusableDay.bind(this); } @@ -461,7 +462,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { }); } - onMultiplyScrollableMonths() { + onGetNextScrollableMonths() { const { numberOfMonths, enableOutsideDays } = this.props; const { currentMonth, visibleDays } = this.state; @@ -477,6 +478,24 @@ export default class DayPickerSingleDateController extends React.PureComponent { }); } + onGetPrevScrollableMonths() { + const { numberOfMonths, enableOutsideDays } = this.props; + const { currentMonth, visibleDays } = this.state; + + const firstPreviousMonth = currentMonth.clone().subtract(numberOfMonths, 'month'); + const newVisibleDays = getVisibleDays( + firstPreviousMonth, numberOfMonths, enableOutsideDays, true, + ); + + this.setState({ + currentMonth: firstPreviousMonth.clone(), + visibleDays: { + ...visibleDays, + ...this.getModifiers(newVisibleDays), + }, + }); + } + getFirstFocusableDay(newMonth) { const { date, numberOfMonths } = this.props; @@ -632,7 +651,8 @@ export default class DayPickerSingleDateController extends React.PureComponent { onNextMonthClick={this.onNextMonthClick} onMonthChange={this.onMonthChange} onYearChange={this.onYearChange} - onMultiplyScrollableMonths={this.onMultiplyScrollableMonths} + onGetNextScrollableMonths={this.onGetNextScrollableMonths} + onGetPrevScrollableMonths={this.onGetPrevScrollableMonths} monthFormat={monthFormat} withPortal={withPortal} hidden={!focused} diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index 62c3567940..e4f9a9a769 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -442,6 +442,23 @@ storiesOf('DayPickerRangeController', module)
    )} + navPrev={( +
    + + Show More Months + +
    + )} /> ))) diff --git a/stories/DayPickerSingleDateController.js b/stories/DayPickerSingleDateController.js index 5752fef190..4d454557ce 100644 --- a/stories/DayPickerSingleDateController.js +++ b/stories/DayPickerSingleDateController.js @@ -10,7 +10,7 @@ import isSameDay from '../src/utils/isSameDay'; import isInclusivelyAfterDay from '../src/utils/isInclusivelyAfterDay'; import CustomizableCalendarDay, { defaultStyles, selectedStyles } from '../src/components/CustomizableCalendarDay'; -import { NAV_POSITION_BOTTOM, VERTICAL_ORIENTATION } from '../src/constants'; +import { NAV_POSITION_BOTTOM, VERTICAL_ORIENTATION, VERTICAL_SCROLLABLE } from '../src/constants'; import DayPickerSingleDateControllerWrapper from '../examples/DayPickerSingleDateControllerWrapper'; @@ -220,6 +220,15 @@ storiesOf('DayPickerSingleDateController', module) orientation={VERTICAL_ORIENTATION} /> ))) + .add('verticalScrollable', withInfo()(() => ( + + ))) .add('with custom month navigation icons', withInfo()(() => ( { expect(onPrevMonthStub).to.have.property('callCount', 1); prevMonthButton.simulate('keyup', { key: ' ' }); expect(onPrevMonthStub).to.have.property('callCount', 2); + prevMonthButton.simulate('keyup', { key: 'k' }); + expect(onPrevMonthStub).to.have.property('callCount', 2); }); it('props.onNextMonthClick is triggered by next month button key up', () => { @@ -145,6 +147,8 @@ describe('DayPickerNavigation', () => { expect(onNextMonthStub).to.have.property('callCount', 1); nextMonthButton.simulate('keyup', { key: ' ' }); expect(onNextMonthStub).to.have.property('callCount', 2); + nextMonthButton.simulate('keyup', { key: 'k' }); + expect(onNextMonthStub).to.have.property('callCount', 2); }); it('props.onPrevMonthClick is triggered by custom prev month button click', () => { @@ -192,5 +196,35 @@ describe('DayPickerNavigation', () => { nextMonthButton.simulate('click'); expect(onNextMonthStub).to.have.property('callCount', 0); }); + + it('props.onPrevMonthClick is triggered by custom prev month button key up', () => { + const onPrevMonthStub = sinon.stub(); + const renderNavPrevButtonStub = sinon.stub().onCall(0).callsFake(({ onKeyUp }) => ); + const prevMonthButton = shallow().dive().find('button').at(0); + prevMonthButton.simulate('keyup', { key: 'Enter' }); + expect(onPrevMonthStub).to.have.property('callCount', 1); + prevMonthButton.simulate('keyup', { key: ' ' }); + expect(onPrevMonthStub).to.have.property('callCount', 2); + prevMonthButton.simulate('keyup', { key: 'k' }); + expect(onPrevMonthStub).to.have.property('callCount', 2); + }); + + it('props.onNextMonthClick is triggered by custom next month button key up', () => { + const onNextMonthStub = sinon.stub(); + const renderNavNextButtonStub = sinon.stub().onCall(0).callsFake(({ onKeyUp }) => ); + const nextMonthButton = shallow().dive().find('button').at(0); + nextMonthButton.simulate('keyup', { key: 'Enter' }); + expect(onNextMonthStub).to.have.property('callCount', 1); + nextMonthButton.simulate('keyup', { key: ' ' }); + expect(onNextMonthStub).to.have.property('callCount', 2); + nextMonthButton.simulate('keyup', { key: 'k' }); + expect(onNextMonthStub).to.have.property('callCount', 2); + }); }); }); diff --git a/test/components/DayPickerSingleDateController_spec.jsx b/test/components/DayPickerSingleDateController_spec.jsx index 6cd614a91c..268d1696f7 100644 --- a/test/components/DayPickerSingleDateController_spec.jsx +++ b/test/components/DayPickerSingleDateController_spec.jsx @@ -1178,7 +1178,7 @@ describe('DayPickerSingleDateController', () => { expect(Array.from(modifiers[nextMonthISO][nextMonthDayISO])).to.contain(modifierToAdd); }); - it('return value now has modifier arg for day after multiplying number of months', () => { + it('return value now has modifier arg for day after getting next scrollable months', () => { const modifierToAdd = 'foo'; const numberOfMonths = 2; const nextMonth = today.clone().add(numberOfMonths, 'month'); @@ -1197,10 +1197,34 @@ describe('DayPickerSingleDateController', () => { )).instance(); let modifiers = wrapper.addModifier(updatedDays, nextMonth, modifierToAdd); expect(Array.from(modifiers[nextMonthISO][nextMonthDayISO])).to.not.contain(modifierToAdd); - wrapper.onMultiplyScrollableMonths(); + wrapper.onGetNextScrollableMonths(); modifiers = wrapper.addModifier(updatedDays, nextMonth, modifierToAdd); expect(Array.from(modifiers[nextMonthISO][nextMonthDayISO])).to.contain(modifierToAdd); }); + + it('return value now has modifier arg for day after getting previous scrollable months', () => { + const modifierToAdd = 'foo'; + const numberOfMonths = 2; + const pastDateAfterMultiply = today.clone().subtract(numberOfMonths, 'months'); + const monthISO = toISOMonthString(pastDateAfterMultiply); + const dayISO = toISODateString(pastDateAfterMultiply); + const updatedDays = { + [monthISO]: { [dayISO]: new Set(['bar', 'baz']) }, + }; + const wrapper = shallow(( + + )).instance(); + let modifiers = wrapper.addModifier(updatedDays, pastDateAfterMultiply, modifierToAdd); + expect(Array.from(modifiers[monthISO][dayISO])).to.not.contain(modifierToAdd); + wrapper.onGetPrevScrollableMonths(); + modifiers = wrapper.addModifier(updatedDays, pastDateAfterMultiply, modifierToAdd); + expect(Array.from(modifiers[monthISO][dayISO])).to.contain(modifierToAdd); + }); }); describe('#deleteModifier', () => { diff --git a/test/components/DayPicker_spec.jsx b/test/components/DayPicker_spec.jsx index 6259c7ab4e..8b20aab508 100644 --- a/test/components/DayPicker_spec.jsx +++ b/test/components/DayPicker_spec.jsx @@ -224,7 +224,6 @@ describe('DayPicker', () => { ).dive(); expect(wrapper.find(DayPickerNavigation)).to.have.length(2); const nav = wrapper.find(DayPickerNavigation).get(1); - console.log(nav); expect(nav.props.onNextMonthClick).to.equal(wrapper.instance().getNextScrollableMonths); }); @@ -235,7 +234,6 @@ describe('DayPicker', () => { ).dive(); expect(wrapper.find(DayPickerNavigation)).to.have.length(2); const nav = wrapper.find(DayPickerNavigation).get(0); - console.log(nav); expect(nav.props.onPrevMonthClick).to.equal(wrapper.instance().getPrevScrollableMonths); }); }); From 48f7cd370fd0772cb118bb7059cd3be6c99bf326 Mon Sep 17 00:00:00 2001 From: Kevin Pan Date: Mon, 13 Jan 2020 14:54:57 -0800 Subject: [PATCH 549/618] Version 21.6.0 --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4d3b8fee9..1ab70db682 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ +## 21.6.0 + +- [new] Add functionality to see previous months for vertical scrollable calendar ([#1894](https://github.com/airbnb/react-dates/pull/1894)) +- [new] Add support for noNavButtons in DayPickerSingleDateController (match DayPickerRangeController support) ([#1895](https://github.com/airbnb/react-dates/pull/1895)) + ## 21.5.1 - [fix] Remove redundant overflowY:scroll on CalendarMonthGrid ([#1881](https://github.com/airbnb/react-dates/pull/1881)) diff --git a/package.json b/package.json index 7cefa80754..7f0e43f074 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "21.5.1", + "version": "21.6.0", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From d4cc1673c679cf994674ddf09330162e80fdd41a Mon Sep 17 00:00:00 2001 From: Nicole Kinser Date: Mon, 13 Jan 2020 17:59:18 -0800 Subject: [PATCH 550/618] Add ability to hide prev or next navigation button --- src/components/DayPicker.jsx | 10 ++++++++-- src/components/DayPickerRangeController.jsx | 8 ++++++++ src/components/DayPickerSingleDateController.jsx | 8 ++++++++ stories/DayPickerRangeController.js | 12 ++++++++++++ stories/DayPickerSingleDateController.js | 12 ++++++++++++ 5 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index 86ebe0c9a8..b0434d8c53 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -89,6 +89,8 @@ const propTypes = forbidExtraProps({ renderNavPrevButton: PropTypes.func, renderNavNextButton: PropTypes.func, noNavButtons: PropTypes.bool, + noNavNextButton: PropTypes.bool, + noNavPrevButton: PropTypes.bool, onPrevMonthClick: PropTypes.func, onNextMonthClick: PropTypes.func, onMonthChange: PropTypes.func, @@ -157,6 +159,8 @@ export const defaultProps = { renderNavPrevButton: null, renderNavNextButton: null, noNavButtons: false, + noNavNextButton: false, + noNavPrevButton: false, onPrevMonthClick() {}, onNextMonthClick() {}, onMonthChange() {}, @@ -868,6 +872,8 @@ class DayPicker extends React.PureComponent { navPrev, navNext, noNavButtons, + noNavNextButton, + noNavPrevButton, orientation, phrases, renderNavPrevButton, @@ -903,10 +909,10 @@ class DayPicker extends React.PureComponent { phrases={phrases} isRTL={isRTL} showNavNextButton={ - !(orientation === VERTICAL_SCROLLABLE && navDirection === PREV_NAV) + !(noNavNextButton || (orientation === VERTICAL_SCROLLABLE && navDirection === PREV_NAV)) } showNavPrevButton={ - !(orientation === VERTICAL_SCROLLABLE && navDirection === NEXT_NAV) + !(noNavPrevButton || (orientation === VERTICAL_SCROLLABLE && navDirection === NEXT_NAV)) } /> ); diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index c657628666..5087f7d6db 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -87,6 +87,8 @@ const propTypes = forbidExtraProps({ renderNavPrevButton: PropTypes.func, renderNavNextButton: PropTypes.func, noNavButtons: PropTypes.bool, + noNavNextButton: PropTypes.bool, + noNavPrevButton: PropTypes.bool, onPrevMonthClick: PropTypes.func, onNextMonthClick: PropTypes.func, @@ -156,6 +158,8 @@ const defaultProps = { renderNavPrevButton: null, renderNavNextButton: null, noNavButtons: false, + noNavNextButton: false, + noNavPrevButton: false, onPrevMonthClick() {}, onNextMonthClick() {}, @@ -1284,6 +1288,8 @@ export default class DayPickerRangeController extends React.PureComponent { renderNavPrevButton, renderNavNextButton, noNavButtons, + noNavNextButton, + noNavPrevButton, onOutsideClick, withPortal, enableOutsideDays, @@ -1355,6 +1361,8 @@ export default class DayPickerRangeController extends React.PureComponent { renderNavPrevButton={renderNavPrevButton} renderNavNextButton={renderNavNextButton} noNavButtons={noNavButtons} + noNavPrevButton={noNavPrevButton} + noNavNextButton={noNavNextButton} renderCalendarDay={renderCalendarDay} renderDayContents={renderDayContents} renderCalendarInfo={renderCalendarInfo} diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 274171c489..480132891b 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -71,6 +71,8 @@ const propTypes = forbidExtraProps({ renderNavPrevButton: PropTypes.func, renderNavNextButton: PropTypes.func, noNavButtons: PropTypes.bool, + noNavNextButton: PropTypes.bool, + noNavPrevButton: PropTypes.bool, onPrevMonthClick: PropTypes.func, onNextMonthClick: PropTypes.func, @@ -133,6 +135,8 @@ const defaultProps = { renderNavPrevButton: null, renderNavNextButton: null, noNavButtons: false, + noNavNextButton: false, + noNavPrevButton: false, onPrevMonthClick() {}, onNextMonthClick() {}, @@ -611,6 +615,8 @@ export default class DayPickerSingleDateController extends React.PureComponent { renderNavPrevButton, renderNavNextButton, noNavButtons, + noNavPrevButton, + noNavNextButton, onOutsideClick, onShiftTab, onTab, @@ -670,6 +676,8 @@ export default class DayPickerSingleDateController extends React.PureComponent { renderNavPrevButton={renderNavPrevButton} renderNavNextButton={renderNavNextButton} noNavButtons={noNavButtons} + noNavNextButton={noNavNextButton} + noNavPrevButton={noNavPrevButton} renderMonthText={renderMonthText} renderWeekHeaderElement={renderWeekHeaderElement} renderCalendarDay={renderCalendarDay} diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index e4f9a9a769..f12948bd93 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -653,6 +653,18 @@ storiesOf('DayPickerRangeController', module) noNavButtons /> ))) + .add('with no nav prev button', withInfo()(() => ( + + ))) + .add('with no nav next button', withInfo()(() => ( + + ))) .add('with minimum nights for the hovered date', withInfo()(() => ( + ))) + .add('with no nav prev button', withInfo()(() => ( + + ))) + .add('with no nav next button', withInfo()(() => ( + ))); From 97be8e9e573341c4716fe4dc940c619d8000dd7f Mon Sep 17 00:00:00 2001 From: Nicole Kinser Date: Mon, 13 Jan 2020 20:20:33 -0800 Subject: [PATCH 551/618] Add ability to not show prev or next navigation buttons --- src/components/DayPickerNavigation.jsx | 4 ++ stories/DayPickerRangeController.js | 30 +++++++++---- stories/DayPickerSingleDateController.js | 44 +++++++++++++------- test/components/DayPickerNavigation_spec.jsx | 7 ++++ 4 files changed, 62 insertions(+), 23 deletions(-) diff --git a/src/components/DayPickerNavigation.jsx b/src/components/DayPickerNavigation.jsx index 5cb4ecdf3e..56cd811ee9 100644 --- a/src/components/DayPickerNavigation.jsx +++ b/src/components/DayPickerNavigation.jsx @@ -84,6 +84,10 @@ function DayPickerNavigation({ showNavNextButton, styles, }) { + if (!showNavNextButton && !showNavPrevButton) { + return null; + } + const isHorizontal = orientation === HORIZONTAL_ORIENTATION; const isVertical = orientation !== HORIZONTAL_ORIENTATION; const isVerticalScrollable = orientation === VERTICAL_SCROLLABLE; diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index f12948bd93..e89e2d77c4 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -654,16 +654,30 @@ storiesOf('DayPickerRangeController', module) /> ))) .add('with no nav prev button', withInfo()(() => ( - +
    + +
    ))) .add('with no nav next button', withInfo()(() => ( - +
    + +
    ))) .add('with minimum nights for the hovered date', withInfo()(() => ( ))) .add('verticalScrollable', withInfo()(() => ( - +
    + +
    ))) .add('with custom month navigation icons', withInfo()(() => ( ))) .add('with no nav prev button', withInfo()(() => ( - +
    + +
    ))) .add('with no nav next button', withInfo()(() => ( - +
    + +
    ))); diff --git a/test/components/DayPickerNavigation_spec.jsx b/test/components/DayPickerNavigation_spec.jsx index a0e67cdfd9..f15f358c75 100644 --- a/test/components/DayPickerNavigation_spec.jsx +++ b/test/components/DayPickerNavigation_spec.jsx @@ -24,6 +24,13 @@ describe('DayPickerNavigation', () => { expect(wrapper.find('[role="button"]')).to.have.lengthOf(1); }); + it('is null when showNavNextButton and showNavPrevButton are both false', () => { + const wrapper = shallow( + , + ).dive(); + expect(wrapper.type()).to.equal(null); + }); + it('tabindex is present when the default buttons are used', () => { const wrapper = shallow().dive(); const prevMonthButton = wrapper.find('[role="button"]').at(0); From 94c2397909e9956ef77d522803e7720b66c777d0 Mon Sep 17 00:00:00 2001 From: Nicole Kinser Date: Tue, 14 Jan 2020 12:26:29 -0800 Subject: [PATCH 552/618] make class component so that it is valid to return null for versions of react below 15 --- src/components/DayPickerNavigation.jsx | 414 +++++++++++++------------ 1 file changed, 210 insertions(+), 204 deletions(-) diff --git a/src/components/DayPickerNavigation.jsx b/src/components/DayPickerNavigation.jsx index 56cd811ee9..0cbb1ce13e 100644 --- a/src/components/DayPickerNavigation.jsx +++ b/src/components/DayPickerNavigation.jsx @@ -66,220 +66,226 @@ const defaultProps = { showNavNextButton: true, }; -function DayPickerNavigation({ - inlineStyles, - isRTL, - disablePrev, - disableNext, - navPosition, - navPrev, - navNext, - onPrevMonthClick, - onNextMonthClick, - orientation, - phrases, - renderNavPrevButton, - renderNavNextButton, - showNavPrevButton, - showNavNextButton, - styles, -}) { - if (!showNavNextButton && !showNavPrevButton) { - return null; - } +class DayPickerNavigation extends React.PureComponent { + render() { + const { + inlineStyles, + isRTL, + disablePrev, + disableNext, + navPosition, + navPrev, + navNext, + onPrevMonthClick, + onNextMonthClick, + orientation, + phrases, + renderNavPrevButton, + renderNavNextButton, + showNavPrevButton, + showNavNextButton, + styles, + } = this.props; + + if (!showNavNextButton && !showNavPrevButton) { + return null; + } - const isHorizontal = orientation === HORIZONTAL_ORIENTATION; - const isVertical = orientation !== HORIZONTAL_ORIENTATION; - const isVerticalScrollable = orientation === VERTICAL_SCROLLABLE; - const isBottomNavPosition = navPosition === NAV_POSITION_BOTTOM; - const hasInlineStyles = !!inlineStyles; - - let navPrevIcon = navPrev; - let navNextIcon = navNext; - let isDefaultNavPrev = false; - let isDefaultNavNext = false; - let navPrevTabIndex = {}; - let navNextTabIndex = {}; - - if (!navPrevIcon) { - navPrevTabIndex = { tabIndex: '0' }; - isDefaultNavPrev = true; - let Icon = isVertical ? ChevronUp : LeftArrow; - if (isRTL && !isVertical) { - Icon = RightArrow; + const isHorizontal = orientation === HORIZONTAL_ORIENTATION; + const isVertical = orientation !== HORIZONTAL_ORIENTATION; + const isVerticalScrollable = orientation === VERTICAL_SCROLLABLE; + const isBottomNavPosition = navPosition === NAV_POSITION_BOTTOM; + const hasInlineStyles = !!inlineStyles; + + let navPrevIcon = navPrev; + let navNextIcon = navNext; + let isDefaultNavPrev = false; + let isDefaultNavNext = false; + let navPrevTabIndex = {}; + let navNextTabIndex = {}; + + if (!navPrevIcon) { + navPrevTabIndex = { tabIndex: '0' }; + isDefaultNavPrev = true; + let Icon = isVertical ? ChevronUp : LeftArrow; + if (isRTL && !isVertical) { + Icon = RightArrow; + } + navPrevIcon = ( + + ); } - navPrevIcon = ( - - ); - } - if (!navNextIcon) { - navNextTabIndex = { tabIndex: '0' }; - isDefaultNavNext = true; - let Icon = isVertical ? ChevronDown : RightArrow; - if (isRTL && !isVertical) { - Icon = LeftArrow; + if (!navNextIcon) { + navNextTabIndex = { tabIndex: '0' }; + isDefaultNavNext = true; + let Icon = isVertical ? ChevronDown : RightArrow; + if (isRTL && !isVertical) { + Icon = LeftArrow; + } + navNextIcon = ( + + ); } - navNextIcon = ( - - ); - } - - const isDefaultNav = isDefaultNavNext || isDefaultNavPrev; - - return ( -
    - {showNavPrevButton - && (renderNavPrevButton ? ( - renderNavPrevButton({ - ariaLabel: phrases.jumpToPrevMonth, - disabled: disablePrev, - onClick: disablePrev ? undefined : onPrevMonthClick, - onKeyUp: disablePrev ? undefined : (e) => { - const { key } = e; - if (key === 'Enter' || key === ' ') { - onPrevMonthClick(e); - } - }, - onMouseUp: disablePrev ? undefined : (e) => { - e.currentTarget.blur(); - }, - }) - ) : ( -
    + {showNavPrevButton + && (renderNavPrevButton ? ( + renderNavPrevButton({ + ariaLabel: phrases.jumpToPrevMonth, + disabled: disablePrev, + onClick: disablePrev ? undefined : onPrevMonthClick, + onKeyUp: disablePrev ? undefined : (e) => { + const { key } = e; + if (key === 'Enter' || key === ' ') { + onPrevMonthClick(e); + } + }, + onMouseUp: disablePrev ? undefined : (e) => { + e.currentTarget.blur(); + }, + }) + ) : ( +
    { - const { key } = e; - if (key === 'Enter' || key === ' ') { - onPrevMonthClick(e); - } - }} - onMouseUp={disablePrev ? undefined : (e) => { - e.currentTarget.blur(); - }} - > - {navPrevIcon} -
    - ))} - - {showNavNextButton - && (renderNavNextButton ? ( - renderNavNextButton({ - ariaLabel: phrases.jumpToNextMonth, - disabled: disableNext, - onClick: disableNext ? undefined : onNextMonthClick, - onKeyUp: disableNext ? undefined : (e) => { - const { key } = e; - if (key === 'Enter' || key === ' ') { - onNextMonthClick(e); - } - }, - onMouseUp: disableNext ? undefined : (e) => { - e.currentTarget.blur(); - }, - }) - ) : ( -
    { + const { key } = e; + if (key === 'Enter' || key === ' ') { + onPrevMonthClick(e); + } + }} + onMouseUp={disablePrev ? undefined : (e) => { + e.currentTarget.blur(); + }} + > + {navPrevIcon} +
    + ))} + + {showNavNextButton + && (renderNavNextButton ? ( + renderNavNextButton({ + ariaLabel: phrases.jumpToNextMonth, + disabled: disableNext, + onClick: disableNext ? undefined : onNextMonthClick, + onKeyUp: disableNext ? undefined : (e) => { + const { key } = e; + if (key === 'Enter' || key === ' ') { + onNextMonthClick(e); + } + }, + onMouseUp: disableNext ? undefined : (e) => { + e.currentTarget.blur(); + }, + }) + ) : ( +
    { - const { key } = e; - if (key === 'Enter' || key === ' ') { - onNextMonthClick(e); - } - }} - onMouseUp={disableNext ? undefined : (e) => { - e.currentTarget.blur(); - }} - > - {navNextIcon} -
    - ))} -
    - ); + )} + aria-disabled={disableNext ? true : undefined} + aria-label={phrases.jumpToNextMonth} + onClick={disableNext ? undefined : onNextMonthClick} + onKeyUp={disableNext ? undefined : (e) => { + const { key } = e; + if (key === 'Enter' || key === ' ') { + onNextMonthClick(e); + } + }} + onMouseUp={disableNext ? undefined : (e) => { + e.currentTarget.blur(); + }} + > + {navNextIcon} +
    + ))} + + ); + } } DayPickerNavigation.propTypes = propTypes; From c9a3c6f00fbd7ee8b8f9889888b3efa0843fa1db Mon Sep 17 00:00:00 2001 From: Nicole Kinser Date: Mon, 13 Jan 2020 17:24:40 -0800 Subject: [PATCH 553/618] Fix logic for apply default navigation button styling --- src/components/DayPickerNavigation.jsx | 4 +- stories/DayPickerRangeController.js | 62 ++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/components/DayPickerNavigation.jsx b/src/components/DayPickerNavigation.jsx index 0cbb1ce13e..df1f28b30c 100644 --- a/src/components/DayPickerNavigation.jsx +++ b/src/components/DayPickerNavigation.jsx @@ -104,7 +104,7 @@ class DayPickerNavigation extends React.PureComponent { let navPrevTabIndex = {}; let navNextTabIndex = {}; - if (!navPrevIcon) { + if (!navPrevIcon && !renderNavPrevButton) { navPrevTabIndex = { tabIndex: '0' }; isDefaultNavPrev = true; let Icon = isVertical ? ChevronUp : LeftArrow; @@ -122,7 +122,7 @@ class DayPickerNavigation extends React.PureComponent { ); } - if (!navNextIcon) { + if (!navNextIcon && !renderNavNextButton) { navNextTabIndex = { tabIndex: '0' }; isDefaultNavNext = true; let Icon = isVertical ? ChevronDown : RightArrow; diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index e89e2d77c4..7d5f2def3c 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -157,6 +157,54 @@ function renderNavNextButton(buttonProps) { ); } +function renderNavPrevButtonForVerticalScrollable(buttonProps) { + const { + ariaLabel, + disabled, + onClick, + onKeyUp, + onMouseUp, + } = buttonProps; + + return ( + + ); +} + +function renderNavNextButtonForVerticalScrollable(buttonProps) { + const { + ariaLabel, + disabled, + onClick, + onKeyUp, + onMouseUp, + } = buttonProps; + + return ( + + ); +} + function renderKeyboardShortcutsButton(buttonProps) { const { ref, onClick, ariaLabel } = buttonProps; @@ -462,6 +510,20 @@ storiesOf('DayPickerRangeController', module) /> ))) + .add('vertical scrollable with custom rendered month navigation', withInfo()(() => ( +
    + +
    + ))) .add('with custom month navigation icons', withInfo()(() => ( Date: Tue, 14 Jan 2020 12:15:38 -0800 Subject: [PATCH 554/618] regression tests --- src/components/DayPickerNavigation.jsx | 4 +- stories/DayPickerRangeController.js | 4 +- test/components/DayPickerNavigation_spec.jsx | 62 ++++++++++++++++++++ 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/components/DayPickerNavigation.jsx b/src/components/DayPickerNavigation.jsx index df1f28b30c..f7fb91f65b 100644 --- a/src/components/DayPickerNavigation.jsx +++ b/src/components/DayPickerNavigation.jsx @@ -104,7 +104,7 @@ class DayPickerNavigation extends React.PureComponent { let navPrevTabIndex = {}; let navNextTabIndex = {}; - if (!navPrevIcon && !renderNavPrevButton) { + if (!navPrevIcon && !renderNavPrevButton && showNavPrevButton) { navPrevTabIndex = { tabIndex: '0' }; isDefaultNavPrev = true; let Icon = isVertical ? ChevronUp : LeftArrow; @@ -122,7 +122,7 @@ class DayPickerNavigation extends React.PureComponent { ); } - if (!navNextIcon && !renderNavNextButton) { + if (!navNextIcon && !renderNavNextButton && showNavNextButton) { navNextTabIndex = { tabIndex: '0' }; isDefaultNavNext = true; let Icon = isVertical ? ChevronDown : RightArrow; diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index 7d5f2def3c..80f8497029 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -152,7 +152,7 @@ function renderNavNextButton(buttonProps) { style={{ position: 'absolute', top: 23, right: 22 }} type="button" > - Next › + Next ); } @@ -176,7 +176,7 @@ function renderNavPrevButtonForVerticalScrollable(buttonProps) { style={{ width: '100%', textAlign: 'center' }} type="button" > - ‹ Prev + Prev ); } diff --git a/test/components/DayPickerNavigation_spec.jsx b/test/components/DayPickerNavigation_spec.jsx index f15f358c75..489b0754ab 100644 --- a/test/components/DayPickerNavigation_spec.jsx +++ b/test/components/DayPickerNavigation_spec.jsx @@ -6,6 +6,7 @@ import { shallow } from 'enzyme'; import DayPickerNavigation from '../../src/components/DayPickerNavigation'; import RightArrow from '../../src/components/RightArrow'; import LeftArrow from '../../src/components/LeftArrow'; +import { VERTICAL_ORIENTATION } from '../../src/constants'; describe('DayPickerNavigation', () => { describe('#render', () => { @@ -91,6 +92,67 @@ describe('DayPickerNavigation', () => { expect(wrapper.childAt(1).find('div[role="button"]')).to.have.lengthOf(0); expect(renderNavNextButtonStub).to.have.property('callCount', 1); }); + + it('does not render default styles when custom navigation is used', () => { + const renderNavPrevButtonStub = sinon.stub().returns(); + const renderNavNextButtonStub = sinon.stub().returns(); + const wrapper = shallow( + , + ).dive(); + const wrapperDiv = wrapper.find('div').filterWhere((div) => { + const className = div.prop('className') || ''; + return className.includes('DayPickerNavigation__verticalDefault'); + }); + expect(wrapperDiv).to.have.lengthOf(0); + }); + + it('does render default styles when custom navigation is used for only one nav button', () => { + const renderNavPrevButtonStub = sinon.stub().returns(); + const wrapper = shallow( + , + ).dive(); + const wrapperDiv = wrapper.find('div').filterWhere((div) => { + const className = div.prop('className') || ''; + return className.includes('DayPickerNavigation__verticalDefault'); + }); + expect(wrapperDiv).to.have.lengthOf(1); + }); + + it('does not render default styles when custom navigation is used for only button but the other nav button is not shown', () => { + const renderNavPrevButtonStub = sinon.stub().returns(); + const wrapper = shallow( + , + ).dive(); + const wrapperDiv = wrapper.find('div').filterWhere((div) => { + const className = div.prop('className') || ''; + return className.includes('DayPickerNavigation__verticalDefault'); + }); + expect(wrapperDiv).to.have.lengthOf(0); + }); + + it('renders default styles when default navigation is used', () => { + const wrapper = shallow( + , + ).dive(); + const wrapperDiv = wrapper.find('div').filterWhere((div) => { + const className = div.prop('className') || ''; + return className.includes('DayPickerNavigation__verticalDefault'); + }); + expect(wrapperDiv).to.have.lengthOf(1); + }); }); describe('interactions', () => { From 399ba3c6b20693d9979b0ab2bfbdb619fd94acb8 Mon Sep 17 00:00:00 2001 From: Nicole Kinser Date: Tue, 14 Jan 2020 16:27:47 -0800 Subject: [PATCH 555/618] Keep scroll position when prev months rendered on vertical scrollable calendar --- src/components/DayPicker.jsx | 63 ++++++++++++++++++++++++------ test/components/DayPicker_spec.jsx | 57 +++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 11 deletions(-) diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index b0434d8c53..a31a710f10 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -25,6 +25,7 @@ import getCalendarMonthWidth from '../utils/getCalendarMonthWidth'; import calculateDimension from '../utils/calculateDimension'; import getActiveElement from '../utils/getActiveElement'; import isDayVisible from '../utils/isDayVisible'; +import isSameMonth from '../utils/isSameMonth'; import ModifiersShape from '../shapes/ModifiersShape'; import NavPositionShape from '../shapes/NavPositionShape'; @@ -215,6 +216,7 @@ class DayPicker extends React.PureComponent { this.hasSetInitialVisibleMonth = !props.hidden; this.state = { + currentMonthScrollTop: null, currentMonth, monthTransition: null, translationValue, @@ -260,29 +262,37 @@ class DayPicker extends React.PureComponent { } componentDidMount() { + const { orientation } = this.props; const { currentMonth } = this.state; - if (this.calendarInfo) { - this.setState({ - isTouchDevice: isTouchDevice(), - calendarInfoWidth: calculateDimension(this.calendarInfo, 'width', true, true), - }); - } else { - this.setState({ isTouchDevice: isTouchDevice() }); - } + + const calendarInfoWidth = this.calendarInfo + ? calculateDimension(this.calendarInfo, 'width', true, true) + : 0; + const currentMonthScrollTop = this.transitionContainer && orientation === VERTICAL_SCROLLABLE + ? this.transitionContainer.scrollHeight - this.transitionContainer.scrollTop + : null; + + this.setState({ + isTouchDevice: isTouchDevice(), + calendarInfoWidth, + currentMonthScrollTop, + }); this.setCalendarMonthWeeks(currentMonth); } - componentWillReceiveProps(nextProps) { + componentWillReceiveProps(nextProps, nextState) { const { hidden, isFocused, showKeyboardShortcuts, onBlur, + orientation, renderMonthText, horizontalMonthPadding, } = nextProps; const { currentMonth } = this.state; + const { currentMonth: nextCurrentMonth } = nextState; if (!hidden) { if (!this.hasSetInitialVisibleMonth) { @@ -334,6 +344,20 @@ class DayPicker extends React.PureComponent { monthTitleHeight: null, }); } + + // Capture the scroll position so when previous months are rendered above the current month + // we can adjust scroll after the component has updated and the previous current month + // stays in view. + if ( + orientation === VERTICAL_SCROLLABLE + && this.transitionContainer + && !isSameMonth(currentMonth, nextCurrentMonth) + ) { + this.setState({ + currentMonthScrollTop: + this.transitionContainer.scrollHeight - this.transitionContainer.scrollTop, + }); + } } componentWillUpdate() { @@ -355,11 +379,16 @@ class DayPicker extends React.PureComponent { } } - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps, prevState) { const { orientation, daySize, isFocused, numberOfMonths, } = this.props; - const { focusedDate, monthTitleHeight } = this.state; + const { + currentMonth, + currentMonthScrollTop, + focusedDate, + monthTitleHeight, + } = this.state; if ( this.isHorizontal() @@ -374,6 +403,18 @@ class DayPicker extends React.PureComponent { if (!prevProps.isFocused && isFocused && !focusedDate) { this.container.focus(); } + + // If orientation is VERTICAL_SCROLLABLE and currentMonth has changed adjust scrollTop so the + // new months rendered above the current month don't push the current month out of view. + if ( + orientation === VERTICAL_SCROLLABLE + && !isSameMonth(prevState.currentMonth, currentMonth) + && currentMonthScrollTop + && this.transitionContainer + ) { + this.transitionContainer.scrollTop = this.transitionContainer.scrollHeight + - currentMonthScrollTop; + } } componentWillUnmount() { diff --git a/test/components/DayPicker_spec.jsx b/test/components/DayPicker_spec.jsx index 8b20aab508..4a8f119c9d 100644 --- a/test/components/DayPicker_spec.jsx +++ b/test/components/DayPicker_spec.jsx @@ -906,6 +906,12 @@ describe('DayPicker', () => { mount(); expect(adjustDayPickerHeightSpy).to.have.property('callCount', 1); }); + + it('does not update state.currentMonthScrollTop', () => { + sinon.spy(DayPicker.prototype, 'setTransitionContainerRef'); + const wrapper = mount(); + expect(wrapper.state().currentMonthScrollTop).to.equal(null); + }); }); describe('props.orientation === VERTICAL_ORIENTATION', () => { @@ -913,6 +919,35 @@ describe('DayPicker', () => { mount(); expect(adjustDayPickerHeightSpy.called).to.equal(false); }); + + it('does not update state.currentMonthScrollTop', () => { + sinon.spy(DayPicker.prototype, 'setTransitionContainerRef'); + const wrapper = mount(); + expect(wrapper.state().currentMonthScrollTop).to.equal(null); + }); + }); + + describe('props.orientation === VERTICAL_SCROLLABLE', () => { + it('updates state.currentMonthScrollTop', () => { + sinon.spy(DayPicker.prototype, 'setTransitionContainerRef'); + const wrapper = mount(); + expect(wrapper.state().currentMonthScrollTop).to.not.equal(null); + }); + }); + }); + + describe('#componentWillReceiveProps', () => { + describe('props.orientation === VERTICAL_SCROLLABLE', () => { + it('updates state.currentMonthScrollTop', () => { + sinon.spy(DayPicker.prototype, 'setTransitionContainerRef'); + const wrapper = mount(); + const prevCurrentMonthScrollTop = wrapper.state().currentMonthScrollTop; + wrapper.setState({ + currentMonth: moment().subtract(1, 'months'), + }); + wrapper.setProps({ initialVisibleMonth: () => moment().subtract(1, 'month') }); + expect(wrapper.state().currentMonthScrollTop).to.not.equal(prevCurrentMonthScrollTop); + }); }); }); @@ -1027,6 +1062,28 @@ describe('DayPicker', () => { }); }); + describe('props.orientation === VERTICAL_SCROLLABLE', () => { + it('does not update transitionContainer ref`s scrollTop currentMonth stays the same', () => { + sinon.spy(DayPicker.prototype, 'setTransitionContainerRef'); + const wrapper = mount(); + const prevScrollTop = wrapper.transitionContainer.scrollTop; + wrapper.setState({ + currentMonth: moment(), + }); + expect(wrapper.transitionContainer).to.have.property('scrollTop', prevScrollTop); + }); + + it('updates transitionContainer ref`s scrollTop currentMonth changes', () => { + sinon.spy(DayPicker.prototype, 'setTransitionContainerRef'); + const wrapper = mount(); + const prevScrollTop = wrapper.transitionContainer.scrollTop; + wrapper.setState({ + currentMonth: moment().subtract(1, 'months'), + }); + expect(wrapper.transitionContainer).to.not.have.property('scrollTop', prevScrollTop); + }); + }); + describe('when isFocused is updated to true', () => { const prevProps = { isFocused: false }; const newProps = { isFocused: true }; From c644b5c0b66a57dc2b1287b9bf188578e1475fac Mon Sep 17 00:00:00 2001 From: Kevin Pan Date: Wed, 15 Jan 2020 12:46:04 -0800 Subject: [PATCH 556/618] Version 21.7.0 --- CHANGELOG.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ab70db682..433ab3c5ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ + +## 21.7.0 +- [fix] Keep scroll position when prev months rendered on vertical scrollable calendar ([#1902](https://github.com/airbnb/react-dates/pull/1902)) +- [new] Add ability to not show prev or next navigation buttons ([#1900](https://github.com/airbnb/react-dates/pull/1900)) +- [fix] Fix logic for applying default navigation button styling ([#1898](https://github.com/airbnb/react-dates/pull/1898)) + ## 21.6.0 - [new] Add functionality to see previous months for vertical scrollable calendar ([#1894](https://github.com/airbnb/react-dates/pull/1894)) diff --git a/package.json b/package.json index 7f0e43f074..7ae237d97e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dates", - "version": "21.6.0", + "version": "21.7.0", "description": "A responsive and accessible date range picker component built with React", "main": "index.js", "scripts": { From fe3bdbc5292d6921f474ad41a05ac6131aec3d17 Mon Sep 17 00:00:00 2001 From: Kevin Pan Date: Tue, 28 Jan 2020 11:34:59 -0800 Subject: [PATCH 557/618] fix conditional --- src/components/DayPickerKeyboardShortcuts.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DayPickerKeyboardShortcuts.jsx b/src/components/DayPickerKeyboardShortcuts.jsx index 2861d3eda5..7e9b70014a 100644 --- a/src/components/DayPickerKeyboardShortcuts.jsx +++ b/src/components/DayPickerKeyboardShortcuts.jsx @@ -189,7 +189,7 @@ class DayPickerKeyboardShortcuts extends React.PureComponent { onClick: this.onShowKeyboardShortcutsButtonClick, ariaLabel: toggleButtonText, })} - {renderKeyboardShortcutsButton || ( + {!renderKeyboardShortcutsButton && ( )} diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index 275736d1c9..656dc6566f 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -569,6 +569,7 @@ class SingleDatePicker extends React.PureComponent { keepOpenOnDateSelect, styles, isOutsideRange, + isDayBlocked, } = this.props; const { isInputFocused } = this.state; @@ -593,6 +594,7 @@ class SingleDatePicker extends React.PureComponent { showDefaultInputIcon={showDefaultInputIcon} inputIconPosition={inputIconPosition} isOutsideRange={isOutsideRange} + isDayBlocked={isDayBlocked} customCloseIcon={customCloseIcon} customInputIcon={customInputIcon} date={date} diff --git a/src/components/SingleDatePickerInputController.jsx b/src/components/SingleDatePickerInputController.jsx index 2da008ee16..ac65adcd54 100644 --- a/src/components/SingleDatePickerInputController.jsx +++ b/src/components/SingleDatePickerInputController.jsx @@ -54,6 +54,7 @@ const propTypes = forbidExtraProps({ keepOpenOnDateSelect: PropTypes.bool, reopenPickerOnClearDate: PropTypes.bool, isOutsideRange: PropTypes.func, + isDayBlocked: PropTypes.func, displayFormat: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), onClose: PropTypes.func, @@ -98,6 +99,7 @@ const defaultProps = { keepOpenOnDateSelect: false, reopenPickerOnClearDate: false, isOutsideRange: (day) => !isInclusivelyAfterDay(day, moment()), + isDayBlocked: () => false, displayFormat: () => moment.localeData().longDateFormat('L'), onClose() {}, @@ -129,6 +131,7 @@ export default class SingleDatePickerInputController extends React.PureComponent onChange(dateString) { const { isOutsideRange, + isDayBlocked, keepOpenOnDateSelect, onDateChange, onFocusChange, @@ -136,7 +139,7 @@ export default class SingleDatePickerInputController extends React.PureComponent } = this.props; const newDate = toMomentObject(dateString, this.getDisplayFormat()); - const isValid = newDate && !isOutsideRange(newDate); + const isValid = newDate && !isOutsideRange(newDate) && !isDayBlocked(newDate); if (isValid) { onDateChange(newDate); if (!keepOpenOnDateSelect) { diff --git a/test/components/DateRangePickerInputController_spec.jsx b/test/components/DateRangePickerInputController_spec.jsx index 779450d8ec..a9b6566912 100644 --- a/test/components/DateRangePickerInputController_spec.jsx +++ b/test/components/DateRangePickerInputController_spec.jsx @@ -432,6 +432,37 @@ describe('DateRangePickerInputController', () => { }); }); + describe('is blocked', () => { + const futureDate = moment().add(7, 'days').format('DD/MM/YYYY'); + const isDayBlocked = sinon.stub().returns(true); + + it('calls props.onDatesChange', () => { + const onDatesChangeStub = sinon.stub(); + const wrapper = shallow(( + + )); + wrapper.instance().onEndDateChange(futureDate); + expect(onDatesChangeStub.callCount).to.equal(1); + }); + + it('calls props.onDatesChange with endDate === null', () => { + const onDatesChangeStub = sinon.stub(); + const wrapper = shallow(( + + )); + wrapper.instance().onEndDateChange(futureDate); + const args = onDatesChangeStub.getCall(0).args[0]; + expect(args.endDate).to.equal(null); + }); + }); + describe('is inclusively before state.startDate', () => { const startDate = moment(today).add(10, 'days'); const beforeStartDateString = today.toISOString(); @@ -720,7 +751,7 @@ describe('DateRangePickerInputController', () => { }); describe('is outside range', () => { - const futureDate = moment().add(7, 'days').toISOString(); + const futureDate = moment().add(7, 'days').format('DD/MM/YYYY'); const isOutsideRange = (day) => day > moment().add(5, 'days'); it('calls props.onDatesChange', () => { @@ -763,6 +794,37 @@ describe('DateRangePickerInputController', () => { expect(args.endDate).to.equal(today); }); }); + + describe('is blocked', () => { + const futureDate = moment().add(7, 'days').format('DD/MM/YYYY'); + const isDayBlocked = sinon.stub().returns(true); + + it('calls props.onDatesChange', () => { + const onDatesChangeStub = sinon.stub(); + const wrapper = shallow(( + + )); + wrapper.instance().onStartDateChange(futureDate); + expect(onDatesChangeStub.callCount).to.equal(1); + }); + + it('calls props.onDatesChange with startDate === null', () => { + const onDatesChangeStub = sinon.stub(); + const wrapper = shallow(( + + )); + wrapper.instance().onStartDateChange(futureDate); + const args = onDatesChangeStub.getCall(0).args[0]; + expect(args.startDate).to.equal(null); + }); + }); }); describe('#onStartDateFocus', () => { diff --git a/test/components/DateRangePicker_spec.jsx b/test/components/DateRangePicker_spec.jsx index deadee3453..e08f19c0f9 100644 --- a/test/components/DateRangePicker_spec.jsx +++ b/test/components/DateRangePicker_spec.jsx @@ -110,6 +110,16 @@ describe('DateRangePicker', () => { }); }); + describe('props.isDayBlocked is defined', () => { + it('should pass props.isDayBlocked to ', () => { + const isDayBlocked = sinon.stub(); + const wrapper = shallow(( + + )).dive(); + expect(wrapper.find(DateRangePickerInputController).prop('isDayBlocked')).to.equal(isDayBlocked); + }); + }); + describe('props.appendToBody', () => { it('renders inside ', () => { const wrapper = shallow(( diff --git a/test/components/SingleDatePickerInputController_spec.jsx b/test/components/SingleDatePickerInputController_spec.jsx index af304dc712..4e6407934c 100644 --- a/test/components/SingleDatePickerInputController_spec.jsx +++ b/test/components/SingleDatePickerInputController_spec.jsx @@ -203,7 +203,7 @@ describe('SingleDatePickerInputController', () => { describe('date string outside range', () => { const isOutsideRangeStub = sinon.stub().returns(true); - const todayDateString = today.toISOString(); + const todayDateString = today.format('DD/MM/YYYY'); it('calls props.onDateChange once', () => { const onDateChangeStub = sinon.stub(); @@ -247,6 +247,39 @@ describe('SingleDatePickerInputController', () => { expect(onFocusChangeStub.callCount).to.equal(0); }); }); + + describe('date string is blocked', () => { + const isDayBlocked = sinon.stub().returns(true); + const todayDateString = today.format('DD/MM/YYYY'); + + it('calls props.onDateChange once', () => { + const onDateChangeStub = sinon.stub(); + const wrapper = shallow(( + {}} + isDayBlocked={isDayBlocked} + /> + )); + wrapper.instance().onChange(todayDateString); + expect(onDateChangeStub.callCount).to.equal(1); + }); + + it('calls props.onDateChange with null as arg', () => { + const onDateChangeStub = sinon.stub(); + const wrapper = shallow(( + {}} + isDayBlocked={isDayBlocked} + /> + )); + wrapper.instance().onChange(todayDateString); + expect(onDateChangeStub.getCall(0).args[0]).to.equal(null); + }); + }); }); describe('#onFocus', () => { diff --git a/test/components/SingleDatePicker_spec.jsx b/test/components/SingleDatePicker_spec.jsx index 3b05e5e421..bd2ddff8e0 100644 --- a/test/components/SingleDatePicker_spec.jsx +++ b/test/components/SingleDatePicker_spec.jsx @@ -56,6 +56,21 @@ describe('SingleDatePicker', () => { expect(wrapper.find(SingleDatePickerInputController).prop('isOutsideRange')).to.equal(isOutsideRange); }); }); + + describe('props.isDayBlocked is defined', () => { + it('should pass props.isDayBlocked to ', () => { + const isDayBlocked = sinon.stub(); + const wrapper = shallow(( + {}} + onFocusChange={() => {}} + isDayBlocked={isDayBlocked} + /> + )).dive(); + expect(wrapper.find(SingleDatePickerInputController).prop('isDayBlocked')).to.equal(isDayBlocked); + }); + }); }); describe('DayPickerSingleDateController', () => { From 927fb93091f84393301b857bdb8b9d7c4aa00081 Mon Sep 17 00:00:00 2001 From: mmarkelov Date: Thu, 18 Jul 2019 10:09:03 +0300 Subject: [PATCH 565/618] [New] Add minDate and maxDate support to SingleDatePicker, DayPickerSingleDateController Co-authored-by: mmarkelov Co-authored-by: Stephen Fixes #1632. Closes #1727. --- .../DayPickerSingleDateController.jsx | 48 +++++++++++++- src/components/SingleDatePicker.jsx | 6 ++ src/shapes/SingleDatePickerShape.js | 2 + stories/SingleDatePicker.js | 19 ++++++ .../DayPickerSingleDateController_spec.jsx | 66 +++++++++++++++++++ 5 files changed, 138 insertions(+), 3 deletions(-) diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 480132891b..89be9f6ed8 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -11,6 +11,7 @@ import getPhrasePropTypes from '../utils/getPhrasePropTypes'; import isSameDay from '../utils/isSameDay'; import isAfterDay from '../utils/isAfterDay'; +import isDayVisible from '../utils/isDayVisible'; import getVisibleDays from '../utils/getVisibleDays'; @@ -35,6 +36,8 @@ import getPooledMoment from '../utils/getPooledMoment'; const propTypes = forbidExtraProps({ date: momentPropTypes.momentObj, + minDate: momentPropTypes.momentObj, + maxDate: momentPropTypes.momentObj, onDateChange: PropTypes.func, focused: PropTypes.bool, @@ -100,6 +103,8 @@ const propTypes = forbidExtraProps({ const defaultProps = { date: undefined, // TODO: use null + minDate: null, + maxDate: null, onDateChange() {}, focused: false, @@ -190,6 +195,8 @@ export default class DayPickerSingleDateController extends React.PureComponent { hoverDate: null, currentMonth, visibleDays, + disablePrev: this.shouldDisableMonthNavigation(props.minDate, currentMonth), + disableNext: this.shouldDisableMonthNavigation(props.maxDate, currentMonth), }; this.onDayMouseEnter = this.onDayMouseEnter.bind(this); @@ -390,7 +397,13 @@ export default class DayPickerSingleDateController extends React.PureComponent { } onPrevMonthClick() { - const { onPrevMonthClick, numberOfMonths, enableOutsideDays } = this.props; + const { + enableOutsideDays, + maxDate, + minDate, + numberOfMonths, + onPrevMonthClick, + } = this.props; const { currentMonth, visibleDays } = this.state; const newVisibleDays = {}; @@ -400,9 +413,12 @@ export default class DayPickerSingleDateController extends React.PureComponent { const prevMonth = currentMonth.clone().subtract(1, 'month'); const prevMonthVisibleDays = getVisibleDays(prevMonth, 1, enableOutsideDays); + const newCurrentMonth = currentMonth.clone().subtract(1, 'month'); this.setState({ currentMonth: prevMonth, + disablePrev: this.shouldDisableMonthNavigation(minDate, newCurrentMonth), + disableNext: this.shouldDisableMonthNavigation(maxDate, newCurrentMonth), visibleDays: { ...newVisibleDays, ...this.getModifiers(prevMonthVisibleDays), @@ -413,7 +429,13 @@ export default class DayPickerSingleDateController extends React.PureComponent { } onNextMonthClick() { - const { onNextMonthClick, numberOfMonths, enableOutsideDays } = this.props; + const { + enableOutsideDays, + maxDate, + minDate, + numberOfMonths, + onNextMonthClick, + } = this.props; const { currentMonth, visibleDays } = this.state; const newVisibleDays = {}; @@ -427,6 +449,8 @@ export default class DayPickerSingleDateController extends React.PureComponent { const newCurrentMonth = currentMonth.clone().add(1, 'month'); this.setState({ currentMonth: newCurrentMonth, + disablePrev: this.shouldDisableMonthNavigation(minDate, newCurrentMonth), + disableNext: this.shouldDisableMonthNavigation(maxDate, newCurrentMonth), visibleDays: { ...newVisibleDays, ...this.getModifiers(nextMonthVisibleDays), @@ -564,6 +588,17 @@ export default class DayPickerSingleDateController extends React.PureComponent { return { currentMonth, visibleDays }; } + shouldDisableMonthNavigation(date, visibleMonth) { + if (!date) return false; + + const { + numberOfMonths, + enableOutsideDays, + } = this.props; + + return isDayVisible(date, visibleMonth, numberOfMonths, enableOutsideDays); + } + addModifier(updatedDays, day, modifier) { return addModifier(updatedDays, day, modifier, this.props, this.state); } @@ -645,7 +680,12 @@ export default class DayPickerSingleDateController extends React.PureComponent { horizontalMonthPadding, } = this.props; - const { currentMonth, visibleDays } = this.state; + const { + currentMonth, + disableNext, + disablePrev, + visibleDays, + } = this.state; return ( ))) + .add('with custom month navigation and blocked navigation (minDate and maxDate)', withInfo()(() => ( + + ))) + .add('with custom isOutsideRange and month navigation and blocked navigation (minDate and maxDate)', withInfo()(() => { + const minDate = moment().subtract(2, 'months').startOf('month') + const maxDate = moment().add(2, 'months').endOf('month') + const isOutsideRange = day => isInclusivelyBeforeDay(day, minDate) || isInclusivelyAfterDay(day, maxDate) + return ( + + )})) .add('vertical with custom height', withInfo()(() => ( { expect(onPrevMonthClickStub.firstCall.args[0].year()).to.equal(newMonth.year()); expect(onPrevMonthClickStub.firstCall.args[0].month()).to.equal(newMonth.month()); }); + + it('calls this.shouldDisableMonthNavigation twice', () => { + const shouldDisableMonthNavigationSpy = sinon.spy(DayPickerSingleDateController.prototype, 'shouldDisableMonthNavigation'); + const wrapper = shallow(( + + )); + shouldDisableMonthNavigationSpy.resetHistory(); + wrapper.instance().onPrevMonthClick(); + expect(shouldDisableMonthNavigationSpy).to.have.property('callCount', 2); + }); + + it('sets disablePrev and disablePrev as false on onPrevMonthClick call withouth maxDate and minDate set', () => { + const numberOfMonths = 2; + const wrapper = shallow(( + + )); + wrapper.setState({ + currentMonth: today, + }); + wrapper.instance().onPrevMonthClick(); + expect(wrapper.state()).to.have.property('disablePrev', false); + expect(wrapper.state()).to.have.property('disableNext', false); + }); + + it('sets disableNext as true when maxDate is in visible month', () => { + const numberOfMonths = 2; + const wrapper = shallow(( + + )); + wrapper.setState({ + currentMonth: today, + }); + wrapper.instance().onPrevMonthClick(); + expect(wrapper.state()).to.have.property('disablePrev', false); + expect(wrapper.state()).to.have.property('disableNext', true); + }); + + it('sets disablePrev as true when minDate is in visible month', () => { + const numberOfMonths = 2; + const wrapper = shallow(( + + )); + wrapper.setState({ + currentMonth: today, + }); + wrapper.instance().onPrevMonthClick(); + expect(wrapper.state()).to.have.property('disablePrev', true); + expect(wrapper.state()).to.have.property('disableNext', false); + }); }); describe('#onNextMonthClick', () => { From bc0e051478963817c66d7e437854a093362aec22 Mon Sep 17 00:00:00 2001 From: Ian MacIntosh Date: Fri, 6 Mar 2020 17:38:07 -0600 Subject: [PATCH 566/618] Update docs with an alternative installer command Since the primarily recommended command doesn't work on Windows, @ljharb provided a `npx` command that is platform-agnostic for contributors running npm 5+ Also made it more clear that the "primarily recommended command" should be run from the command line. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 524783951e..397366af28 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ To run that demo on your own computer: ## Getting Started ### Install dependencies -Ensure packages are installed with correct version numbers by running: +Ensure packages are installed with correct version numbers by running (from your command line): ```sh ( export PKG=react-dates; @@ -40,6 +40,8 @@ Ensure packages are installed with correct version numbers by running: npm install --save react-dates moment@>=#.## react@>=#.## react-dom@>=#.## ``` + > If you are running Windows, that command will not work, but if you are running npm 5 or higher, you can run `npx install-peerdeps react-dates` on any platform + ### Initialize ```js import 'react-dates/initialize'; From 0b1c7eee0aa151592695378251427134668f1e0c Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Wed, 15 Jul 2020 22:08:22 -0700 Subject: [PATCH 567/618] [eslint] fix linting errors --- src/components/CalendarMonth.jsx | 1 - src/components/CalendarMonthGrid.jsx | 1 - src/components/DateRangePickerInput.jsx | 1 - src/components/DayPickerRangeController.jsx | 1 - src/components/SingleDatePickerInputController.jsx | 1 - test/components/DateRangePickerInput_spec.jsx | 1 - test/components/DateRangePicker_spec.jsx | 1 - test/components/DayPickerRangeController_spec.jsx | 1 - test/components/DayPicker_spec.jsx | 2 -- test/components/SingleDatePicker_spec.jsx | 1 - 10 files changed, 11 deletions(-) diff --git a/src/components/CalendarMonth.jsx b/src/components/CalendarMonth.jsx index 226918b433..0c9002fbf2 100644 --- a/src/components/CalendarMonth.jsx +++ b/src/components/CalendarMonth.jsx @@ -22,7 +22,6 @@ import ModifiersShape from '../shapes/ModifiersShape'; import ScrollableOrientationShape from '../shapes/ScrollableOrientationShape'; import DayOfWeekShape from '../shapes/DayOfWeekShape'; - import { HORIZONTAL_ORIENTATION, VERTICAL_SCROLLABLE, diff --git a/src/components/CalendarMonthGrid.jsx b/src/components/CalendarMonthGrid.jsx index 7ade948e44..073d260458 100644 --- a/src/components/CalendarMonthGrid.jsx +++ b/src/components/CalendarMonthGrid.jsx @@ -23,7 +23,6 @@ import ModifiersShape from '../shapes/ModifiersShape'; import ScrollableOrientationShape from '../shapes/ScrollableOrientationShape'; import DayOfWeekShape from '../shapes/DayOfWeekShape'; - import { HORIZONTAL_ORIENTATION, VERTICAL_ORIENTATION, diff --git a/src/components/DateRangePickerInput.jsx b/src/components/DateRangePickerInput.jsx index 2389595852..aa4bae5cd0 100644 --- a/src/components/DateRangePickerInput.jsx +++ b/src/components/DateRangePickerInput.jsx @@ -281,7 +281,6 @@ function DateRangePickerInput({ regular={regular} /> - {showClearDates && ( + + ); + } +} + const requiredProps = { onDatesChange: () => {}, onFocusChange: () => {}, @@ -539,6 +583,23 @@ describe('DateRangePicker', () => { }); }); + describeIfWindow('day picker position', () => { + it('day picker is opened after the end date input when end date input is focused', () => { + const wrapper = mount(( + + )); + expect(wrapper.find(DayPicker)).to.have.length(0); + wrapper.find('input').at(0).simulate('focus'); // when focusing on start date the day picker is rendered after the start date input + expect(wrapper.find('DateRangePickerInput').children().childAt(1).find(DayPicker)).to.have.length(1); + wrapper.find('input').at(1).simulate('focus'); // when focusing on end date the day picker is rendered after the end date input + expect(wrapper.find('DateRangePickerInput').children().childAt(1).find(DayPicker)).to.have.length(0); + expect(wrapper.find('DateRangePickerInput').children().childAt(3).find(DayPicker)).to.have.length(1); + }); + }); + describeIfWindow('#onDayPickerBlur', () => { it('sets state.isDateRangePickerInputFocused to true', () => { const wrapper = shallow(( From b8e26f01933aa6513af6fd22a7525ff1cd30df06 Mon Sep 17 00:00:00 2001 From: Tanner Byers Date: Thu, 28 Oct 2021 15:29:24 -0400 Subject: [PATCH 592/618] [New] DateInput, {DateRange,SingleDate}Picker{,Input,InputController}: add autocomplete prop --- README.md | 2 ++ examples/DateRangePickerWrapper.jsx | 1 + examples/SingleDatePickerWrapper.jsx | 1 + src/components/DateInput.jsx | 5 ++++- src/components/DateRangePicker.jsx | 3 +++ src/components/DateRangePickerInput.jsx | 5 +++++ src/components/DateRangePickerInputController.jsx | 4 ++++ src/components/SingleDatePicker.jsx | 3 +++ src/components/SingleDatePickerInput.jsx | 4 ++++ src/components/SingleDatePickerInputController.jsx | 4 ++++ src/shapes/DateRangePickerShape.js | 1 + src/shapes/SingleDatePickerShape.js | 1 + stories/DateRangePicker.js | 3 +++ stories/SingleDatePicker.js | 5 +++++ 14 files changed, 41 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f3a23ef153..315ac38e69 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,7 @@ noBorder: PropTypes.bool, block: PropTypes.bool, small: PropTypes.bool, regular: PropTypes.bool, +autoComplete: PropTypes.string, // calendar presentation and interaction related props renderMonthText: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), // (month) => PropTypes.string, @@ -244,6 +245,7 @@ noBorder: PropTypes.bool, block: PropTypes.bool, small: PropTypes.bool, regular: PropTypes.bool, +autoComplete: PropTypes.string, // calendar presentation and interaction related props renderMonthText: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), // (month) => PropTypes.string, diff --git a/examples/DateRangePickerWrapper.jsx b/examples/DateRangePickerWrapper.jsx index 96154f4d06..a210448ff0 100644 --- a/examples/DateRangePickerWrapper.jsx +++ b/examples/DateRangePickerWrapper.jsx @@ -58,6 +58,7 @@ const defaultProps = { block: false, small: false, regular: false, + autoComplete: 'off', // calendar presentation and interaction related props renderMonthText: null, diff --git a/examples/SingleDatePickerWrapper.jsx b/examples/SingleDatePickerWrapper.jsx index 0dcf491462..9b3eaf8c80 100644 --- a/examples/SingleDatePickerWrapper.jsx +++ b/examples/SingleDatePickerWrapper.jsx @@ -43,6 +43,7 @@ const defaultProps = { regular: false, verticalSpacing: undefined, keepFocusOnInput: false, + autoComplete: 'off', // calendar presentation and interaction related props renderMonthText: null, diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index 8a3467105f..e9c12dfccc 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -29,6 +29,7 @@ const propTypes = forbidExtraProps({ placeholder: PropTypes.string, displayValue: PropTypes.string, ariaLabel: PropTypes.string, + autoComplete: PropTypes.string, titleText: PropTypes.string, screenReaderMessage: PropTypes.string, focused: PropTypes.bool, @@ -58,6 +59,7 @@ const defaultProps = { placeholder: 'Select Date', displayValue: '', ariaLabel: undefined, + autoComplete: 'off', titleText: undefined, screenReaderMessage: '', focused: false, @@ -177,6 +179,7 @@ class DateInput extends React.PureComponent { id, placeholder, ariaLabel, + autoComplete, titleText, displayValue, screenReaderMessage, @@ -234,7 +237,7 @@ class DateInput extends React.PureComponent { onKeyDown={this.onKeyDown} onFocus={onFocus} placeholder={placeholder} - autoComplete="off" + autoComplete={autoComplete} disabled={disabled} readOnly={typeof readOnly === 'boolean' ? readOnly : isTouch} required={required} diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index a67e535c1a..1f706991d4 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -99,6 +99,7 @@ const defaultProps = { verticalHeight: null, transitionDuration: undefined, verticalSpacing: DEFAULT_VERTICAL_SPACING, + autoComplete: 'off', horizontalMonthPadding: undefined, // navigation related props @@ -595,6 +596,7 @@ class DateRangePicker extends React.PureComponent { disabled, required, readOnly, + autoComplete, openDirection, phrases, isOutsideRange, @@ -668,6 +670,7 @@ class DateRangePicker extends React.PureComponent { small={small} regular={regular} verticalSpacing={verticalSpacing} + autoComplete={autoComplete} > {this.maybeRenderDayPickerWithPortal()} diff --git a/src/components/DateRangePickerInput.jsx b/src/components/DateRangePickerInput.jsx index d3923c98b5..1f0a2551e2 100644 --- a/src/components/DateRangePickerInput.jsx +++ b/src/components/DateRangePickerInput.jsx @@ -72,6 +72,7 @@ const propTypes = forbidExtraProps({ small: PropTypes.bool, regular: PropTypes.bool, verticalSpacing: nonNegativeInteger, + autoComplete: PropTypes.string, // accessibility isFocused: PropTypes.bool, // describes actual DOM focus @@ -93,6 +94,7 @@ const defaultProps = { startDateTitleText: undefined, endDateTitleText: undefined, screenReaderMessage: '', + autoComplete: 'off', onStartDateFocus() {}, onEndDateFocus() {}, onStartDateChange() {}, @@ -162,6 +164,7 @@ function DateRangePickerInput({ disabled, required, readOnly, + autoComplete, showCaret, openDirection, showDefaultInputIcon, @@ -233,6 +236,7 @@ function DateRangePickerInput({ id={startDateId} placeholder={startDatePlaceholderText} ariaLabel={startDateAriaLabel} + autoComplete={autoComplete} titleText={startDateTitleText} displayValue={startDate} screenReaderMessage={screenReaderStartDateText} @@ -269,6 +273,7 @@ function DateRangePickerInput({ id={endDateId} placeholder={endDatePlaceholderText} ariaLabel={endDateAriaLabel} + autoComplete={autoComplete} titleText={endDateTitleText} displayValue={endDate} screenReaderMessage={screenReaderEndDateText} diff --git a/src/components/DateRangePickerInputController.jsx b/src/components/DateRangePickerInputController.jsx index d644b55037..56d2a39fd0 100644 --- a/src/components/DateRangePickerInputController.jsx +++ b/src/components/DateRangePickerInputController.jsx @@ -58,6 +58,7 @@ const propTypes = forbidExtraProps({ small: PropTypes.bool, regular: PropTypes.bool, verticalSpacing: nonNegativeInteger, + autoComplete: PropTypes.string, keepOpenOnDateSelect: PropTypes.bool, reopenPickerOnClearDates: PropTypes.bool, @@ -117,6 +118,7 @@ const defaultProps = { small: false, regular: false, verticalSpacing: undefined, + autoComplete: 'off', keepOpenOnDateSelect: false, reopenPickerOnClearDates: false, @@ -312,6 +314,7 @@ export default class DateRangePickerInputController extends React.PureComponent small, regular, verticalSpacing, + autoComplete, } = this.props; const startDateString = this.getDateString(startDate); @@ -359,6 +362,7 @@ export default class DateRangePickerInputController extends React.PureComponent small={small} regular={regular} verticalSpacing={verticalSpacing} + autoComplete={autoComplete} > {children} diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index c449f7e2fb..d1a5e3f19c 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -53,6 +53,7 @@ const defaultProps = { id: 'date', placeholder: 'Date', ariaLabel: undefined, + autoComplete: 'off', titleText: undefined, disabled: false, required: false, @@ -550,6 +551,7 @@ class SingleDatePicker extends React.PureComponent { id, placeholder, ariaLabel, + autoComplete, titleText, disabled, focused, @@ -592,6 +594,7 @@ class SingleDatePicker extends React.PureComponent { id={id} placeholder={placeholder} ariaLabel={ariaLabel} + autoComplete={autoComplete} titleText={titleText} focused={focused} isFocused={isInputFocused} diff --git a/src/components/SingleDatePickerInput.jsx b/src/components/SingleDatePickerInput.jsx index ce9aa24e79..17de3d017b 100644 --- a/src/components/SingleDatePickerInput.jsx +++ b/src/components/SingleDatePickerInput.jsx @@ -22,6 +22,7 @@ const propTypes = forbidExtraProps({ children: PropTypes.node, placeholder: PropTypes.string, ariaLabel: PropTypes.string, + autoComplete: PropTypes.string, titleText: PropTypes.string, displayValue: PropTypes.string, screenReaderMessage: PropTypes.string, @@ -60,6 +61,7 @@ const defaultProps = { children: null, placeholder: 'Select Date', ariaLabel: undefined, + autoComplete: 'off', titleText: undefined, displayValue: '', screenReaderMessage: '', @@ -99,6 +101,7 @@ function SingleDatePickerInput({ children, placeholder, ariaLabel, + autoComplete, titleText, displayValue, focused, @@ -172,6 +175,7 @@ function SingleDatePickerInput({ id={id} placeholder={placeholder} ariaLabel={ariaLabel} + autoComplete={autoComplete} titleText={titleText} displayValue={displayValue} screenReaderMessage={screenReaderText} diff --git a/src/components/SingleDatePickerInputController.jsx b/src/components/SingleDatePickerInputController.jsx index ccaf469369..c7a3214c3e 100644 --- a/src/components/SingleDatePickerInputController.jsx +++ b/src/components/SingleDatePickerInputController.jsx @@ -36,6 +36,7 @@ const propTypes = forbidExtraProps({ id: PropTypes.string.isRequired, placeholder: PropTypes.string, ariaLabel: PropTypes.string, + autoComplete: PropTypes.string, titleText: PropTypes.string, screenReaderMessage: PropTypes.string, showClearDate: PropTypes.bool, @@ -82,6 +83,7 @@ const defaultProps = { placeholder: '', ariaLabel: undefined, + autoComplete: 'off', titleText: undefined, screenReaderMessage: 'Date', showClearDate: false, @@ -204,6 +206,7 @@ export default class SingleDatePickerInputController extends React.PureComponent id, placeholder, ariaLabel, + autoComplete, titleText, disabled, focused, @@ -237,6 +240,7 @@ export default class SingleDatePickerInputController extends React.PureComponent id={id} placeholder={placeholder} ariaLabel={ariaLabel} + autoComplete={autoComplete} titleText={titleText} focused={focused} isFocused={isFocused} diff --git a/src/shapes/DateRangePickerShape.js b/src/shapes/DateRangePickerShape.js index a54126932b..94763c0aab 100644 --- a/src/shapes/DateRangePickerShape.js +++ b/src/shapes/DateRangePickerShape.js @@ -52,6 +52,7 @@ export default { small: PropTypes.bool, regular: PropTypes.bool, keepFocusOnInput: PropTypes.bool, + autoComplete: PropTypes.string, // calendar presentation and interaction related props renderMonthText: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), diff --git a/src/shapes/SingleDatePickerShape.js b/src/shapes/SingleDatePickerShape.js index ef9edcb4e0..a55a02c012 100644 --- a/src/shapes/SingleDatePickerShape.js +++ b/src/shapes/SingleDatePickerShape.js @@ -41,6 +41,7 @@ export default { regular: PropTypes.bool, verticalSpacing: nonNegativeInteger, keepFocusOnInput: PropTypes.bool, + autoComplete: PropTypes.string, // calendar presentation and interaction related props renderMonthText: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), diff --git a/stories/DateRangePicker.js b/stories/DateRangePicker.js index 0353260f59..8f124cde84 100644 --- a/stories/DateRangePicker.js +++ b/stories/DateRangePicker.js @@ -124,4 +124,7 @@ storiesOf('DateRangePicker (DRP)', module) maxDate={moment().add(2, 'months').endOf('month')} numberOfMonths={2} /> + ))) + .add('with custom autoComplete', withInfo()(() => ( + ))); diff --git a/stories/SingleDatePicker.js b/stories/SingleDatePicker.js index 81ad053090..3a7d96e656 100644 --- a/stories/SingleDatePicker.js +++ b/stories/SingleDatePicker.js @@ -98,4 +98,9 @@ storiesOf('SingleDatePicker (SDP)', module) orientation={VERTICAL_ORIENTATION} verticalHeight={568} /> + ))) + .add('with custom autoComplete attribute', withInfo()(() => ( + ))); From af5d7c25a2be60b139736afe931cabf10b747870 Mon Sep 17 00:00:00 2001 From: mum-never-proud Date: Sat, 30 May 2020 20:03:30 +0530 Subject: [PATCH 593/618] [meta] storybook tag fix --- stories/DateRangePicker_day.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stories/DateRangePicker_day.js b/stories/DateRangePicker_day.js index 7c75a2614a..8f27abb041 100644 --- a/stories/DateRangePicker_day.js +++ b/stories/DateRangePicker_day.js @@ -126,7 +126,7 @@ storiesOf('DRP - Day Props', module) ))) .add('with custom daily details', withInfo()(() => ( {day.format('ddd')}} + renderDayContents={day =>
    {day.format('ddd')}
    } autoFocus /> ))) From 3ddb7b56a944f64bede7b33c1cdb300a0b6fb877 Mon Sep 17 00:00:00 2001 From: Dane David Date: Fri, 8 May 2020 22:34:04 +0530 Subject: [PATCH 594/618] [readme] Add `verticalSpacing` Fixes #1852. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 315ac38e69..da8f6c36e2 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,7 @@ reopenPickerOnClearDates: PropTypes.bool, renderCalendarInfo: PropTypes.func, renderMonthElement: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'renderMonthElement'), PropTypes.func, // ({ month, onMonthSelect, onYearSelect, isVisible }) => PropTypes.node, hideKeyboardShortcutsPanel: PropTypes.bool, +verticalSpacing: PropTypes.number, // navigation related props navPrev: PropTypes.node, @@ -267,6 +268,7 @@ renderMonthElement: mutuallyExclusiveProps(PropTypes.func, 'renderMonthText', 'r hideKeyboardShortcutsPanel: PropTypes.bool, daySize: nonNegativeInteger, isRTL: PropTypes.bool, +verticalSpacing: PropTypes.number, // navigation related props navPrev: PropTypes.node, From 5bcf4d59655c641dfdd620984d07119c3e4cbc1d Mon Sep 17 00:00:00 2001 From: sainath-batthala Date: Wed, 19 Jul 2017 16:22:25 +0530 Subject: [PATCH 595/618] [Fix] `DateRangePicker`, `SingleDatePickerInput`: Set tab index as -1 for default and custom date icons --- src/components/DateRangePicker.jsx | 1 + src/components/SingleDatePickerInput.jsx | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 1f706991d4..445d7e0c5e 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -563,6 +563,7 @@ class DateRangePicker extends React.PureComponent { type="button" onClick={this.onOutsideClick} aria-label={phrases.closeDatePicker} + tabIndex="-1" > {closeIcon} diff --git a/src/components/SingleDatePickerInput.jsx b/src/components/SingleDatePickerInput.jsx index 17de3d017b..e6516704b6 100644 --- a/src/components/SingleDatePickerInput.jsx +++ b/src/components/SingleDatePickerInput.jsx @@ -153,6 +153,7 @@ function SingleDatePickerInput({ disabled={disabled} aria-label={phrases.focusStartDate} onClick={onFocus} + tabIndex="-1" > {calendarIcon} From f5fa12291d858546a9754bea72e13ccc433102bc Mon Sep 17 00:00:00 2001 From: Sajid Ahmed Date: Mon, 18 Feb 2019 17:20:16 -0500 Subject: [PATCH 596/618] [Fix] `DayPicker`: Fixed month transition in RTL mode Fixes #1551 --- src/components/DayPicker.jsx | 6 +- test/components/DayPicker_spec.jsx | 104 +++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 2 deletions(-) diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index 34345f7259..a5aff8b123 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -473,10 +473,11 @@ class DayPicker extends React.PureComponent { e.preventDefault(); if (isRTL) { newFocusedDate.add(1, 'day'); + didTransitionMonth = this.maybeTransitionNextMonth(newFocusedDate); } else { newFocusedDate.subtract(1, 'day'); + didTransitionMonth = this.maybeTransitionPrevMonth(newFocusedDate); } - didTransitionMonth = this.maybeTransitionPrevMonth(newFocusedDate); break; case 'Home': e.preventDefault(); @@ -498,10 +499,11 @@ class DayPicker extends React.PureComponent { e.preventDefault(); if (isRTL) { newFocusedDate.subtract(1, 'day'); + didTransitionMonth = this.maybeTransitionPrevMonth(newFocusedDate); } else { newFocusedDate.add(1, 'day'); + didTransitionMonth = this.maybeTransitionNextMonth(newFocusedDate); } - didTransitionMonth = this.maybeTransitionNextMonth(newFocusedDate); break; case 'End': e.preventDefault(); diff --git a/test/components/DayPicker_spec.jsx b/test/components/DayPicker_spec.jsx index 46d0697f90..ed005f40c1 100644 --- a/test/components/DayPicker_spec.jsx +++ b/test/components/DayPicker_spec.jsx @@ -296,6 +296,58 @@ describe('DayPicker', () => { const arg = maybeTransitionPrevMonthSpy.getCall(0).args[0]; expect(arg.isSame(oneDayBefore, 'day')).to.equal(true); }); + + it('arg is end of previous month', () => { + const startOfThisMonth = today.clone().startOf('month'); + const endOfPrevMonth = startOfThisMonth.clone().subtract(1, 'day'); + + const maybeTransitionPrevMonthSpy = sinon.spy(PureDayPicker.prototype, 'maybeTransitionPrevMonth'); + const wrapper = shallow().dive(); + wrapper.setState({ + focusedDate: startOfThisMonth, + }); + wrapper.instance().onKeyDown({ ...event, key: 'ArrowLeft' }); + const arg = maybeTransitionPrevMonthSpy.getCall(0).args[0]; + expect(arg.isSame(endOfPrevMonth, 'day')).to.equal(true); + }); + }); + + describe('ArrowLeft -- RTL', () => { + it('calls maybeTransitionNextMonth', () => { + const maybeTransitionNextMonthSpy = sinon.spy(PureDayPicker.prototype, 'maybeTransitionNextMonth'); + const wrapper = shallow().dive(); + wrapper.setState({ + focusedDate: today, + }); + wrapper.instance().onKeyDown({ ...event, key: 'ArrowLeft' }); + expect(maybeTransitionNextMonthSpy.callCount).to.equal(1); + }); + + it('arg is 1 day after focusedDate', () => { + const oneDayAfter = today.clone().add(1, 'day'); + const maybeTransitionNextMonthSpy = sinon.spy(PureDayPicker.prototype, 'maybeTransitionNextMonth'); + const wrapper = shallow().dive(); + wrapper.setState({ + focusedDate: today, + }); + wrapper.instance().onKeyDown({ ...event, key: 'ArrowLeft' }); + const arg = maybeTransitionNextMonthSpy.getCall(0).args[0]; + expect(arg.isSame(oneDayAfter, 'day')).to.equal(true); + }); + + it('arg is start of next month', () => { + const endOfThisMonth = today.clone().endOf('month'); + const startOfNextMonth = endOfThisMonth.clone().add(1, 'day'); + + const maybeTransitionNextMonthSpy = sinon.spy(PureDayPicker.prototype, 'maybeTransitionNextMonth'); + const wrapper = shallow().dive(); + wrapper.setState({ + focusedDate: endOfThisMonth, + }); + wrapper.instance().onKeyDown({ ...event, key: 'ArrowLeft' }); + const arg = maybeTransitionNextMonthSpy.getCall(0).args[0]; + expect(arg.isSame(startOfNextMonth, 'day')).to.equal(true); + }); }); describe('Home', () => { @@ -392,6 +444,58 @@ describe('DayPicker', () => { const arg = maybeTransitionNextMonthSpy.getCall(0).args[0]; expect(arg.isSame(oneDayAfter, 'day')).to.equal(true); }); + + it('arg is start of next month', () => { + const endOfThisMonth = today.clone().endOf('month'); + const startOfNextMonth = endOfThisMonth.clone().add(1, 'day'); + + const maybeTransitionNextMonthSpy = sinon.spy(PureDayPicker.prototype, 'maybeTransitionNextMonth'); + const wrapper = shallow().dive(); + wrapper.setState({ + focusedDate: endOfThisMonth, + }); + wrapper.instance().onKeyDown({ ...event, key: 'ArrowRight' }); + const arg = maybeTransitionNextMonthSpy.getCall(0).args[0]; + expect(arg.isSame(startOfNextMonth, 'day')).to.equal(true); + }); + }); + + describe('ArrowRight -- RTL', () => { + it('calls maybeTransitionPrevMonth', () => { + const maybeTransitionPrevMonthSpy = sinon.spy(PureDayPicker.prototype, 'maybeTransitionPrevMonth'); + const wrapper = shallow().dive(); + wrapper.setState({ + focusedDate: today, + }); + wrapper.instance().onKeyDown({ ...event, key: 'ArrowRight' }); + expect(maybeTransitionPrevMonthSpy.callCount).to.equal(1); + }); + + it('arg is 1 day before focusedDate', () => { + const oneDayBefore = today.clone().subtract(1, 'day'); + const maybeTransitionPrevMonthSpy = sinon.spy(PureDayPicker.prototype, 'maybeTransitionPrevMonth'); + const wrapper = shallow().dive(); + wrapper.setState({ + focusedDate: today, + }); + wrapper.instance().onKeyDown({ ...event, key: 'ArrowRight' }); + const arg = maybeTransitionPrevMonthSpy.getCall(0).args[0]; + expect(arg.isSame(oneDayBefore, 'day')).to.equal(true); + }); + + it('arg is end of previous month', () => { + const startOfThisMonth = today.clone().startOf('month'); + const endOfPrevMonth = startOfThisMonth.clone().subtract(1, 'day'); + + const maybeTransitionPrevMonthSpy = sinon.spy(PureDayPicker.prototype, 'maybeTransitionPrevMonth'); + const wrapper = shallow().dive(); + wrapper.setState({ + focusedDate: startOfThisMonth, + }); + wrapper.instance().onKeyDown({ ...event, key: 'ArrowRight' }); + const arg = maybeTransitionPrevMonthSpy.getCall(0).args[0]; + expect(arg.isSame(endOfPrevMonth, 'day')).to.equal(true); + }); }); describe('End', () => { From 7490089eb2d4a31b0ce3a6a0aa6681025436e486 Mon Sep 17 00:00:00 2001 From: Jay Swain Date: Tue, 27 Nov 2018 16:59:21 -0800 Subject: [PATCH 597/618] [Fix] `DayPicker`, `DayPickerSingleDateController`: Snap to new date if date is off screen Fixes #1320. Co-authored-by: Jay Swain Co-authored-by: Jordan Harband --- src/components/DayPicker.jsx | 6 ++ .../DayPickerSingleDateController.jsx | 6 ++ .../DayPickerSingleDateController_spec.jsx | 26 +++++++ test/components/DayPicker_spec.jsx | 67 ++++++++++++++++--- 4 files changed, 96 insertions(+), 9 deletions(-) diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index a5aff8b123..0d25a948cf 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -300,6 +300,12 @@ class DayPicker extends React.PureComponent { this.setState({ currentMonth: nextProps.initialVisibleMonth(), }); + } else { + const { numberOfMonths } = this.props; + const newDate = nextProps.initialVisibleMonth(); + if (!isDayVisible(newDate, currentMonth, numberOfMonths)) { + this.onMonthChange(newDate); + } } } diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index de492c3a24..36705917a6 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -268,6 +268,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { const recomputePropModifiers = ( recomputeOutsideRange || recomputeDayBlocked || recomputeDayHighlighted ); + const { currentMonth: prevCurrentMonth } = this.state; if ( numberOfMonths !== prevNumberOfMonths @@ -277,6 +278,11 @@ export default class DayPickerSingleDateController extends React.PureComponent { && !prevFocused && focused ) + || ( + prevDate + && prevDate.diff(date) + && !isDayVisible(date, prevCurrentMonth, numberOfMonths) + ) ) { const newMonthState = this.getStateForNewMonth(nextProps); const { currentMonth } = newMonthState; diff --git a/test/components/DayPickerSingleDateController_spec.jsx b/test/components/DayPickerSingleDateController_spec.jsx index 3bc892f635..f6a62e04e5 100644 --- a/test/components/DayPickerSingleDateController_spec.jsx +++ b/test/components/DayPickerSingleDateController_spec.jsx @@ -47,6 +47,32 @@ describe('DayPickerSingleDateController', () => { onFocusChange() {}, }; + describe('props.date changed', () => { + describe('date is not visible', () => { + it('setState gets called with new month', () => { + sinon.stub(isDayVisible, 'default').returns(false); + const date = today; + const newDate = moment().add(1, 'month'); + const wrapper = shallow(); + expect(wrapper.state()).to.have.property('currentMonth', date); + wrapper.instance().componentWillReceiveProps({ ...props, date: newDate }); + expect(wrapper.state()).to.have.property('currentMonth', newDate); + }); + }); + + describe('date is visible', () => { + it('setState gets called with existing month', () => { + sinon.stub(isDayVisible, 'default').returns(true); + const date = today; + const newDate = moment().add(1, 'month'); + const wrapper = shallow(); + expect(wrapper.state()).to.have.property('currentMonth', date); + wrapper.instance().componentWillReceiveProps({ ...props, date: newDate }); + expect(wrapper.state()).to.have.property('currentMonth', date); + }); + }); + }); + describe('modifiers', () => { describe('selected modifier', () => { describe('props.date did not change', () => { diff --git a/test/components/DayPicker_spec.jsx b/test/components/DayPicker_spec.jsx index ed005f40c1..53070b483c 100644 --- a/test/components/DayPicker_spec.jsx +++ b/test/components/DayPicker_spec.jsx @@ -7,7 +7,7 @@ import { mount, shallow } from 'enzyme'; import * as isDayVisible from '../../src/utils/isDayVisible'; import isSameMonth from '../../src/utils/isSameMonth'; -import DayPicker, { PureDayPicker } from '../../src/components/DayPicker'; +import DayPicker, { PureDayPicker, defaultProps as DayPickerDefaultProps } from '../../src/components/DayPicker'; import CalendarMonthGrid from '../../src/components/CalendarMonthGrid'; import DayPickerNavigation from '../../src/components/DayPickerNavigation'; import DayPickerKeyboardShortcuts from '../../src/components/DayPickerKeyboardShortcuts'; @@ -1022,7 +1022,7 @@ describe('DayPicker', () => { }); it('does not update state.currentMonthScrollTop', () => { - sinon.spy(DayPicker.prototype, 'setTransitionContainerRef'); + sinon.spy(PureDayPicker.prototype, 'setTransitionContainerRef'); const wrapper = mount(); expect(wrapper.state().currentMonthScrollTop).to.equal(null); }); @@ -1035,7 +1035,7 @@ describe('DayPicker', () => { }); it('does not update state.currentMonthScrollTop', () => { - sinon.spy(DayPicker.prototype, 'setTransitionContainerRef'); + sinon.spy(PureDayPicker.prototype, 'setTransitionContainerRef'); const wrapper = mount(); expect(wrapper.state().currentMonthScrollTop).to.equal(null); }); @@ -1043,17 +1043,17 @@ describe('DayPicker', () => { describe('props.orientation === VERTICAL_SCROLLABLE', () => { it('updates state.currentMonthScrollTop', () => { - sinon.spy(DayPicker.prototype, 'setTransitionContainerRef'); + sinon.spy(PureDayPicker.prototype, 'setTransitionContainerRef'); const wrapper = mount(); expect(wrapper.state().currentMonthScrollTop).to.not.equal(null); }); }); }); - describe.skip('#componentWillReceiveProps', () => { - describe('props.orientation === VERTICAL_SCROLLABLE', () => { + describe('#componentWillReceiveProps', () => { + describe.skip('props.orientation === VERTICAL_SCROLLABLE', () => { it('updates state.currentMonthScrollTop', () => { - sinon.spy(DayPicker.prototype, 'setTransitionContainerRef'); + sinon.spy(PureDayPicker.prototype, 'setTransitionContainerRef'); const wrapper = mount(); const prevCurrentMonthScrollTop = wrapper.state().currentMonthScrollTop; wrapper.setState({ @@ -1063,6 +1063,55 @@ describe('DayPicker', () => { expect(wrapper.state().currentMonthScrollTop).to.not.equal(prevCurrentMonthScrollTop); }); }); + + describe('props.date changed', () => { + const props = { + ...DayPickerDefaultProps, + onDateChange() {}, + onFocusChange() {}, + initialVisibleMonth() { return today; }, + theme: { reactDates: { spacing: {} } }, + styles: {}, + }; + + describe('date is not visible', () => { + it('setState gets called with new month', () => { + sinon.stub(isDayVisible, 'default').returns(false); + const date = today; + const newDate = date.clone().add(1, 'month'); + const wrapper = shallow(); + expect(wrapper.state().currentMonth).to.eql(date); + wrapper.instance().componentWillReceiveProps( + { + ...props, + date: newDate, + initialVisibleMonth() { return newDate; }, + }, + {}, + ); + expect(wrapper.state().currentMonth).to.eql(newDate); + }); + }); + + describe('date is visible', () => { + it('setState gets called with existing month', () => { + sinon.stub(isDayVisible, 'default').returns(true); + const date = today; + const newDate = date.clone().add(1, 'month'); + const wrapper = shallow(); + expect(wrapper.state().currentMonth).to.eql(date); + wrapper.instance().componentWillReceiveProps( + { + ...props, + date: newDate, + initialVisibleMonth() { return newDate; }, + }, + {}, + ); + expect(wrapper.state().currentMonth).to.eql(date); + }); + }); + }); }); describe('#componentDidUpdate', () => { @@ -1199,7 +1248,7 @@ describe('DayPicker', () => { describe.skip('props.orientation === VERTICAL_SCROLLABLE', () => { it('does not update transitionContainer ref`s scrollTop currentMonth stays the same', () => { - sinon.spy(DayPicker.prototype, 'setTransitionContainerRef'); + sinon.spy(PureDayPicker.prototype, 'setTransitionContainerRef'); const wrapper = mount(); const prevScrollTop = wrapper.transitionContainer.scrollTop; wrapper.setState({ @@ -1209,7 +1258,7 @@ describe('DayPicker', () => { }); it('updates transitionContainer ref`s scrollTop currentMonth changes', () => { - sinon.spy(DayPicker.prototype, 'setTransitionContainerRef'); + sinon.spy(PureDayPicker.prototype, 'setTransitionContainerRef'); const wrapper = mount(); const prevScrollTop = wrapper.transitionContainer.scrollTop; wrapper.setState({ From fe31485fad71929eabbb349403f9deccff466b72 Mon Sep 17 00:00:00 2001 From: Cedric Sohrauer Date: Tue, 2 Jul 2019 18:08:18 +0200 Subject: [PATCH 598/618] [Tests] add shared `describeIfWindow` helper --- test/_helpers/describeIfWindow.js | 1 + test/components/DateRangePicker_spec.jsx | 2 +- test/components/SingleDatePicker_spec.jsx | 2 +- test/utils/disableScroll_spec.js | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 test/_helpers/describeIfWindow.js diff --git a/test/_helpers/describeIfWindow.js b/test/_helpers/describeIfWindow.js new file mode 100644 index 0000000000..0889ec0a72 --- /dev/null +++ b/test/_helpers/describeIfWindow.js @@ -0,0 +1 @@ +export default typeof document === 'undefined' ? describe.skip : describe; diff --git a/test/components/DateRangePicker_spec.jsx b/test/components/DateRangePicker_spec.jsx index 7f20e12d75..ca34ae3f8f 100644 --- a/test/components/DateRangePicker_spec.jsx +++ b/test/components/DateRangePicker_spec.jsx @@ -16,7 +16,7 @@ import { START_DATE, } from '../../src/constants'; -const describeIfWindow = typeof document === 'undefined' ? describe.skip : describe; +import describeIfWindow from '../_helpers/describeIfWindow'; class DateRangePickerWrapper extends React.Component { constructor(props) { diff --git a/test/components/SingleDatePicker_spec.jsx b/test/components/SingleDatePicker_spec.jsx index 05f2a217bd..b94e184811 100644 --- a/test/components/SingleDatePicker_spec.jsx +++ b/test/components/SingleDatePicker_spec.jsx @@ -9,7 +9,7 @@ import DayPickerSingleDateController from '../../src/components/DayPickerSingleD import SingleDatePickerInputController from '../../src/components/SingleDatePickerInputController'; import SingleDatePicker, { PureSingleDatePicker } from '../../src/components/SingleDatePicker'; -const describeIfWindow = typeof document === 'undefined' ? describe.skip : describe; +import describeIfWindow from '../_helpers/describeIfWindow'; describe('SingleDatePicker', () => { afterEach(() => { diff --git a/test/utils/disableScroll_spec.js b/test/utils/disableScroll_spec.js index 8063b578fd..6a06021073 100644 --- a/test/utils/disableScroll_spec.js +++ b/test/utils/disableScroll_spec.js @@ -5,7 +5,7 @@ import disableScroll, { getScrollAncestorsOverflowY, } from '../../src/utils/disableScroll'; -const describeIfWindow = typeof document === 'undefined' ? describe.skip : describe; +import describeIfWindow from '../_helpers/describeIfWindow'; const createScrollContainer = (level = 1) => { const el = document.createElement('div'); From 17c3c881bcc890dc02d2423f90dcdfb6b478fe26 Mon Sep 17 00:00:00 2001 From: Cedric Sohrauer Date: Tue, 2 Jul 2019 18:08:18 +0200 Subject: [PATCH 599/618] [Fix] `CalendarMonth`: recompute month title height when rerendering --- src/components/CalendarMonth.jsx | 14 ++++++++- test/components/CalendarMonth_spec.jsx | 40 +++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/components/CalendarMonth.jsx b/src/components/CalendarMonth.jsx index 0c9002fbf2..d20650b2e9 100644 --- a/src/components/CalendarMonth.jsx +++ b/src/components/CalendarMonth.jsx @@ -106,7 +106,7 @@ class CalendarMonth extends React.PureComponent { } componentDidMount() { - this.setMonthTitleHeightTimeout = setTimeout(this.setMonthTitleHeight, 0); + this.queueSetMonthTitleHeight(); } componentWillReceiveProps(nextProps) { @@ -131,6 +131,14 @@ class CalendarMonth extends React.PureComponent { } } + componentDidUpdate(prevProps) { + const { setMonthTitleHeight } = this.props; + + if (prevProps.setMonthTitleHeight === null && setMonthTitleHeight !== null) { + this.queueSetMonthTitleHeight(); + } + } + componentWillUnmount() { if (this.setMonthTitleHeightTimeout) { clearTimeout(this.setMonthTitleHeightTimeout); @@ -149,6 +157,10 @@ class CalendarMonth extends React.PureComponent { this.captionRef = ref; } + queueSetMonthTitleHeight() { + this.setMonthTitleHeightTimeout = window.setTimeout(this.setMonthTitleHeight, 0); + } + render() { const { dayAriaLabelFormat, diff --git a/test/components/CalendarMonth_spec.jsx b/test/components/CalendarMonth_spec.jsx index 05a3a6dd82..1a0ab8966e 100644 --- a/test/components/CalendarMonth_spec.jsx +++ b/test/components/CalendarMonth_spec.jsx @@ -1,8 +1,9 @@ import React from 'react'; import { expect } from 'chai'; -import { shallow } from 'enzyme'; +import { shallow, mount } from 'enzyme'; import sinon from 'sinon-sandbox'; import moment from 'moment'; +import describeIfWindow from '../_helpers/describeIfWindow'; import CalendarMonth from '../../src/components/CalendarMonth'; @@ -46,5 +47,42 @@ describe('CalendarMonth', () => { expect(typeof isVisible).to.equal('boolean'); expect(wrapper.find('#month-element').exists()).to.equal(true); }); + + describeIfWindow('setMonthTitleHeight', () => { + beforeEach(() => { + sinon.stub(window, 'setTimeout').callsFake((handler) => handler()); + }); + + it('sets the title height after mount', () => { + const setMonthTitleHeightStub = sinon.stub(); + mount( + , + ); + + expect(setMonthTitleHeightStub).to.have.property('callCount', 1); + }); + + describe('if the callbacks gets set again', () => { + it('updates the title height', () => { + const setMonthTitleHeightStub = sinon.stub(); + const wrapper = mount( + , + ); + + expect(setMonthTitleHeightStub).to.have.property('callCount', 1); + + wrapper.setProps({ setMonthTitleHeight: null }); + + wrapper.setProps({ setMonthTitleHeight: setMonthTitleHeightStub }); + expect(setMonthTitleHeightStub).to.have.property('callCount', 2); + }); + }); + }); }); }); From ba61165b236d4e6ea053d0d34553468dbdaa4940 Mon Sep 17 00:00:00 2001 From: mmarkelov Date: Thu, 9 Jan 2020 13:11:41 +0300 Subject: [PATCH 600/618] [Fix] `DateRangePickerInputController`: fix onClose method --- .../DateRangePickerInputController.jsx | 7 ++++++- .../DateRangePickerInputController_spec.jsx | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/components/DateRangePickerInputController.jsx b/src/components/DateRangePickerInputController.jsx index 56d2a39fd0..b2c9ad7135 100644 --- a/src/components/DateRangePickerInputController.jsx +++ b/src/components/DateRangePickerInputController.jsx @@ -179,6 +179,8 @@ export default class DateRangePickerInputController extends React.PureComponent minimumNights, keepOpenOnDateSelect, onDatesChange, + onClose, + onFocusChange, } = this.props; const endDate = toMomentObject(endDateString, this.getDisplayFormat()); @@ -188,7 +190,10 @@ export default class DateRangePickerInputController extends React.PureComponent && !(startDate && isBeforeDay(endDate, startDate.clone().add(minimumNights, 'days'))); if (isEndDateValid) { onDatesChange({ startDate, endDate }); - if (!keepOpenOnDateSelect) this.onClearFocus(); + if (!keepOpenOnDateSelect) { + onFocusChange(null); + onClose({ startDate, endDate }); + } } else { onDatesChange({ startDate, diff --git a/test/components/DateRangePickerInputController_spec.jsx b/test/components/DateRangePickerInputController_spec.jsx index b6112065bd..a5f63e1539 100644 --- a/test/components/DateRangePickerInputController_spec.jsx +++ b/test/components/DateRangePickerInputController_spec.jsx @@ -221,6 +221,23 @@ describe('DateRangePickerInputController', () => { expect(isSameDay(onDatesChangeArgs.endDate, futureDate)).to.equal(true); }); + it('calls props.onClose with props.startDate and provided end date', () => { + const onCloseStub = sinon.stub(); + const wrapper = shallow(( + + )); + wrapper.instance().onEndDateChange(validFutureDateString); + expect(onCloseStub).to.have.property('callCount', 1); + + const [onCloseArgs] = onCloseStub.getCall(0).args; + const futureDate = moment(validFutureDateString); + expect(onCloseArgs).to.have.property('startDate', startDate); + expect(isSameDay(onCloseArgs.endDate, futureDate)).to.equal(true); + }); + describe('props.onFocusChange', () => { it('is called once', () => { const onFocusChangeStub = sinon.stub(); From dce9c227e305072d34aa62d3f9384a13a89f666c Mon Sep 17 00:00:00 2001 From: Maja Wichrowska Date: Fri, 11 Oct 2019 20:35:48 -0700 Subject: [PATCH 601/618] Revert "Revert "[Tests] `DayPicker`: unit test for getWeekHeaders()"" This reverts commit defb7fd680cab4154d12e9416c9f28e4a47e9e5b. --- test/components/DayPicker_spec.jsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/components/DayPicker_spec.jsx b/test/components/DayPicker_spec.jsx index 53070b483c..6b11bf3793 100644 --- a/test/components/DayPicker_spec.jsx +++ b/test/components/DayPicker_spec.jsx @@ -3,6 +3,7 @@ import moment from 'moment/min/moment-with-locales'; import { expect } from 'chai'; import sinon from 'sinon-sandbox'; import { mount, shallow } from 'enzyme'; +import { cloneDeep } from 'lodash'; import * as isDayVisible from '../../src/utils/isDayVisible'; import isSameMonth from '../../src/utils/isSameMonth'; @@ -1013,6 +1014,23 @@ describe('DayPicker', () => { }); }); + describe('#getWeekHeaders', () => { + it('returns unmutated weekday headers for currentMonth in a future', () => { + sinon.stub(PureDayPicker.prototype, 'render').returns(null); + + const getWeekHeadersSpy = sinon.spy(PureDayPicker.prototype, 'getWeekHeaders'); + const INITIAL_MONTH = moment().add(2, 'Months').week(3).weekday(3); + const wrapper = shallow( INITIAL_MONTH} />).dive(); + const instance = wrapper.instance(); + const state = cloneDeep(wrapper.state()); + + expect(instance.getWeekHeaders()).to.be.eql(INITIAL_MONTH.localeData().weekdaysMin()); + expect(instance.state).not.to.equal(state); + expect(instance.state).to.eql(state); + expect(getWeekHeadersSpy).to.have.property('callCount', 1); + }); + }); + describe('life cycle methods', () => { describe.skip('#componentDidMount', () => { describe('props.orientation === HORIZONTAL_ORIENTATION', () => { From 57cba2f836a203f6ff246feb5edf423a3853953e Mon Sep 17 00:00:00 2001 From: mmarkelov Date: Tue, 17 Dec 2019 10:14:37 +0300 Subject: [PATCH 602/618] [Refactor] `CalendarMonthGrid`: Skip unnecessary setState --- src/components/CalendarMonthGrid.jsx | 44 +++++++++++----------- test/components/CalendarMonthGrid_spec.jsx | 15 ++++++++ 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/components/CalendarMonthGrid.jsx b/src/components/CalendarMonthGrid.jsx index 849635d423..8734f6e8fe 100644 --- a/src/components/CalendarMonthGrid.jsx +++ b/src/components/CalendarMonthGrid.jsx @@ -151,33 +151,35 @@ class CalendarMonthGrid extends React.PureComponent { const hasNumberOfMonthsChanged = prevNumberOfMonths !== numberOfMonths; let newMonths = months; - if (hasMonthChanged && !hasNumberOfMonthsChanged) { - if (isNextMonth(prevInitialMonth, initialMonth)) { - newMonths = months.slice(1); - newMonths.push(months[months.length - 1].clone().add(1, 'month')); - } else if (isPrevMonth(prevInitialMonth, initialMonth)) { - newMonths = months.slice(0, months.length - 1); - newMonths.unshift(months[0].clone().subtract(1, 'month')); - } else { + if (hasMonthChanged || hasNumberOfMonthsChanged) { + if (hasMonthChanged && !hasNumberOfMonthsChanged) { + if (isNextMonth(prevInitialMonth, initialMonth)) { + newMonths = months.slice(1); + newMonths.push(months[months.length - 1].clone().add(1, 'month')); + } else if (isPrevMonth(prevInitialMonth, initialMonth)) { + newMonths = months.slice(0, months.length - 1); + newMonths.unshift(months[0].clone().subtract(1, 'month')); + } else { + const withoutTransitionMonths = orientation === VERTICAL_SCROLLABLE; + newMonths = getMonths(initialMonth, numberOfMonths, withoutTransitionMonths); + } + } + + if (hasNumberOfMonthsChanged) { const withoutTransitionMonths = orientation === VERTICAL_SCROLLABLE; newMonths = getMonths(initialMonth, numberOfMonths, withoutTransitionMonths); } - } - if (hasNumberOfMonthsChanged) { - const withoutTransitionMonths = orientation === VERTICAL_SCROLLABLE; - newMonths = getMonths(initialMonth, numberOfMonths, withoutTransitionMonths); - } + const momentLocale = moment.locale(); + if (this.locale !== momentLocale) { + this.locale = momentLocale; + newMonths = newMonths.map((m) => m.locale(this.locale)); + } - const momentLocale = moment.locale(); - if (this.locale !== momentLocale) { - this.locale = momentLocale; - newMonths = newMonths.map((m) => m.locale(this.locale)); + this.setState({ + months: newMonths, + }); } - - this.setState({ - months: newMonths, - }); } componentDidUpdate() { diff --git a/test/components/CalendarMonthGrid_spec.jsx b/test/components/CalendarMonthGrid_spec.jsx index 209d7a3d92..3461a18ec1 100644 --- a/test/components/CalendarMonthGrid_spec.jsx +++ b/test/components/CalendarMonthGrid_spec.jsx @@ -45,6 +45,21 @@ describe('CalendarMonthGrid', () => { expect(Object.keys(collisions).length).to.equal(months.length); }); + it('does not setState if hasMonthChanged and hasNumberOfMonthsChanged are falsy', () => { + const setState = sinon.stub(CalendarMonthGrid.prototype, 'setState'); + const initialMonth = moment(); + const wrapper = shallow(( + + )).dive(); + + wrapper.instance().componentWillReceiveProps({ + initialMonth, + numberOfMonths: 12, + }); + + expect(setState.callCount).to.eq(0); + }); + it('works with the same number of months', () => { const initialMonth = moment(); const wrapper = shallow(( From a83df7eac8c85e48326c2a44951ae21d086d7498 Mon Sep 17 00:00:00 2001 From: "maksim.markelov" Date: Thu, 28 Nov 2019 12:27:13 +0300 Subject: [PATCH 603/618] [Fix] `DateInput`: Fix invalid font-weight for regular option Fixes #1868. Closes #1869. --- src/components/DateInput.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index e9c12dfccc..e24da287e0 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -342,7 +342,7 @@ export default withStyles(({ }, DateInput_input__regular: { - fontWeight: 'auto', + fontWeight: 'inherit', }, DateInput_input__readOnly: { From d9b1dd0e0171af551fb8c61f0b23831b86d82a52 Mon Sep 17 00:00:00 2001 From: Cheng Wang Date: Sun, 19 Jan 2020 13:06:21 +0800 Subject: [PATCH 604/618] [Fix] use the injected css prop instead of the imported one --- src/components/CalendarDay.jsx | 3 ++- src/components/CalendarMonth.jsx | 3 ++- src/components/CalendarMonthGrid.jsx | 3 ++- src/components/CustomizableCalendarDay.jsx | 3 ++- src/components/DateInput.jsx | 3 ++- src/components/DateRangePicker.jsx | 4 +++- src/components/DateRangePickerInput.jsx | 3 ++- src/components/DayPicker.jsx | 4 +++- src/components/DayPickerKeyboardShortcuts.jsx | 3 ++- src/components/DayPickerNavigation.jsx | 3 ++- src/components/KeyboardShortcutRow.jsx | 3 ++- src/components/SingleDatePicker.jsx | 4 +++- src/components/SingleDatePickerInput.jsx | 3 ++- test/components/DayPicker_spec.jsx | 1 + 14 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/components/CalendarDay.jsx b/src/components/CalendarDay.jsx index 29816a1dbf..036a7c7655 100644 --- a/src/components/CalendarDay.jsx +++ b/src/components/CalendarDay.jsx @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import momentPropTypes from 'react-moment-proptypes'; import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import moment from 'moment'; import raf from 'raf'; @@ -105,6 +105,7 @@ class CalendarDay extends React.PureComponent { modifiers, renderDayContents, tabIndex, + css, styles, phrases, } = this.props; diff --git a/src/components/CalendarMonth.jsx b/src/components/CalendarMonth.jsx index d20650b2e9..248a486416 100644 --- a/src/components/CalendarMonth.jsx +++ b/src/components/CalendarMonth.jsx @@ -4,7 +4,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import momentPropTypes from 'react-moment-proptypes'; import { forbidExtraProps, mutuallyExclusiveProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import moment from 'moment'; import { CalendarDayPhrases } from '../defaultPhrases'; @@ -183,6 +183,7 @@ class CalendarMonth extends React.PureComponent { renderDayContents, renderMonthElement, renderMonthText, + css, styles, verticalBorderSpacing, } = this.props; diff --git a/src/components/CalendarMonthGrid.jsx b/src/components/CalendarMonthGrid.jsx index 8734f6e8fe..dd1c80bf8f 100644 --- a/src/components/CalendarMonthGrid.jsx +++ b/src/components/CalendarMonthGrid.jsx @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import momentPropTypes from 'react-moment-proptypes'; import { forbidExtraProps, mutuallyExclusiveProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import moment from 'moment'; import { addEventListener } from 'consolidated-events'; @@ -260,6 +260,7 @@ class CalendarMonthGrid extends React.PureComponent { focusedDate, isFocused, isRTL, + css, styles, phrases, dayAriaLabelFormat, diff --git a/src/components/CustomizableCalendarDay.jsx b/src/components/CustomizableCalendarDay.jsx index 2a3ee15752..ad3cdbb5d7 100644 --- a/src/components/CustomizableCalendarDay.jsx +++ b/src/components/CustomizableCalendarDay.jsx @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import momentPropTypes from 'react-moment-proptypes'; import { forbidExtraProps, nonNegativeInteger, or } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import moment from 'moment'; import raf from 'raf'; @@ -279,6 +279,7 @@ class CustomizableCalendarDay extends React.PureComponent { modifiers, tabIndex, renderDayContents, + css, styles, phrases, diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx index e24da287e0..ff486eb155 100644 --- a/src/components/DateInput.jsx +++ b/src/components/DateInput.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import throttle from 'lodash/throttle'; import isTouchDevice from 'is-touch-device'; @@ -194,6 +194,7 @@ class DateInput extends React.PureComponent { small, regular, block, + css, styles, theme: { reactDates }, } = this.props; diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 445d7e0c5e..361056c7df 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -1,6 +1,6 @@ import React from 'react'; import moment from 'moment'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import { Portal } from 'react-portal'; import { forbidExtraProps } from 'airbnb-prop-types'; import { addEventListener } from 'consolidated-events'; @@ -450,6 +450,7 @@ class DateRangePicker extends React.PureComponent { dayAriaLabelFormat, isRTL, weekDayFormat, + css, styles, verticalHeight, noBorder, @@ -616,6 +617,7 @@ class DateRangePicker extends React.PureComponent { verticalSpacing, small, regular, + css, styles, } = this.props; diff --git a/src/components/DateRangePickerInput.jsx b/src/components/DateRangePickerInput.jsx index 1f0a2551e2..09e2ef7d82 100644 --- a/src/components/DateRangePickerInput.jsx +++ b/src/components/DateRangePickerInput.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import { DateRangePickerInputPhrases } from '../defaultPhrases'; import getPhrasePropTypes from '../utils/getPhrasePropTypes'; @@ -180,6 +180,7 @@ function DateRangePickerInput({ verticalSpacing, small, regular, + css, styles, }) { const calendarIcon = customInputIcon || ( diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index 0d25a948cf..a56653cd23 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps, mutuallyExclusiveProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import moment from 'moment'; import throttle from 'lodash/throttle'; @@ -983,6 +983,7 @@ class DayPicker extends React.PureComponent { horizontalMonthPadding, orientation, renderWeekHeaderElement, + css, styles, } = this.props; @@ -1068,6 +1069,7 @@ class DayPicker extends React.PureComponent { daySize, isFocused, isRTL, + css, styles, theme, phrases, diff --git a/src/components/DayPickerKeyboardShortcuts.jsx b/src/components/DayPickerKeyboardShortcuts.jsx index 0f686a1c17..acb521e19d 100644 --- a/src/components/DayPickerKeyboardShortcuts.jsx +++ b/src/components/DayPickerKeyboardShortcuts.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import { DayPickerKeyboardShortcutsPhrases } from '../defaultPhrases'; import getPhrasePropTypes from '../utils/getPhrasePropTypes'; @@ -166,6 +166,7 @@ class DayPickerKeyboardShortcuts extends React.PureComponent { buttonLocation, showKeyboardShortcutsPanel, closeKeyboardShortcutsPanel, + css, styles, phrases, renderKeyboardShortcutsButton, diff --git a/src/components/DayPickerNavigation.jsx b/src/components/DayPickerNavigation.jsx index 1fc2ec3d2f..9b576ef3c5 100644 --- a/src/components/DayPickerNavigation.jsx +++ b/src/components/DayPickerNavigation.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import { DayPickerNavigationPhrases } from '../defaultPhrases'; import getPhrasePropTypes from '../utils/getPhrasePropTypes'; @@ -84,6 +84,7 @@ class DayPickerNavigation extends React.PureComponent { renderNavNextButton, showNavPrevButton, showNavNextButton, + css, styles, } = this.props; diff --git a/src/components/KeyboardShortcutRow.jsx b/src/components/KeyboardShortcutRow.jsx index 7e3d2401c4..2aa755a09e 100644 --- a/src/components/KeyboardShortcutRow.jsx +++ b/src/components/KeyboardShortcutRow.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; const propTypes = forbidExtraProps({ ...withStylesPropTypes, @@ -20,6 +20,7 @@ function KeyboardShortcutRow({ label, action, block, + css, styles, }) { return ( diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index d1a5e3f19c..1f7195b110 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -1,6 +1,6 @@ import React from 'react'; import moment from 'moment'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import { Portal } from 'react-portal'; import { forbidExtraProps } from 'airbnb-prop-types'; import { addEventListener } from 'consolidated-events'; @@ -437,6 +437,7 @@ class SingleDatePicker extends React.PureComponent { isDayBlocked, isDayHighlighted, weekDayFormat, + css, styles, verticalHeight, transitionDuration, @@ -578,6 +579,7 @@ class SingleDatePicker extends React.PureComponent { verticalSpacing, reopenPickerOnClearDate, keepOpenOnDateSelect, + css, styles, isOutsideRange, isDayBlocked, diff --git a/src/components/SingleDatePickerInput.jsx b/src/components/SingleDatePickerInput.jsx index e6516704b6..82a0269832 100644 --- a/src/components/SingleDatePickerInput.jsx +++ b/src/components/SingleDatePickerInput.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types'; -import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; +import { withStyles, withStylesPropTypes } from 'react-with-styles'; import { SingleDatePickerInputPhrases } from '../defaultPhrases'; import getPhrasePropTypes from '../utils/getPhrasePropTypes'; @@ -131,6 +131,7 @@ function SingleDatePickerInput({ small, regular, verticalSpacing, + css, styles, }) { const calendarIcon = customInputIcon || ( diff --git a/test/components/DayPicker_spec.jsx b/test/components/DayPicker_spec.jsx index 6b11bf3793..be120be3c6 100644 --- a/test/components/DayPicker_spec.jsx +++ b/test/components/DayPicker_spec.jsx @@ -1090,6 +1090,7 @@ describe('DayPicker', () => { initialVisibleMonth() { return today; }, theme: { reactDates: { spacing: {} } }, styles: {}, + css() {}, }; describe('date is not visible', () => { From adec1c86590f1f86a4cd2537c6b780cdc35dd812 Mon Sep 17 00:00:00 2001 From: Alex Kubyshkin Date: Sat, 18 Sep 2021 19:31:01 +0300 Subject: [PATCH 605/618] [Fix] `DayPicker{,RangeController,SingleDateController}`: Start every day at noon(12:00). --- src/components/DayPicker.jsx | 6 +++--- src/components/DayPickerRangeController.jsx | 2 +- src/components/DayPickerSingleDateController.jsx | 2 +- src/utils/getNumberOfCalendarMonthWeeks.js | 2 +- src/utils/isDayVisible.js | 9 +++++---- test/components/DayPickerRangeController_spec.jsx | 15 +++++++++++++++ .../DayPickerSingleDateController_spec.jsx | 13 +++++++++++++ test/utils/isDayVisible_spec.js | 8 ++++++++ 8 files changed, 47 insertions(+), 10 deletions(-) diff --git a/src/components/DayPicker.jsx b/src/components/DayPicker.jsx index a56653cd23..83afc34680 100644 --- a/src/components/DayPicker.jsx +++ b/src/components/DayPicker.jsx @@ -203,7 +203,7 @@ class DayPicker extends React.PureComponent { const currentMonth = props.hidden ? moment() : props.initialVisibleMonth(); - let focusedDate = currentMonth.clone().startOf('month'); + let focusedDate = currentMonth.clone().startOf('month').hour(12); if (props.getFirstFocusableDay) { focusedDate = props.getFirstFocusableDay(currentMonth); } @@ -487,7 +487,7 @@ class DayPicker extends React.PureComponent { break; case 'Home': e.preventDefault(); - newFocusedDate.startOf('week'); + newFocusedDate.startOf('week').hour(12); didTransitionMonth = this.maybeTransitionPrevMonth(newFocusedDate); break; case 'PageUp': @@ -703,7 +703,7 @@ class DayPicker extends React.PureComponent { } if (newMonth && (!focusedDate || !isDayVisible(focusedDate, newMonth, numberOfMonths))) { - focusedDate = newMonth.clone().startOf('month'); + focusedDate = newMonth.clone().startOf('month').hour(12); } return focusedDate; diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 87c61f0544..988595a855 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -1045,7 +1045,7 @@ export default class DayPickerRangeController extends React.PureComponent { numberOfMonths, } = this.props; - let focusedDate = newMonth.clone().startOf('month'); + let focusedDate = newMonth.clone().startOf('month').hour(12); if (focusedInput === START_DATE && startDate) { focusedDate = startDate.clone(); } else if (focusedInput === END_DATE && !endDate && startDate) { diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index 36705917a6..dad08cb141 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -545,7 +545,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { getFirstFocusableDay(newMonth) { const { date, numberOfMonths } = this.props; - let focusedDate = newMonth.clone().startOf('month'); + let focusedDate = newMonth.clone().startOf('month').hour(12); if (date) { focusedDate = date.clone(); } diff --git a/src/utils/getNumberOfCalendarMonthWeeks.js b/src/utils/getNumberOfCalendarMonthWeeks.js index 0ed4da8676..0e9482664f 100644 --- a/src/utils/getNumberOfCalendarMonthWeeks.js +++ b/src/utils/getNumberOfCalendarMonthWeeks.js @@ -9,7 +9,7 @@ export default function getNumberOfCalendarMonthWeeks( month, firstDayOfWeek = moment.localeData().firstDayOfWeek(), ) { - const firstDayOfMonth = month.clone().startOf('month'); + const firstDayOfMonth = month.clone().startOf('month').hour(12); const numBlankDays = getBlankDaysBeforeFirstDay(firstDayOfMonth, firstDayOfWeek); return Math.ceil((numBlankDays + month.daysInMonth()) / 7); } diff --git a/src/utils/isDayVisible.js b/src/utils/isDayVisible.js index 34b9420339..e0d43f376c 100644 --- a/src/utils/isDayVisible.js +++ b/src/utils/isDayVisible.js @@ -21,7 +21,7 @@ export default function isDayVisible(day, month, numberOfMonths, enableOutsideDa if (enableOutsideDays) { if (!startCacheOutsideDays.has(startKey)) { - startCacheOutsideDays.set(startKey, month.clone().startOf('month').startOf('week')); + startCacheOutsideDays.set(startKey, month.clone().startOf('month').startOf('week').hour(12)); } if (isBeforeDay(day, startCacheOutsideDays.get(startKey))) return false; @@ -30,7 +30,8 @@ export default function isDayVisible(day, month, numberOfMonths, enableOutsideDa endCacheOutsideDays.set( endKey, month.clone().endOf('week').add(numberOfMonths - 1, 'months').endOf('month') - .endOf('week'), + .endOf('week') + .hour(12), ); } @@ -40,7 +41,7 @@ export default function isDayVisible(day, month, numberOfMonths, enableOutsideDa // !enableOutsideDays if (!startCacheInsideDays.has(startKey)) { - startCacheInsideDays.set(startKey, month.clone().startOf('month')); + startCacheInsideDays.set(startKey, month.clone().startOf('month').hour(12)); } if (isBeforeDay(day, startCacheInsideDays.get(startKey))) return false; @@ -48,7 +49,7 @@ export default function isDayVisible(day, month, numberOfMonths, enableOutsideDa if (!endCacheInsideDays.has(endKey)) { endCacheInsideDays.set( endKey, - month.clone().add(numberOfMonths - 1, 'months').endOf('month'), + month.clone().add(numberOfMonths - 1, 'months').endOf('month').hour(12), ); } diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index f530b93f83..c224009c7c 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -3775,6 +3775,21 @@ describe('DayPickerRangeController', () => { }); }); + it('time is a noon', () => { + sinon.stub(DayPickerRangeController.prototype, 'isBlocked').returns(false); + const wrapper = shallow(( + + )); + const firstFocusableDay = wrapper.instance().getFirstFocusableDay(today); + expect(firstFocusableDay.hours()).to.equal(12); + }); + describe('desired day is blocked', () => { it('returns next unblocked visible day after desired day if exists', () => { const isBlockedStub = sinon.stub(DayPickerRangeController.prototype, 'isBlocked'); diff --git a/test/components/DayPickerSingleDateController_spec.jsx b/test/components/DayPickerSingleDateController_spec.jsx index f6a62e04e5..5e6e09522f 100644 --- a/test/components/DayPickerSingleDateController_spec.jsx +++ b/test/components/DayPickerSingleDateController_spec.jsx @@ -1000,6 +1000,19 @@ describe('DayPickerSingleDateController', () => { expect(firstFocusableDay.isSame(date, 'day')).to.equal(true); }); + it('time is a noon', () => { + sinon.stub(DayPickerSingleDateController.prototype, 'isBlocked').returns(false); + const wrapper = shallow(( + + )); + const firstFocusableDay = wrapper.instance().getFirstFocusableDay(today); + expect(firstFocusableDay.hours()).to.equal(12); + }); + describe('desired date is blocked', () => { it('returns first unblocked visible day if exists', () => { const isBlockedStub = sinon.stub(DayPickerSingleDateController.prototype, 'isBlocked'); diff --git a/test/utils/isDayVisible_spec.js b/test/utils/isDayVisible_spec.js index f42455b549..074c5f2e3e 100644 --- a/test/utils/isDayVisible_spec.js +++ b/test/utils/isDayVisible_spec.js @@ -49,4 +49,12 @@ describe('#isDayVisible', () => { expect(isDayVisible(test, currentMonth, 1, true)).to.equal(false); }); }); + + // this test fails when run with the whole suite, + // potentially due to cache pollution from other tests + it.skip('works when the first day of the week that starts the month does not have a midnight', () => { + const march29 = moment('2020-03-29').utcOffset(-1 /* 'Atlantic/Azores' */); + const april2020 = moment('2020-04-02').utcOffset(-1 /* 'Atlantic/Azores' */); + expect(isDayVisible(march29, april2020, 1, true)).to.equal(true); + }); }); From fdcdc457cfeedecd8fc3572db1b6ba15fe3ab44c Mon Sep 17 00:00:00 2001 From: axsann <7417697+axsann@users.noreply.github.com> Date: Mon, 2 Mar 2020 18:52:42 +0900 Subject: [PATCH 606/618] [Fix] `DayPicker{Range,SingleDate}Controller`: Fix incorrect firstDayOfWeek null checking Co-authored-by: axsann <7417697+axsann@users.noreply.github.com> Co-authored-by: Jordan Harband --- src/components/DayPickerRangeController.jsx | 15 +++++++++++---- src/components/DayPickerSingleDateController.jsx | 15 +++++++++++---- test/components/DayPickerRangeController_spec.jsx | 13 +++++++++++++ .../DayPickerSingleDateController_spec.jsx | 13 +++++++++++++ 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 988595a855..881c4e75ea 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -1036,6 +1036,15 @@ export default class DayPickerRangeController extends React.PureComponent { }); } + getFirstDayOfWeek() { + const { firstDayOfWeek } = this.props; + if (firstDayOfWeek == null) { + return moment.localeData().firstDayOfWeek(); + } + + return firstDayOfWeek; + } + getFirstFocusableDay(newMonth) { const { startDate, @@ -1250,13 +1259,11 @@ export default class DayPickerRangeController extends React.PureComponent { } isFirstDayOfWeek(day) { - const { firstDayOfWeek } = this.props; - return day.day() === (firstDayOfWeek || moment.localeData().firstDayOfWeek()); + return day.day() === this.getFirstDayOfWeek(); } isLastDayOfWeek(day) { - const { firstDayOfWeek } = this.props; - return day.day() === ((firstDayOfWeek || moment.localeData().firstDayOfWeek()) + 6) % 7; + return day.day() === (this.getFirstDayOfWeek() + 6) % 7; } isFirstPossibleEndDateForHoveredStartDate(day, hoverDate) { diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index dad08cb141..d4fae53608 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -542,6 +542,15 @@ export default class DayPickerSingleDateController extends React.PureComponent { }); } + getFirstDayOfWeek() { + const { firstDayOfWeek } = this.props; + if (firstDayOfWeek == null) { + return moment.localeData().firstDayOfWeek(); + } + + return firstDayOfWeek; + } + getFirstFocusableDay(newMonth) { const { date, numberOfMonths } = this.props; @@ -643,13 +652,11 @@ export default class DayPickerSingleDateController extends React.PureComponent { } isFirstDayOfWeek(day) { - const { firstDayOfWeek } = this.props; - return day.day() === (firstDayOfWeek || moment.localeData().firstDayOfWeek()); + return day.day() === this.getFirstDayOfWeek(); } isLastDayOfWeek(day) { - const { firstDayOfWeek } = this.props; - return day.day() === ((firstDayOfWeek || moment.localeData().firstDayOfWeek()) + 6) % 7; + return day.day() === (this.getFirstDayOfWeek() + 6) % 7; } render() { diff --git a/test/components/DayPickerRangeController_spec.jsx b/test/components/DayPickerRangeController_spec.jsx index c224009c7c..55734c7b04 100644 --- a/test/components/DayPickerRangeController_spec.jsx +++ b/test/components/DayPickerRangeController_spec.jsx @@ -4946,6 +4946,19 @@ describe('DayPickerRangeController', () => { expect(wrapper.instance().isFirstDayOfWeek(moment().startOf('week').day(firstDayOfWeek))).to.equal(true); }); + it('returns true if first day of week and prop are both zero', () => { + const firstDayOfWeek = 0; + const wrapper = shallow(); + expect(wrapper.instance().isFirstDayOfWeek(moment().startOf('week').day(firstDayOfWeek))).to.equal(true); + }); + + it('returns true if first day of week is not zero, and prop is zero', () => { + sinon.stub(moment.localeData(), 'firstDayOfWeek').returns(1); + const firstDayOfWeek = 0; + const wrapper = shallow(); + expect(wrapper.instance().isFirstDayOfWeek(moment().startOf('week').day(firstDayOfWeek))).to.equal(true); + }); + it('returns false if not the first day of the week', () => { const wrapper = shallow(); expect(wrapper.instance().isFirstDayOfWeek(moment().endOf('week'))).to.equal(false); diff --git a/test/components/DayPickerSingleDateController_spec.jsx b/test/components/DayPickerSingleDateController_spec.jsx index 5e6e09522f..a0ff339810 100644 --- a/test/components/DayPickerSingleDateController_spec.jsx +++ b/test/components/DayPickerSingleDateController_spec.jsx @@ -1634,6 +1634,19 @@ describe('DayPickerSingleDateController', () => { expect(wrapper.instance().isFirstDayOfWeek(moment().startOf('week').day(firstDayOfWeek))).to.equal(true); }); + it('returns true if first day of week and prop are both zero', () => { + const firstDayOfWeek = 0; + const wrapper = shallow(); + expect(wrapper.instance().isFirstDayOfWeek(moment().startOf('week').day(firstDayOfWeek))).to.equal(true); + }); + + it('returns true if first day of week is not zero, and prop is zero', () => { + sinon.stub(moment.localeData(), 'firstDayOfWeek').returns(1); + const firstDayOfWeek = 0; + const wrapper = shallow(); + expect(wrapper.instance().isFirstDayOfWeek(moment().startOf('week').day(firstDayOfWeek))).to.equal(true); + }); + it('returns false if not the first day of the week', () => { const wrapper = shallow(); expect(wrapper.instance().isFirstDayOfWeek(moment().endOf('week'))).to.equal(false); From 9494adb4616e2b42229bd510b789694dd9711ed4 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Thu, 2 Dec 2021 23:49:54 -0800 Subject: [PATCH 607/618] [Deps] update `color2k`, `object.values`, `react-moment-proptypes`, `react-with-direction` --- package.json | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 9400cc49fd..b052d45f12 100644 --- a/package.json +++ b/package.json @@ -112,19 +112,19 @@ }, "dependencies": { "airbnb-prop-types": "^2.16.0", - "color2k": "^1.1.1", + "color2k": "^1.2.4", "consolidated-events": "^1.1.1 || ^2.0.0", "enzyme-shallow-equal": "^1.0.4", "is-touch-device": "^1.0.1", "lodash": "^4.1.1", "object.assign": "^4.1.2", - "object.values": "^1.1.2", + "object.values": "^1.1.5", "prop-types": "^15.7.2", "raf": "^3.4.1", - "react-moment-proptypes": "^1.7.0", + "react-moment-proptypes": "^1.8.1", "react-outside-click-handler": "^1.3.0", "react-portal": "^4.2.1", - "react-with-direction": "^1.3.1", + "react-with-direction": "^1.4.0", "react-with-styles": "~4.1.0", "react-with-styles-interface-css": "^6.0.0" }, @@ -132,8 +132,7 @@ "@babel/runtime": "^7.0.0", "moment": "^2.18.1", "react": "^0.14 || ^15.5.4 || ^16.1.1", - "react-dom": "^0.14 || ^15.5.4 || ^16.1.1", - "react-with-direction": "^1.3.1" + "react-dom": "^0.14 || ^15.5.4 || ^16.1.1" }, "engines": { "node": ">=0.10" From 0f5082fe296d74e3d99f9c8fd9d24ed200a15782 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 21 Jan 2022 14:29:06 -0800 Subject: [PATCH 608/618] [meta] update repo URLs --- .storybook-css/config.js | 6 +- .storybook/config.js | 6 +- CHANGELOG.md | 616 +++++++++++------------ INTHEWILD.md | 2 +- README.md | 26 +- package.json | 6 +- stories/DayPickerRangeController.js | 2 +- stories/DayPickerSingleDateController.js | 2 +- stories/PresetDateRangePicker.js | 4 +- 9 files changed, 335 insertions(+), 335 deletions(-) diff --git a/.storybook-css/config.js b/.storybook-css/config.js index d6652020d0..da06e751a8 100644 --- a/.storybook-css/config.js +++ b/.storybook-css/config.js @@ -21,8 +21,8 @@ function getLink(href, text) { return `${text}`; } -const README = getLink('https://github.com/airbnb/react-dates/blob/master/README.md', 'README'); -const wrapperSource = getLink('https://github.com/airbnb/react-dates/tree/master/examples', 'wrapper source'); +const README = getLink('https://github.com/react-dates/react-dates/blob/HEAD/README.md', 'README'); +const wrapperSource = getLink('https://github.com/react-dates/react-dates/tree/HEAD/examples', 'wrapper source'); const helperText = `All examples are built using a wrapper component that is not exported by react-dates. Please see the ${README} for more information about minimal setup or explore @@ -52,7 +52,7 @@ addDecorator(story => ( setOptions({ name: 'REACT-DATES', - url: 'https://github.com/airbnb/react-dates', + url: 'https://github.com/react-dates/react-dates', }); function loadStories() { diff --git a/.storybook/config.js b/.storybook/config.js index 3882b60251..a14004d170 100644 --- a/.storybook/config.js +++ b/.storybook/config.js @@ -27,8 +27,8 @@ function getLink(href, text) { return `${text}`; } -const README = getLink('https://github.com/airbnb/react-dates/blob/master/README.md', 'README'); -const wrapperSource = getLink('https://github.com/airbnb/react-dates/tree/master/examples', 'wrapper source'); +const README = getLink('https://github.com/react-dates/react-dates/blob/HEAD/README.md', 'README'); +const wrapperSource = getLink('https://github.com/react-dates/react-dates/tree/HEAD/examples', 'wrapper source'); const helperText = `All examples are built using a wrapper component that is not exported by react-dates. Please see the ${README} for more information about minimal setup or explore @@ -58,7 +58,7 @@ addDecorator(story => ( setOptions({ name: 'REACT-DATES', - url: 'https://github.com/airbnb/react-dates', + url: 'https://github.com/react-dates/react-dates', }); function loadStories() { diff --git a/CHANGELOG.md b/CHANGELOG.md index f8bf0a1fd5..ede78bcdbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,46 +1,46 @@ # Change Log ## 21.8.0 -- [new] Add option to allow days violating min nights to be clicked ([#1913](https://github.com/airbnb/react-dates/pull/1913)) -- [fix] Prevent vertical scrollable prev nav button from overlapping weekday headers ([#1914](https://github.com/airbnb/react-dates/pull/1914)) +- [new] Add option to allow days violating min nights to be clicked ([#1913](https://github.com/react-dates/react-dates/pull/1913)) +- [fix] Prevent vertical scrollable prev nav button from overlapping weekday headers ([#1914](https://github.com/react-dates/react-dates/pull/1914)) ## 21.7.2 -- [fix] Add tests for previous fix ([#1912](https://github.com/airbnb/react-dates/pull/1912)) +- [fix] Add tests for previous fix ([#1912](https://github.com/react-dates/react-dates/pull/1912)) ## 21.7.1 -- [fix] Fix render conditional in DayPickerKeyboardShortcuts ([#1911](https://github.com/airbnb/react-dates/pull/1911)) +- [fix] Fix render conditional in DayPickerKeyboardShortcuts ([#1911](https://github.com/react-dates/react-dates/pull/1911)) ## 21.7.0 -- [fix] Keep scroll position when prev months rendered on vertical scrollable calendar ([#1902](https://github.com/airbnb/react-dates/pull/1902)) -- [new] Add ability to not show prev or next navigation buttons ([#1900](https://github.com/airbnb/react-dates/pull/1900)) -- [fix] Fix logic for applying default navigation button styling ([#1898](https://github.com/airbnb/react-dates/pull/1898)) +- [fix] Keep scroll position when prev months rendered on vertical scrollable calendar ([#1902](https://github.com/react-dates/react-dates/pull/1902)) +- [new] Add ability to not show prev or next navigation buttons ([#1900](https://github.com/react-dates/react-dates/pull/1900)) +- [fix] Fix logic for applying default navigation button styling ([#1898](https://github.com/react-dates/react-dates/pull/1898)) ## 21.6.0 -- [new] Add functionality to see previous months for vertical scrollable calendar ([#1894](https://github.com/airbnb/react-dates/pull/1894)) -- [new] Add support for noNavButtons in DayPickerSingleDateController (match DayPickerRangeController support) ([#1895](https://github.com/airbnb/react-dates/pull/1895)) +- [new] Add functionality to see previous months for vertical scrollable calendar ([#1894](https://github.com/react-dates/react-dates/pull/1894)) +- [new] Add support for noNavButtons in DayPickerSingleDateController (match DayPickerRangeController support) ([#1895](https://github.com/react-dates/react-dates/pull/1895)) ## 21.5.1 -- [fix] Remove redundant overflowY:scroll on CalendarMonthGrid ([#1881](https://github.com/airbnb/react-dates/pull/1881)) +- [fix] Remove redundant overflowY:scroll on CalendarMonthGrid ([#1881](https://github.com/react-dates/react-dates/pull/1881)) ## 21.5.0 -- [new] Add support for custom month navigation buttons ([#1859](https://github.com/airbnb/react-dates/pull/1859)) +- [new] Add support for custom month navigation buttons ([#1859](https://github.com/react-dates/react-dates/pull/1859)) ## 21.4.0 -- [new] Expose 'after-hovered-start' modifier and add 'before-hovered-end', 'no-selected-start-before-selected-end', 'selected-start-in-hovered-span', 'selected-end-in-hovered-span', 'selected-start-no-selected-end', and 'selected-end-no-selected-start' modifiers [#1608](https://github.com/airbnb/react-dates/pull/1608) -- [fix] Loosen up CustomizeableCalendarDay restriction on children ([#1857](https://github.com/airbnb/react-dates/pull/1857)) +- [new] Expose 'after-hovered-start' modifier and add 'before-hovered-end', 'no-selected-start-before-selected-end', 'selected-start-in-hovered-span', 'selected-end-in-hovered-span', 'selected-start-no-selected-end', and 'selected-end-no-selected-start' modifiers [#1608](https://github.com/react-dates/react-dates/pull/1608) +- [fix] Loosen up CustomizeableCalendarDay restriction on children ([#1857](https://github.com/react-dates/react-dates/pull/1857)) ## 21.3.2 -- [fix] Revert "Call getStateForNewMonth when date/startDate/endDate is set to a date that is not visible" ([#1851](https://github.com/airbnb/react-dates/pull/1851)) +- [fix] Revert "Call getStateForNewMonth when date/startDate/endDate is set to a date that is not visible" ([#1851](https://github.com/react-dates/react-dates/pull/1851)) ## 21.3.1 @@ -48,602 +48,602 @@ ## 21.3.0 -- [new] Update react-with-styles 4.0.1 -> 4.1.0 ([#1843](https://github.com/airbnb/react-dates/pull/1843)) -- [new] Add Input Font-Weight to Default Theme ([#1765](https://github.com/airbnb/react-dates/pull/#1765)) +- [new] Update react-with-styles 4.0.1 -> 4.1.0 ([#1843](https://github.com/react-dates/react-dates/pull/1843)) +- [new] Add Input Font-Weight to Default Theme ([#1765](https://github.com/react-dates/react-dates/pull/#1765)) ## 21.2.1 -- [fix] Call getStateForNewMonth when date/startDate/endDate is set to a date that is not visible ([#1834](https://github.com/airbnb/react-dates/pull/1834)) +- [fix] Call getStateForNewMonth when date/startDate/endDate is set to a date that is not visible ([#1834](https://github.com/react-dates/react-dates/pull/1834)) ## 21.2.0 -- [fix] Revert "Merge pull request [#1758](https://github.com/airbnb/react-dates/pull/1758): Remove all direct imports of css in favor of injected prop" ([#1818](https://github.com/airbnb/react-dates/pull/1818)) -- [fix] Fix for getWeekHeaders(), prevents it from changing state.currentMonth ([#1796](https://github.com/airbnb/react-dates/pull/1796)) -- [new] Add support for positioning month navigation under the calendar ([#1784](https://github.com/airbnb/react-dates/pull/1784)) -- [new] Add minDate and maxDate props to DateRangePicker ([#1793](https://github.com/airbnb/react-dates/pull/1793), [#1794](https://github.com/airbnb/react-dates/pull/1794)) +- [fix] Revert "Merge pull request [#1758](https://github.com/react-dates/react-dates/pull/1758): Remove all direct imports of css in favor of injected prop" ([#1818](https://github.com/react-dates/react-dates/pull/1818)) +- [fix] Fix for getWeekHeaders(), prevents it from changing state.currentMonth ([#1796](https://github.com/react-dates/react-dates/pull/1796)) +- [new] Add support for positioning month navigation under the calendar ([#1784](https://github.com/react-dates/react-dates/pull/1784)) +- [new] Add minDate and maxDate props to DateRangePicker ([#1793](https://github.com/react-dates/react-dates/pull/1793), [#1794](https://github.com/react-dates/react-dates/pull/1794)) ## 21.1.0 -- [fix] `DayPicker`: week headers: use the passed-in moment object’s instance, to support localy ([#1577](https://github.com/airbnb/react-dates/pull/1577)) -- [fix] Combine labelled DayPicker container elements ([#1783](https://github.com/airbnb/react-dates/pull/1783)) -- [new] Add a render function for customizable week header text ([#1787](https://github.com/airbnb/react-dates/pull/1787)) +- [fix] `DayPicker`: week headers: use the passed-in moment object’s instance, to support localy ([#1577](https://github.com/react-dates/react-dates/pull/1577)) +- [fix] Combine labelled DayPicker container elements ([#1783](https://github.com/react-dates/react-dates/pull/1783)) +- [new] Add a render function for customizable week header text ([#1787](https://github.com/react-dates/react-dates/pull/1787)) ## 21.0.1 -- [fix] [deps] Update react-with-styles ^4.0.0 -> ^4.0.1 ([#1781](https://github.com/airbnb/react-dates/pull/1781)) +- [fix] [deps] Update react-with-styles ^4.0.0 -> ^4.0.1 ([#1781](https://github.com/react-dates/react-dates/pull/1781)) ## 21.0.0 -- [breaking] [deps] Update react-with-styles and other deps ([#1761](https://github.com/airbnb/react-dates/pull/1761) -- [new] [deps] Update dependencies related to react-with-styles ([#1775](https://github.com/airbnb/react-dates/pull/1775)) -- [new] `DayPickerSingleDateController`: Add onMultiplyScrollableMonths ([#1770](https://github.com/airbnb/react-dates/pull/1770)) -- [dev] Fix moment date formats ([#1767](https://github.com/airbnb/react-dates/pull/1767)) -- [dev] Fix addons webpack loader, Fix tests ([#1764](https://github.com/airbnb/react-dates/pull/1764)) +- [breaking] [deps] Update react-with-styles and other deps ([#1761](https://github.com/react-dates/react-dates/pull/1761) +- [new] [deps] Update dependencies related to react-with-styles ([#1775](https://github.com/react-dates/react-dates/pull/1775)) +- [new] `DayPickerSingleDateController`: Add onMultiplyScrollableMonths ([#1770](https://github.com/react-dates/react-dates/pull/1770)) +- [dev] Fix moment date formats ([#1767](https://github.com/react-dates/react-dates/pull/1767)) +- [dev] Fix addons webpack loader, Fix tests ([#1764](https://github.com/react-dates/react-dates/pull/1764)) - [dev] build: fix linting (refs eslint/eslint#12119) - [new] [deps] update `@babel/runtime`, `airbnb-prop-types`, `object.values`, `prop-types`, `react-outside-click-handler`, `react-portal` - [dev] [deps] update `@babel/*`, `@storybook/*`, `babel-plugin-inline-svg`, `babel-plugin-istanbul`, `babel-preset-airbnb`, `eslint-config-airbnb` to v18 (plus peer deps), `eslint` to v6; `eslint-plugin-react-with-styles`, `karma`, `karma-firefox-launcher`, `mocha`, `safe-publish-latest`, `sass-loader`, `sinon`, `sinon-sandbox`, `coveralls`, `enzyme-adapter-react-helper` -- [fix] Remove all direct imports of css in favor of injected prop ([#1758](https://github.com/airbnb/react-dates/pull/1758)) +- [fix] Remove all direct imports of css in favor of injected prop ([#1758](https://github.com/react-dates/react-dates/pull/1758)) ## 20.3.0 -- [fix] Optimize setState dayPickerContainerStyles in responsivizePickerPosition ([#1735](https://github.com/airbnb/react-dates/pull/1735)) -- [fix] Stop calendar blinking on DateRangePickerInput focus switch (fixes #1523) ([#1553](https://github.com/airbnb/react-dates/pull/1553)) -- [new] [a11y] Add `aria-roledescription` ([#1746](https://github.com/airbnb/react-dates/pull/1746)) +- [fix] Optimize setState dayPickerContainerStyles in responsivizePickerPosition ([#1735](https://github.com/react-dates/react-dates/pull/1735)) +- [fix] Stop calendar blinking on DateRangePickerInput focus switch (fixes #1523) ([#1553](https://github.com/react-dates/react-dates/pull/1553)) +- [new] [a11y] Add `aria-roledescription` ([#1746](https://github.com/react-dates/react-dates/pull/1746)) ## 20.2.5 -- [fix] Defer day focusing until next animation frame ([#1707](https://github.com/airbnb/react-dates/pull/1707)) -- [fix] Fix startDate style (@mmarkelov, [#1710](https://github.com/airbnb/react-dates/pull/1710)) -- [fix] Pass correct props to SingleDatePicker on close (@AlokTakshak, [#1678](https://github.com/airbnb/react-dates/pull/1678)) -- [dev] Update blocked navigation (min/maxDate) stories (@ianduvall, [#1598](https://github.com/airbnb/react-dates/pull/1598)) -- [dev] Add positioning to custom navigation in stories (@dougmacknz, [#1573](https://github.com/airbnb/react-dates/pull/1573)) -- [dev] Update karma 3 → 4, mocha 3 → 6, nyc 12 → 14, sinon 6 → 7, eslint 5 → 6 ([#1713](https://github.com/airbnb/react-dates/pull/1713), [#1712](https://github.com/airbnb/react-dates/pull/1713)) +- [fix] Defer day focusing until next animation frame ([#1707](https://github.com/react-dates/react-dates/pull/1707)) +- [fix] Fix startDate style (@mmarkelov, [#1710](https://github.com/react-dates/react-dates/pull/1710)) +- [fix] Pass correct props to SingleDatePicker on close (@AlokTakshak, [#1678](https://github.com/react-dates/react-dates/pull/1678)) +- [dev] Update blocked navigation (min/maxDate) stories (@ianduvall, [#1598](https://github.com/react-dates/react-dates/pull/1598)) +- [dev] Add positioning to custom navigation in stories (@dougmacknz, [#1573](https://github.com/react-dates/react-dates/pull/1573)) +- [dev] Update karma 3 → 4, mocha 3 → 6, nyc 12 → 14, sinon 6 → 7, eslint 5 → 6 ([#1713](https://github.com/react-dates/react-dates/pull/1713), [#1712](https://github.com/react-dates/react-dates/pull/1713)) ## 20.2.4 - [fix] Replace react-addons-shallow-compare with enzyme-shallow-equal (bf7e3347702f) -- [fix] Optimize SVG assets ([#1690](https://github.com/airbnb/react-dates/pull/1690)) -- [fix] Update babel-preset-airbnb 3.2.1 -> 4.0.0 ([#1692](https://github.com/airbnb/react-dates/pull/1692)) +- [fix] Optimize SVG assets ([#1690](https://github.com/react-dates/react-dates/pull/1690)) +- [fix] Update babel-preset-airbnb 3.2.1 -> 4.0.0 ([#1692](https://github.com/react-dates/react-dates/pull/1692)) ## 20.2.3 -- [fix] Add guard for undefined objects in deleteModifier ([#1687](https://github.com/airbnb/react-dates/pull/1687)) -- [dev] Update Storybook from v4 to v5 ([@trotzig](https://github.com/trotzig) [#1673](https://github.com/airbnb/react-dates/pull/1673)) +- [fix] Add guard for undefined objects in deleteModifier ([#1687](https://github.com/react-dates/react-dates/pull/1687)) +- [dev] Update Storybook from v4 to v5 ([@trotzig](https://github.com/trotzig) [#1673](https://github.com/react-dates/react-dates/pull/1673)) ## 20.2.2 -- [fix] Add guard for undefined objects in addModifier ([#1667](https://github.com/airbnb/react-dates/pull/1667)) +- [fix] Add guard for undefined objects in addModifier ([#1667](https://github.com/react-dates/react-dates/pull/1667)) ## 20.2.1 -- [fix] Compile classes in loose mode ([#1655](https://github.com/airbnb/react-dates/pull/1655)) -- [fix] Performance optimizations ([#1656](https://github.com/airbnb/react-dates/pull/1656), [#1657](https://github.com/airbnb/react-dates/pull/1657), [#1659](https://github.com/airbnb/react-dates/pull/1659), [#1661](https://github.com/airbnb/react-dates/pull/1661), [#1662](https://github.com/airbnb/react-dates/pull/1662), and [#1663](https://github.com/airbnb/react-dates/pull/1663)) -- [docs] Add `initialVisibleMonth` to `DayPickerRangeController` in readme ([@AlokTakshak](https://github.com/AlokTakshak) [1652](https://github.com/airbnb/react-dates/pull/1652)) +- [fix] Compile classes in loose mode ([#1655](https://github.com/react-dates/react-dates/pull/1655)) +- [fix] Performance optimizations ([#1656](https://github.com/react-dates/react-dates/pull/1656), [#1657](https://github.com/react-dates/react-dates/pull/1657), [#1659](https://github.com/react-dates/react-dates/pull/1659), [#1661](https://github.com/react-dates/react-dates/pull/1661), [#1662](https://github.com/react-dates/react-dates/pull/1662), and [#1663](https://github.com/react-dates/react-dates/pull/1663)) +- [docs] Add `initialVisibleMonth` to `DayPickerRangeController` in readme ([@AlokTakshak](https://github.com/AlokTakshak) [1652](https://github.com/react-dates/react-dates/pull/1652)) ## 20.2.0 -- [fix] Fix date selection in the SDP ([#1530](https://github.com/airbnb/react-dates/pull/1530)) -- [new] Add explicit aria label props ([#1594](https://github.com/airbnb/react-dates/pull/1594)) +- [fix] Fix date selection in the SDP ([#1530](https://github.com/react-dates/react-dates/pull/1530)) +- [new] Add explicit aria label props ([#1594](https://github.com/react-dates/react-dates/pull/1594)) ## 20.1.0 -- [new] Add `renderKeyboardShortcutsButton` prop ([#1576](https://github.com/airbnb/react-dates/pull/1576)) +- [new] Add `renderKeyboardShortcutsButton` prop ([#1576](https://github.com/react-dates/react-dates/pull/1576)) ## 20.0.0 -- [breaking] Omit tabindex prop from calendar navigation buttons when custom buttons are supplied ([#1563](https://github.com/airbnb/react-dates/pull/1563)) -- [new] Add hovered-start-blocked-minimun-nights and hovered-start-first-possible-end modifiers ([#1547](https://github.com/airbnb/react-dates/pull/1547)) -- [fix] clearTimeout added for the setCalendarMonthGridHeight timeout ([#1468](https://github.com/airbnb/react-dates/pull/1468)) -- [fix] Remove default styles for the last-in-range modifier ([#1538](https://github.com/airbnb/react-dates/pull/1538)) +- [breaking] Omit tabindex prop from calendar navigation buttons when custom buttons are supplied ([#1563](https://github.com/react-dates/react-dates/pull/1563)) +- [new] Add hovered-start-blocked-minimun-nights and hovered-start-first-possible-end modifiers ([#1547](https://github.com/react-dates/react-dates/pull/1547)) +- [fix] clearTimeout added for the setCalendarMonthGridHeight timeout ([#1468](https://github.com/react-dates/react-dates/pull/1468)) +- [fix] Remove default styles for the last-in-range modifier ([#1538](https://github.com/react-dates/react-dates/pull/1538)) ## 19.0.4 -- [fix] Added `selected` aria label to dates in the selected range ([#1555](https://github.com/airbnb/react-dates/pull/1555)) +- [fix] Added `selected` aria label to dates in the selected range ([#1555](https://github.com/react-dates/react-dates/pull/1555)) ## 19.0.3 -- [fix] Fix date selection in the SDP ([#1540](https://github.com/airbnb/react-dates/pull/1540)) +- [fix] Fix date selection in the SDP ([#1540](https://github.com/react-dates/react-dates/pull/1540)) ## 19.0.2 - no changes; extraneous publish ## 19.0.1 -- [fix] Fix single date picker not responding to input ([#1533](https://github.com/airbnb/react-dates/pull/1533)) -- [fix] Fixes the focus out event in IE11 ([#1524](https://github.com/airbnb/react-dates/pull/1524)) +- [fix] Fix single date picker not responding to input ([#1533](https://github.com/react-dates/react-dates/pull/1533)) +- [fix] Fixes the focus out event in IE11 ([#1524](https://github.com/react-dates/react-dates/pull/1524)) ## 19.0.0 -- [breaking] Call `onDatesChange` before `onFocusChange` in the DRP ([#1525](https://github.com/airbnb/react-dates/pull/1525)) +- [breaking] Call `onDatesChange` before `onFocusChange` in the DRP ([#1525](https://github.com/react-dates/react-dates/pull/1525)) ## 18.5.0 -- [fix] Add `aria-disabled` attribute to the (Customizable)CalendarDay ([#1521](https://github.com/airbnb/react-dates/pull/1521)) -- [new] Add `startDateOffset` and `endDateOffset` props to the DRP ([#1252](https://github.com/airbnb/react-dates/pull/1252)) +- [fix] Add `aria-disabled` attribute to the (Customizable)CalendarDay ([#1521](https://github.com/react-dates/react-dates/pull/1521)) +- [new] Add `startDateOffset` and `endDateOffset` props to the DRP ([#1252](https://github.com/react-dates/react-dates/pull/1252)) ## 18.4.1 -- [fix] Make DRP and SDP calendars tabbable from the inputs ([#1499](https://github.com/airbnb/react-dates/pull/1499)) +- [fix] Make DRP and SDP calendars tabbable from the inputs ([#1499](https://github.com/react-dates/react-dates/pull/1499)) ## 18.4.0 -- [new] Clarify VoiceOver text for dates selected as start-date and end-date ([#1501](https://github.com/airbnb/react-dates/pull/1501)) +- [new] Clarify VoiceOver text for dates selected as start-date and end-date ([#1501](https://github.com/react-dates/react-dates/pull/1501)) ## 18.3.1 -- [fix][RTL] Fix the SDP and DRP noflip util function ([#1492](https://github.com/airbnb/react-dates/pull/1492)) +- [fix][RTL] Fix the SDP and DRP noflip util function ([#1492](https://github.com/react-dates/react-dates/pull/1492)) ## 18.3.0 -- [fix] Update the SDP and DRP to be compatible with `react-with-direction` ([#1482](https://github.com/airbnb/react-dates/pull/1482)) -- [new] Add `minDate` and `maxDate` props to block month navigation ([#1311](https://github.com/airbnb/react-dates/pull/1311)) -- [fix][a11y] Remove space/enter onKeyDown handling for open/close keyboard shortcuts panel ([#1464](https://github.com/airbnb/react-dates/pull/1464)) -- [fix][a11y] Fix lack of visible focus in Firefox and IE on "?" keyboard shortcuts button ([#1463](https://github.com/airbnb/react-dates/pull/1463)) +- [fix] Update the SDP and DRP to be compatible with `react-with-direction` ([#1482](https://github.com/react-dates/react-dates/pull/1482)) +- [new] Add `minDate` and `maxDate` props to block month navigation ([#1311](https://github.com/react-dates/react-dates/pull/1311)) +- [fix][a11y] Remove space/enter onKeyDown handling for open/close keyboard shortcuts panel ([#1464](https://github.com/react-dates/react-dates/pull/1464)) +- [fix][a11y] Fix lack of visible focus in Firefox and IE on "?" keyboard shortcuts button ([#1463](https://github.com/react-dates/react-dates/pull/1463)) ## 18.2.2 -- [fix] Conditionally apply the `shouldComponentUpdate` method in the babel transform ([#1457](https://github.com/airbnb/react-dates/pull/1457)) -- [fix] Fix incorrect VO for selected check-in date ([#1451](https://github.com/airbnb/react-dates/pull/1451)) +- [fix] Conditionally apply the `shouldComponentUpdate` method in the babel transform ([#1457](https://github.com/react-dates/react-dates/pull/1457)) +- [fix] Fix incorrect VO for selected check-in date ([#1451](https://github.com/react-dates/react-dates/pull/1451)) ## 18.2.1 -- [fix] Add babel transform to handle PureComponent fallbacks ([#1452](https://github.com/airbnb/react-dates/pull/1452)) +- [fix] Add babel transform to handle PureComponent fallbacks ([#1452](https://github.com/react-dates/react-dates/pull/1452)) ## 18.2.0 -- [new] Add `onTab`/`onShiftTab` callbacks to the DayPicker (and its controllers) ([#1427](https://github.com/airbnb/react-dates/pull/1427)) +- [new] Add `onTab`/`onShiftTab` callbacks to the DayPicker (and its controllers) ([#1427](https://github.com/react-dates/react-dates/pull/1427)) ## 18.1.1 -- [fix] Prevent outside range dates from being selected by typing ([#1370](https://github.com/airbnb/react-dates/pull/1370)) +- [fix] Prevent outside range dates from being selected by typing ([#1370](https://github.com/react-dates/react-dates/pull/1370)) ## 18.1.0 -- [fix] Default the `calendarMonthPadding` theme variable to 0 when undefined ([#1355](https://github.com/airbnb/react-dates/pull/1355)) -- [new] Add `SingleDatePickerInputController` component ([#1360](https://github.com/airbnb/react-dates/pull/1360)) -- [new] Add `horizontalMonthPadding` as a prop to the DRP ([#1364](https://github.com/airbnb/react-dates/pull/1364)) -- [fix] Fix unnecessary rerender in `DayPickerNavigation` ([#1363](https://github.com/airbnb/react-dates/pull/1363)) +- [fix] Default the `calendarMonthPadding` theme variable to 0 when undefined ([#1355](https://github.com/react-dates/react-dates/pull/1355)) +- [new] Add `SingleDatePickerInputController` component ([#1360](https://github.com/react-dates/react-dates/pull/1360)) +- [new] Add `horizontalMonthPadding` as a prop to the DRP ([#1364](https://github.com/react-dates/react-dates/pull/1364)) +- [fix] Fix unnecessary rerender in `DayPickerNavigation` ([#1363](https://github.com/react-dates/react-dates/pull/1363)) ## 18.0.4 -- [fix] revert 'revert 'Conditionally use `PureComponent` instead of `Component`'' ([4f8eb01](https://github.com/airbnb/react-dates/commit/4f8eb01168ef6c4ae7d74e95ad14acb28960e43e)) +- [fix] revert 'revert 'Conditionally use `PureComponent` instead of `Component`'' ([4f8eb01](https://github.com/react-dates/react-dates/commit/4f8eb01168ef6c4ae7d74e95ad14acb28960e43e)) ## 18.0.3 -- [fix] revert 'Conditionally use `PureComponent` instead of `Component`' ([50c382f](https://github.com/airbnb/react-dates/commit/50c382f7cf3e3ba60f4fdaa00eae53cf06d3c97b)) +- [fix] revert 'Conditionally use `PureComponent` instead of `Component`' ([50c382f](https://github.com/react-dates/react-dates/commit/50c382f7cf3e3ba60f4fdaa00eae53cf06d3c97b)) ## 18.0.2 -- [fix] Remove svgo from "inline-react-svg" babel plugin options ([#1350](https://github.com/airbnb/react-dates/pull/1350)) +- [fix] Remove svgo from "inline-react-svg" babel plugin options ([#1350](https://github.com/react-dates/react-dates/pull/1350)) ## 18.0.1 -- [fix] Center vertical month navigation ([#1347](https://github.com/airbnb/react-dates/pull/1347)) +- [fix] Center vertical month navigation ([#1347](https://github.com/react-dates/react-dates/pull/1347)) ## 18.0.0 -- [fix] Conditionally use `PureComponent` instead of `Component` ([#1335](https://github.com/airbnb/react-dates/pull/1335)) -- [breaking] Remove propTypes in production ([#1322](https://github.com/airbnb/react-dates/pull/1322)) -- [fix] Change border-styles to minimise overlap ([#1328](https://github.com/airbnb/react-dates/pull/1328)) -- [fix] Only blur the `activeElement` when available ([#1345](https://github.com/airbnb/react-dates/pull/1345)) +- [fix] Conditionally use `PureComponent` instead of `Component` ([#1335](https://github.com/react-dates/react-dates/pull/1335)) +- [breaking] Remove propTypes in production ([#1322](https://github.com/react-dates/react-dates/pull/1322)) +- [fix] Change border-styles to minimise overlap ([#1328](https://github.com/react-dates/react-dates/pull/1328)) +- [fix] Only blur the `activeElement` when available ([#1345](https://github.com/react-dates/react-dates/pull/1345)) ## 17.2.0 -- [fix] Add modifiers for next months in the vertical scrollable datepickers ([#1293](https://github.com/airbnb/react-dates/pull/1293)) -- [fix] Fix cursor jumping to the end of the controlled input when typing ([#1287](https://github.com/airbnb/react-dates/pull/1287)) -- [new] Add `horizontalMonthPadding` prop and `dayPickerHorizontalPadding` and `noScrollBarOnVerticalScrollable` theme variables ([#1298](https://github.com/airbnb/react-dates/pull/1298)) -- [fix] Fix issue where custom month navigation was not clickable in FF ([#1305](https://github.com/airbnb/react-dates/pull/1305)) +- [fix] Add modifiers for next months in the vertical scrollable datepickers ([#1293](https://github.com/react-dates/react-dates/pull/1293)) +- [fix] Fix cursor jumping to the end of the controlled input when typing ([#1287](https://github.com/react-dates/react-dates/pull/1287)) +- [new] Add `horizontalMonthPadding` prop and `dayPickerHorizontalPadding` and `noScrollBarOnVerticalScrollable` theme variables ([#1298](https://github.com/react-dates/react-dates/pull/1298)) +- [fix] Fix issue where custom month navigation was not clickable in FF ([#1305](https://github.com/react-dates/react-dates/pull/1305)) ## 17.1.1 -- [fix] Set `DayPickerNavigation__horizontal` height to zero ([#1265](https://github.com/airbnb/react-dates/pull/1265)) +- [fix] Set `DayPickerNavigation__horizontal` height to zero ([#1265](https://github.com/react-dates/react-dates/pull/1265)) ## 17.1.0 -- [new] Add `ModifiersShape` and use throughout the codebase ([#1231](https://github.com/airbnb/react-dates/pull/1231)) -- [fix] Fix minimum nights `blocked` modifiers being applied incorrectly ([#1259](https://github.com/airbnb/react-dates/pull/1259)) -- [fix] Update conditions where `adjustDayPickerHeight` is called ([#1241](https://github.com/airbnb/react-dates/pull/1241)) -- [fix] Do not render `OutsideClickHandler` unnecessarily ([#1256](https://github.com/airbnb/react-dates/pull/1256)) +- [new] Add `ModifiersShape` and use throughout the codebase ([#1231](https://github.com/react-dates/react-dates/pull/1231)) +- [fix] Fix minimum nights `blocked` modifiers being applied incorrectly ([#1259](https://github.com/react-dates/react-dates/pull/1259)) +- [fix] Update conditions where `adjustDayPickerHeight` is called ([#1241](https://github.com/react-dates/react-dates/pull/1241)) +- [fix] Do not render `OutsideClickHandler` unnecessarily ([#1256](https://github.com/react-dates/react-dates/pull/1256)) ## 17.0.0 -- [fix] Replace dumb quotes with smart quotes in default phrases ([#1168](https://github.com/airbnb/react-dates/pull/1168)) -- [fix] Fix outside day movement on hover ([#1178](https://github.com/airbnb/react-dates/pull/1178)) -- [fix] Add `focusable="false"` to SVGs ([#1190](https://github.com/airbnb/react-dates/pull/1190)) -- [fix] Use `react-outside-click-handler` instead of the internal component ([#1191](https://github.com/airbnb/react-dates/pull/1191)) -- [breaking] Change the way month heights are calculated and update the name of some `CalendarMonth`/`CalendarMonthGrid` props ([#1192](https://github.com/airbnb/react-dates/pull/1192)) -- [new] Pass nextMonth to `onPrevMonthClick`/`onNextMonthClick` ([#1207](https://github.com/airbnb/react-dates/pull/1207)) -- [new] Allow input border styles to be overridden in the theme ([#1201](https://github.com/airbnb/react-dates/pull/1201)) -- [new] Allow consolidated-events@2.0.0 ([#1218](https://github.com/airbnb/react-dates/pull/1218)) -- [fix] Remove input.blur() call to fix a focus trap in Safari/IE ([#1214](https://github.com/airbnb/react-dates/pull/1214)) -- [new] Add support for month/year transitions ([#1106](https://github.com/airbnb/react-dates/pull/1106)) -- [breaking] Rename renderMonth=>renderMonthText and renderCaption=>renderMonthElement ([#1220](https://github.com/airbnb/react-dates/pull/1220)) -- [breaking] Remove default styling *completely* from the `navNext`/`navPrev` props, including position ([#1204](https://github.com/airbnb/react-dates/pull/1204)) -- [fix] Fix propType warnings for `onMonthChange`/`onYearChange` ([#1222](https://github.com/airbnb/react-dates/pull/1222)) -- [breaking] Remove `OutsideClickHandler` export entirely ([#1225](https://github.com/airbnb/react-dates/pull/1225)) +- [fix] Replace dumb quotes with smart quotes in default phrases ([#1168](https://github.com/react-dates/react-dates/pull/1168)) +- [fix] Fix outside day movement on hover ([#1178](https://github.com/react-dates/react-dates/pull/1178)) +- [fix] Add `focusable="false"` to SVGs ([#1190](https://github.com/react-dates/react-dates/pull/1190)) +- [fix] Use `react-outside-click-handler` instead of the internal component ([#1191](https://github.com/react-dates/react-dates/pull/1191)) +- [breaking] Change the way month heights are calculated and update the name of some `CalendarMonth`/`CalendarMonthGrid` props ([#1192](https://github.com/react-dates/react-dates/pull/1192)) +- [new] Pass nextMonth to `onPrevMonthClick`/`onNextMonthClick` ([#1207](https://github.com/react-dates/react-dates/pull/1207)) +- [new] Allow input border styles to be overridden in the theme ([#1201](https://github.com/react-dates/react-dates/pull/1201)) +- [new] Allow consolidated-events@2.0.0 ([#1218](https://github.com/react-dates/react-dates/pull/1218)) +- [fix] Remove input.blur() call to fix a focus trap in Safari/IE ([#1214](https://github.com/react-dates/react-dates/pull/1214)) +- [new] Add support for month/year transitions ([#1106](https://github.com/react-dates/react-dates/pull/1106)) +- [breaking] Rename renderMonth=>renderMonthText and renderCaption=>renderMonthElement ([#1220](https://github.com/react-dates/react-dates/pull/1220)) +- [breaking] Remove default styling *completely* from the `navNext`/`navPrev` props, including position ([#1204](https://github.com/react-dates/react-dates/pull/1204)) +- [fix] Fix propType warnings for `onMonthChange`/`onYearChange` ([#1222](https://github.com/react-dates/react-dates/pull/1222)) +- [breaking] Remove `OutsideClickHandler` export entirely ([#1225](https://github.com/react-dates/react-dates/pull/1225)) ## 16.7.1 -- [fix] react-with-styles v3 requires react-with-direction as a peer dep; this provides it but forwards the peer dep requirement ([#1348](https://github.com/airbnb/react-dates/issues/1348)) +- [fix] react-with-styles v3 requires react-with-direction as a peer dep; this provides it but forwards the peer dep requirement ([#1348](https://github.com/react-dates/react-dates/issues/1348)) ## 16.7.0 -- [fix] Force border-radius to be 0 on the inputs ([#1157](https://github.com/airbnb/react-dates/pull/1157)) -- [fix] Clear previous min nights modifiers, not current ([#994](https://github.com/airbnb/react-dates/pull/994)) -- [fix] Tweak default input styling ([#1158](https://github.com/airbnb/react-dates/pull/1158)) -- [fix] Round transform3d values to fix font blur ([#1155](https://github.com/airbnb/react-dates/pull/1155)) -- [new] Add `noNavButtons` prop ([#1160](https://github.com/airbnb/react-dates/pull/1160)) +- [fix] Force border-radius to be 0 on the inputs ([#1157](https://github.com/react-dates/react-dates/pull/1157)) +- [fix] Clear previous min nights modifiers, not current ([#994](https://github.com/react-dates/react-dates/pull/994)) +- [fix] Tweak default input styling ([#1158](https://github.com/react-dates/react-dates/pull/1158)) +- [fix] Round transform3d values to fix font blur ([#1155](https://github.com/react-dates/react-dates/pull/1155)) +- [new] Add `noNavButtons` prop ([#1160](https://github.com/react-dates/react-dates/pull/1160)) ## 16.6.1 -- [fix] Fix selective disabling of the `DateRangePicker` ([#1116](https://github.com/airbnb/react-dates/pull/1116)) -- [fix] Fix `onOutsideClick` refactor ([#1115](https://github.com/airbnb/react-dates/pull/1115)) +- [fix] Fix selective disabling of the `DateRangePicker` ([#1116](https://github.com/react-dates/react-dates/pull/1116)) +- [fix] Fix `onOutsideClick` refactor ([#1115](https://github.com/react-dates/react-dates/pull/1115)) ## 16.6.0 -- [new] Add `appendToBody`/`disableScroll` props ([#1069](https://github.com/airbnb/react-dates/pull/1069)) -- [fix] Address unexpected blur call ([#1107](https://github.com/airbnb/react-dates/pull/1107)) -- [new] Add `verticalBorderSpacing` prop to `DayPickerSingleDateController`/`DayPickerRangeController` ([#1096](https://github.com/airbnb/react-dates/pull/1096)) -- [fix] Move focus to `DayPicker` when readOnly is true ([#961](https://github.com/airbnb/react-dates/pull/961)) +- [new] Add `appendToBody`/`disableScroll` props ([#1069](https://github.com/react-dates/react-dates/pull/1069)) +- [fix] Address unexpected blur call ([#1107](https://github.com/react-dates/react-dates/pull/1107)) +- [new] Add `verticalBorderSpacing` prop to `DayPickerSingleDateController`/`DayPickerRangeController` ([#1096](https://github.com/react-dates/react-dates/pull/1096)) +- [fix] Move focus to `DayPicker` when readOnly is true ([#961](https://github.com/react-dates/react-dates/pull/961)) ## 16.5.0 -- [new] Export `CustomizeableCalendarDay` default styles ([#1095](https://github.com/airbnb/react-dates/pull/1095)) -- [new] Allow selectively disabling either input in the DRP ([#](https://github.com/airbnb/react-dates/pull/606)) -- [new] Add `dayAriaLabelFormat` prop to the SDP/DRP ([#](https://github.com/airbnb/react-dates/pull/984)) +- [new] Export `CustomizeableCalendarDay` default styles ([#1095](https://github.com/react-dates/react-dates/pull/1095)) +- [new] Allow selectively disabling either input in the DRP ([#](https://github.com/react-dates/react-dates/pull/606)) +- [new] Add `dayAriaLabelFormat` prop to the SDP/DRP ([#](https://github.com/react-dates/react-dates/pull/984)) ## 16.4.0 -- [new] Export `OutsideClickHandler` in index.js ([#1089](https://github.com/airbnb/react-dates/pull/1089)) -- [fix] Do not apply `verticalSpacing` when `withPortal` or `withFullScreenPortal` is true ([#980](https://github.com/airbnb/react-dates/pull/980)) -- [fix] Handle minimum nights when selecting `startDate` ([#1015](https://github.com/airbnb/react-dates/pull/1015)) -- [fix] Fix style of `CloseIcon` in the SDP ([#1058](https://github.com/airbnb/react-dates/pull/1058)) +- [new] Export `OutsideClickHandler` in index.js ([#1089](https://github.com/react-dates/react-dates/pull/1089)) +- [fix] Do not apply `verticalSpacing` when `withPortal` or `withFullScreenPortal` is true ([#980](https://github.com/react-dates/react-dates/pull/980)) +- [fix] Handle minimum nights when selecting `startDate` ([#1015](https://github.com/react-dates/react-dates/pull/1015)) +- [fix] Fix style of `CloseIcon` in the SDP ([#1058](https://github.com/react-dates/react-dates/pull/1058)) ## 16.3.6 -- [fix] Address width issues for vertical DayPickers ([#1055](https://github.com/airbnb/react-dates/pull/1055)) +- [fix] Address width issues for vertical DayPickers ([#1055](https://github.com/react-dates/react-dates/pull/1055)) ## 16.3.5 (I promise this one is good) -- [fix] Includes all necessary CSS ([c965348](https://github.com/airbnb/react-dates/commit/c96534896d8fe5c28ddc1f1090ef43dfaeebb5d6)) +- [fix] Includes all necessary CSS ([c965348](https://github.com/react-dates/react-dates/commit/c96534896d8fe5c28ddc1f1090ef43dfaeebb5d6)) ## 16.3.4 - [fix] Bumps all the RWS libraries again, now with less breakage! ## 16.3.3 -- [revert] Reverts 'Bump react-with-style-interface-css dependency ([#1043](https://github.com/airbnb/react-dates/pull/1043))' +- [revert] Reverts 'Bump react-with-style-interface-css dependency ([#1043](https://github.com/react-dates/react-dates/pull/1043))' ## 16.3.2 -- [revert] Reverts 'Bump react-with-styles dependency ([#1041](https://github.com/airbnb/react-dates/pull/1041))' +- [revert] Reverts 'Bump react-with-styles dependency ([#1041](https://github.com/react-dates/react-dates/pull/1041))' ## 16.3.1 -- [deps] Bump react-with-styles dependency ([#1041](https://github.com/airbnb/react-dates/pull/1041)) -- [deps] Bump react-with-style-interface-css dependency ([#1043](https://github.com/airbnb/react-dates/pull/1043)) +- [deps] Bump react-with-styles dependency ([#1041](https://github.com/react-dates/react-dates/pull/1041)) +- [deps] Bump react-with-style-interface-css dependency ([#1043](https://github.com/react-dates/react-dates/pull/1043)) ## 16.3.0 -- [new] customInfoPanel position prop ([#989](https://github.com/airbnb/react-dates/pull/989)) -- [fix] Fix CustomizableCalendarDay selected/selected-start/selected-end specificity issues ([#979](https://github.com/airbnb/react-dates/pull/979)) -- [fix] Add modifiers for `firstDayOfWeek` and `lastDayOfWeek` ([#988](https://github.com/airbnb/react-dates/pull/988)) -- [fix] Ensure callbacks only trigger after state has been updates ([#990](https://github.com/airbnb/react-dates/pull/990)) +- [new] customInfoPanel position prop ([#989](https://github.com/react-dates/react-dates/pull/989)) +- [fix] Fix CustomizableCalendarDay selected/selected-start/selected-end specificity issues ([#979](https://github.com/react-dates/react-dates/pull/979)) +- [fix] Add modifiers for `firstDayOfWeek` and `lastDayOfWeek` ([#988](https://github.com/react-dates/react-dates/pull/988)) +- [fix] Ensure callbacks only trigger after state has been updates ([#990](https://github.com/react-dates/react-dates/pull/990)) ## 16.2.1 -- [fix] SDP `block` styling also makes the input full width ([#972](https://github.com/airbnb/react-dates/pull/972)) +- [fix] SDP `block` styling also makes the input full width ([#972](https://github.com/react-dates/react-dates/pull/972)) ## 16.2.0 -- [new] Add `startDateOffset`/`endDateOffset` props to `DayPickerRangeController` ([#884](https://github.com/airbnb/react-dates/pull/884)) -- [fix] Make all styles inline in `CustomizableCalendarDay` ([#964](https://github.com/airbnb/react-dates/pull/964)) +- [new] Add `startDateOffset`/`endDateOffset` props to `DayPickerRangeController` ([#884](https://github.com/react-dates/react-dates/pull/884)) +- [fix] Make all styles inline in `CustomizableCalendarDay` ([#964](https://github.com/react-dates/react-dates/pull/964)) ## 16.1.1 -- [fix] Address some small bugs with `CustomizableCalendarDay` ([#962](https://github.com/airbnb/react-dates/pull/962)) +- [fix] Address some small bugs with `CustomizableCalendarDay` ([#962](https://github.com/react-dates/react-dates/pull/962)) ## 16.1.0 -- [fix] Allow for changing of the input value via highlight and replace ([#955](https://github.com/airbnb/react-dates/pull/955)) -- [fix] Fix OPEN_UP styling ([#925](https://github.com/airbnb/react-dates/pull/925)) -- [fix] Don't read invisible months to the screen reader ([#940](https://github.com/airbnb/react-dates/pull/940)) -- [new] Add phrase for aria-label for the selected day ([#905](https://github.com/airbnb/react-dates/pull/905)) +- [fix] Allow for changing of the input value via highlight and replace ([#955](https://github.com/react-dates/react-dates/pull/955)) +- [fix] Fix OPEN_UP styling ([#925](https://github.com/react-dates/react-dates/pull/925)) +- [fix] Don't read invisible months to the screen reader ([#940](https://github.com/react-dates/react-dates/pull/940)) +- [new] Add phrase for aria-label for the selected day ([#905](https://github.com/react-dates/react-dates/pull/905)) ## 16.0.2 -- [fix] Fix keyboard navigation issues ([#916](https://github.com/airbnb/react-dates/pull/916)) -- [fix] Fix React warnings when events are referenced later ([#682](https://github.com/airbnb/react-dates/pull/682)) +- [fix] Fix keyboard navigation issues ([#916](https://github.com/react-dates/react-dates/pull/916)) +- [fix] Fix React warnings when events are referenced later ([#682](https://github.com/react-dates/react-dates/pull/682)) ## 16.0.1 -- [fix] Add back missing onKeyDown method to `CalendarDay` ([#901](https://github.com/airbnb/react-dates/pull/901)) +- [fix] Add back missing onKeyDown method to `CalendarDay` ([#901](https://github.com/react-dates/react-dates/pull/901)) ## 16.0.0 -- [breaking] Simplify `CalendarDay` component ([#894](https://github.com/airbnb/react-dates/pull/894)) -- [breaking] rename `renderDay` prop to `renderDayContents` ([#894](https://github.com/airbnb/react-dates/pull/894)) -- [new] Add `renderCalendarDay` component to allow for easy one-off customization of `CalendarDay` ([#894](https://github.com/airbnb/react-dates/pull/894)) +- [breaking] Simplify `CalendarDay` component ([#894](https://github.com/react-dates/react-dates/pull/894)) +- [breaking] rename `renderDay` prop to `renderDayContents` ([#894](https://github.com/react-dates/react-dates/pull/894)) +- [new] Add `renderCalendarDay` component to allow for easy one-off customization of `CalendarDay` ([#894](https://github.com/react-dates/react-dates/pull/894)) ## 15.5.2 -- revert [#866](https://github.com/airbnb/react-dates/pull/866); it turned out to be semver-major +- revert [#866](https://github.com/react-dates/react-dates/pull/866); it turned out to be semver-major ## 15.5.1 -- [fix] Adjust `small` variant height to be 36px ([#892](https://github.com/airbnb/react-dates/pull/892)) +- [fix] Adjust `small` variant height to be 36px ([#892](https://github.com/react-dates/react-dates/pull/892)) ## 15.5.0 -- [new] Add `small` variant ([#891](https://github.com/airbnb/react-dates/pull/891)) +- [new] Add `small` variant ([#891](https://github.com/react-dates/react-dates/pull/891)) ## 15.4.0 -- [fix] Set font sizes according to theme variable ([#885](https://github.com/airbnb/react-dates/pull/885)) -- [new] Add `verticalSpacing` prop ([#883](https://github.com/airbnb/react-dates/pull/883)) +- [fix] Set font sizes according to theme variable ([#885](https://github.com/react-dates/react-dates/pull/885)) +- [new] Add `verticalSpacing` prop ([#883](https://github.com/react-dates/react-dates/pull/883)) ## 15.3.0 -- [new] Add `transitionDuration` prop ([#865](https://github.com/airbnb/react-dates/pull/865)) -- [fix] Remove default prop values for required startDateId and endDateId props ([#866](https://github.com/airbnb/react-dates/pull/866)) -- [new] Add `block` styling prop ([#871](https://github.com/airbnb/react-dates/pull/871)) -- [new] Add `noBorder` prop to `DayPicker` variations ([#869](https://github.com/airbnb/react-dates/pull/869)) -- [new] Add `noBorder` prop to inputs ([#870](https://github.com/airbnb/react-dates/pull/870)) -- [fix] Remove unused width style in `KeyboardShortcutsRow` ([#867](https://github.com/airbnb/react-dates/pull/867)) +- [new] Add `transitionDuration` prop ([#865](https://github.com/react-dates/react-dates/pull/865)) +- [fix] Remove default prop values for required startDateId and endDateId props ([#866](https://github.com/react-dates/react-dates/pull/866)) +- [new] Add `block` styling prop ([#871](https://github.com/react-dates/react-dates/pull/871)) +- [new] Add `noBorder` prop to `DayPicker` variations ([#869](https://github.com/react-dates/react-dates/pull/869)) +- [new] Add `noBorder` prop to inputs ([#870](https://github.com/react-dates/react-dates/pull/870)) +- [fix] Remove unused width style in `KeyboardShortcutsRow` ([#867](https://github.com/react-dates/react-dates/pull/867)) ## 15.2.1 - [fix] Republish `_datepicker.css` ## 15.2.0 -- [new] Add back today modifier (and class) ([#861](https://github.com/airbnb/react-dates/pull/861)) -- [new] Add ariaLabelFormat prop to CalendarDay ([#842](https://github.com/airbnb/react-dates/pull/842), [#857](https://github.com/airbnb/react-dates/pull/857)) -- [fix] Reset `after-hover-start` in `componentWillReceiveProps` instead of only on click ([#843](https://github.com/airbnb/react-dates/pull/843)) -- [fix] Use `color.background` variable instead of hardcoded #fff for theming ([#852](https://github.com/airbnb/react-dates/pull/852)) -- [fix] Update CalendarMonthGrid months based on locale change ([#795](https://github.com/airbnb/react-dates/pull/795)) +- [new] Add back today modifier (and class) ([#861](https://github.com/react-dates/react-dates/pull/861)) +- [new] Add ariaLabelFormat prop to CalendarDay ([#842](https://github.com/react-dates/react-dates/pull/842), [#857](https://github.com/react-dates/react-dates/pull/857)) +- [fix] Reset `after-hover-start` in `componentWillReceiveProps` instead of only on click ([#843](https://github.com/react-dates/react-dates/pull/843)) +- [fix] Use `color.background` variable instead of hardcoded #fff for theming ([#852](https://github.com/react-dates/react-dates/pull/852)) +- [fix] Update CalendarMonthGrid months based on locale change ([#795](https://github.com/react-dates/react-dates/pull/795)) ## 15.1.0 -- [fix] Add explicit border-radius on KeyboardShortcuts button ([#792](https://github.com/airbnb/react-dates/pull/792)) -- [fix] Pass onClose from SingleDatePicker to DayPickerSingleDateController ([#816](https://github.com/airbnb/react-dates/pull/816)) -- [new] Pass modifiers to `renderDay` as second arg ([#829](https://github.com/airbnb/react-dates/pull/829)) -- [fix] Fix KeyboardShortcutsPanel focus issues ([#825](https://github.com/airbnb/react-dates/pull/825)) +- [fix] Add explicit border-radius on KeyboardShortcuts button ([#792](https://github.com/react-dates/react-dates/pull/792)) +- [fix] Pass onClose from SingleDatePicker to DayPickerSingleDateController ([#816](https://github.com/react-dates/react-dates/pull/816)) +- [new] Pass modifiers to `renderDay` as second arg ([#829](https://github.com/react-dates/react-dates/pull/829)) +- [fix] Fix KeyboardShortcutsPanel focus issues ([#825](https://github.com/react-dates/react-dates/pull/825)) ## 15.0.0 -- [breaking] Rename SDP keydown callback props so that they match the DRP ([#800](https://github.com/airbnb/react-dates/pull/800)) -- [fix] Explicitly set the border-radius on the keyboard shortcuts button ([#792](https://github.com/airbnb/react-dates/pull/792)) +- [breaking] Rename SDP keydown callback props so that they match the DRP ([#800](https://github.com/react-dates/react-dates/pull/800)) +- [fix] Explicitly set the border-radius on the keyboard shortcuts button ([#792](https://github.com/react-dates/react-dates/pull/792)) ## 14.1.0 -- [new] Add esm build ([#791](https://github.com/airbnb/react-dates/pull/791)) -- [new] Add back `selected-start`/`selected-end` modifiers to `CalendarDay` ([#796](https://github.com/airbnb/react-dates/pull/796)) +- [new] Add esm build ([#791](https://github.com/react-dates/react-dates/pull/791)) +- [new] Add back `selected-start`/`selected-end` modifiers to `CalendarDay` ([#796](https://github.com/react-dates/react-dates/pull/796)) ## 14.0.0 -- [fix] Flip arrow navigation in RTL context ([#775](https://github.com/airbnb/react-dates/pull/775)) -- [new] Add `verticalHeight` prop to SDP, DRP and DayPicker ([#773](https://github.com/airbnb/react-dates/pull/773)) -- [breaking] Modify default `DateInput` styling, convert inputs to actual inputs, and remove caption ids ([#780](https://github.com/airbnb/react-dates/pull/780)) +- [fix] Flip arrow navigation in RTL context ([#775](https://github.com/react-dates/react-dates/pull/775)) +- [new] Add `verticalHeight` prop to SDP, DRP and DayPicker ([#773](https://github.com/react-dates/react-dates/pull/773)) +- [breaking] Modify default `DateInput` styling, convert inputs to actual inputs, and remove caption ids ([#780](https://github.com/react-dates/react-dates/pull/780)) ## 13.0.6 -- [fix] Update `react-with-styles-interface-css` dependency ([#777](https://github.com/airbnb/react-dates/pull/777)) +- [fix] Update `react-with-styles-interface-css` dependency ([#777](https://github.com/react-dates/react-dates/pull/777)) ## 13.0.5 - [fix] Add back missing built CSS file ## 13.0.4 -- [fix] Pass through `customCloseIcon` prop from the SDP to the SDPInput ([#767](https://github.com/airbnb/react-dates/pull/767)) -- [fix] Fix incorrect available/unavailable phrase being read on `CalendarDay` components ([#771](https://github.com/airbnb/react-dates/pull/771)) +- [fix] Pass through `customCloseIcon` prop from the SDP to the SDPInput ([#767](https://github.com/react-dates/react-dates/pull/767)) +- [fix] Fix incorrect available/unavailable phrase being read on `CalendarDay` components ([#771](https://github.com/react-dates/react-dates/pull/771)) ## 13.0.3 -- [fix] Change CSS style specificity to 0 for the default stylesheet ([#753](https://github.com/airbnb/react-dates/pull/753)) -- [fix] Remove unnecessary caption object from `CalendarMonth` styles ([#757](https://github.com/airbnb/react-dates/pull/757)) +- [fix] Change CSS style specificity to 0 for the default stylesheet ([#753](https://github.com/react-dates/react-dates/pull/753)) +- [fix] Remove unnecessary caption object from `CalendarMonth` styles ([#757](https://github.com/react-dates/react-dates/pull/757)) ## 13.0.2 - [fix] Use default export of `registerCSSInterfaceWithDefaultTheme` in `initialize` ## 13.0.1 -- [fix] Move caption div back outside of `CalendarMonth` table ([#748](https://github.com/airbnb/react-dates/pull/748)) +- [fix] Move caption div back outside of `CalendarMonth` table ([#748](https://github.com/react-dates/react-dates/pull/748)) ## 13.0.0 -- [breaking] Convert react-dates to rely on `react-with-styles` in place of CSS stylesheets ([#722](https://github.com/airbnb/react-dates/pull/722)) +- [breaking] Convert react-dates to rely on `react-with-styles` in place of CSS stylesheets ([#722](https://github.com/react-dates/react-dates/pull/722)) ## 12.7.1 -- [fix] set explicit border radius on shortcuts button ([#792](https://github.com/airbnb/react-dates/pull/792)) +- [fix] set explicit border radius on shortcuts button ([#792](https://github.com/react-dates/react-dates/pull/792)) ## 12.7.0 -- [new] Some accessibility improvements and patches ([#715](https://github.com/airbnb/react-dates/pull/715)) +- [new] Some accessibility improvements and patches ([#715](https://github.com/react-dates/react-dates/pull/715)) ## 12.6.0 -- [new] Add `weekDayFormat` prop to SDP/DRP ([#650](https://github.com/airbnb/react-dates/pull/650)) -- [new] Add `openDirection` prop to SDP/DRP ([#653](https://github.com/airbnb/react-dates/pull/653)) -- [fix] Reset visibleDays/currentMonth state when `enableOutsideDays` or `numberOfMonths` has changed ([#702](https://github.com/airbnb/react-dates/pull/702)) -- [new] Add $react-dates-color-primary-dark CSS variable ([#704](https://github.com/airbnb/react-dates/pull/704)) +- [new] Add `weekDayFormat` prop to SDP/DRP ([#650](https://github.com/react-dates/react-dates/pull/650)) +- [new] Add `openDirection` prop to SDP/DRP ([#653](https://github.com/react-dates/react-dates/pull/653)) +- [fix] Reset visibleDays/currentMonth state when `enableOutsideDays` or `numberOfMonths` has changed ([#702](https://github.com/react-dates/react-dates/pull/702)) +- [new] Add $react-dates-color-primary-dark CSS variable ([#704](https://github.com/react-dates/react-dates/pull/704)) ## 12.5.1 -- [fix] Ensure `this.childNode` exists in the `OutsideClickHandler` ([e330839](https://github.com/airbnb/react-dates/commit/e3308395212bef07d1f3c05f413cac3dd245ac98)) -- [fix] Remove `minimumNights` prop from the `DayPickerSingleDateController` ([#686](https://github.com/airbnb/react-dates/pull/686)) +- [fix] Ensure `this.childNode` exists in the `OutsideClickHandler` ([e330839](https://github.com/react-dates/react-dates/commit/e3308395212bef07d1f3c05f413cac3dd245ac98)) +- [fix] Remove `minimumNights` prop from the `DayPickerSingleDateController` ([#686](https://github.com/react-dates/react-dates/pull/686)) ## 12.5.0 -- [fix] Fix `onOutsideClick` prop functionality in the SDP ([#666](https://github.com/airbnb/react-dates/pull/666)) -- [new] Add `inputIconPosition` prop ([#627](https://github.com/airbnb/react-dates/pull/627)) -- [fix] Adjust DayPicker styles when portal status is set ([#659](https://github.com/airbnb/react-dates/pull/659)) +- [fix] Fix `onOutsideClick` prop functionality in the SDP ([#666](https://github.com/react-dates/react-dates/pull/666)) +- [new] Add `inputIconPosition` prop ([#627](https://github.com/react-dates/react-dates/pull/627)) +- [fix] Adjust DayPicker styles when portal status is set ([#659](https://github.com/react-dates/react-dates/pull/659)) ## 12.4.0 -- [fix] Pass `onPrevMonthClick`/`onNextMonthClick` props through the SDP ([#657](https://github.com/airbnb/react-dates/pull/657)) -- [fix] Recalculate modifiers when prop modifiers change ([#668](https://github.com/airbnb/react-dates/pull/668)) -- [new] Pass back month as argument to `onPrevMonthClick`/`onNextMonthClick` props ([#667](https://github.com/airbnb/react-dates/pull/667)) +- [fix] Pass `onPrevMonthClick`/`onNextMonthClick` props through the SDP ([#657](https://github.com/react-dates/react-dates/pull/657)) +- [fix] Recalculate modifiers when prop modifiers change ([#668](https://github.com/react-dates/react-dates/pull/668)) +- [new] Pass back month as argument to `onPrevMonthClick`/`onNextMonthClick` props ([#667](https://github.com/react-dates/react-dates/pull/667)) ## 12.3.0 -- [fix] Allows users to type in same-day start date and end date when `minimumNights` is 0 ([#555](https://github.com/airbnb/react-dates/pull/555)) -- [new] Add `firstDayOfWeek` prop ([#371](https://github.com/airbnb/react-dates/pull/371)) -- [fix] Add back `phrases` support for `SingleDatePicker` ([#623](https://github.com/airbnb/react-dates/pull/623)) +- [fix] Allows users to type in same-day start date and end date when `minimumNights` is 0 ([#555](https://github.com/react-dates/react-dates/pull/555)) +- [new] Add `firstDayOfWeek` prop ([#371](https://github.com/react-dates/react-dates/pull/371)) +- [fix] Add back `phrases` support for `SingleDatePicker` ([#623](https://github.com/react-dates/react-dates/pull/623)) ## 12.2.4 -- [fix] Fix `initialVisibleMonth` error in the `DayPickerRangeController` component ([#617](https://github.com/airbnb/react-dates/pull/617)) -- [fix] Pass through missing `keepOpenOnDateSelect` prop to the `DayPickerSingleDateController` component ([#620](https://github.com/airbnb/react-dates/pull/620)) +- [fix] Fix `initialVisibleMonth` error in the `DayPickerRangeController` component ([#617](https://github.com/react-dates/react-dates/pull/617)) +- [fix] Pass through missing `keepOpenOnDateSelect` prop to the `DayPickerSingleDateController` component ([#620](https://github.com/react-dates/react-dates/pull/620)) ## 12.2.3 -- [fix] Export `DayPickerSingleDateController` in index.js ([#609](https://github.com/airbnb/react-dates/pull/609)) +- [fix] Export `DayPickerSingleDateController` in index.js ([#609](https://github.com/react-dates/react-dates/pull/609)) ## 12.2.2 -- [fix] Reevaluate `--blocked` and `--blocked-outside-range` modifiers in the SDP componentWilLReceiveProps ([#550](https://github.com/airbnb/react-dates/pull/550)) +- [fix] Reevaluate `--blocked` and `--blocked-outside-range` modifiers in the SDP componentWilLReceiveProps ([#550](https://github.com/react-dates/react-dates/pull/550)) ## 12.2.1 -- [fix] Fix `isTouchDevice` warning in `DayPickerSingleDateController` ([77e2135](https://github.com/airbnb/react-dates/commit/77e2135d2009994fbf2c62e3ff68ce82e5786194)) +- [fix] Fix `isTouchDevice` warning in `DayPickerSingleDateController` ([77e2135](https://github.com/react-dates/react-dates/commit/77e2135d2009994fbf2c62e3ff68ce82e5786194)) ## v12.2.0 -- [fix] Deprecate `isTouchDevice` in favor of `is-touch-device` ([#576](https://github.com/airbnb/react-dates/pull/576)) -- [fix] Disable calendar icon when component is disabled ([#591](https://github.com/airbnb/react-dates/pull/591)) -- [fix] Fix issue where range does not clear on invisible months ([#575](https://github.com/airbnb/react-dates/pull/575)) -- [new] Add `DayPickerSingleDateController` component ([#573](https://github.com/airbnb/react-dates/pull/573)) +- [fix] Deprecate `isTouchDevice` in favor of `is-touch-device` ([#576](https://github.com/react-dates/react-dates/pull/576)) +- [fix] Disable calendar icon when component is disabled ([#591](https://github.com/react-dates/react-dates/pull/591)) +- [fix] Fix issue where range does not clear on invisible months ([#575](https://github.com/react-dates/react-dates/pull/575)) +- [new] Add `DayPickerSingleDateController` component ([#573](https://github.com/react-dates/react-dates/pull/573)) ## v12.1.2 -- [fix] Add null check for calendarMonthGrid ref ([#552](https://github.com/airbnb/react-dates/pull/552)) +- [fix] Add null check for calendarMonthGrid ref ([#552](https://github.com/react-dates/react-dates/pull/552)) ## v12.1.1 -- [fix] Remove `--hovered-span` modifier when selecting a new end date ([#523](https://github.com/airbnb/react-dates/pull/523)) -- [fix] Improve `isTouchDevice` detection logic ([#516](https://github.com/airbnb/react-dates/pull/516)) -- [fix] Recompute `--blocked` and `--blocked-outside-range` when `focusedInput` changes ([#522](https://github.com/airbnb/react-dates/pull/522)) +- [fix] Remove `--hovered-span` modifier when selecting a new end date ([#523](https://github.com/react-dates/react-dates/pull/523)) +- [fix] Improve `isTouchDevice` detection logic ([#516](https://github.com/react-dates/react-dates/pull/516)) +- [fix] Recompute `--blocked` and `--blocked-outside-range` when `focusedInput` changes ([#522](https://github.com/react-dates/react-dates/pull/522)) ## v12.1.0 -- [new] Add `showDefaultInputIcon` and `customInputIcon` props to SDP ([#513](https://github.com/airbnb/react-dates/pull/513)) +- [new] Add `showDefaultInputIcon` and `customInputIcon` props to SDP ([#513](https://github.com/react-dates/react-dates/pull/513)) ## v12.0.0 -- [breaking] Updates moment peer dependency to ^2.18.1 ([#505](https://github.com/airbnb/react-dates/pull/505)) +- [breaking] Updates moment peer dependency to ^2.18.1 ([#505](https://github.com/react-dates/react-dates/pull/505)) ## v11.1.0 -- [fix] Patch issues with vertical scrollable datepickers, after-hovered-start and month transitions ([#503](https://github.com/airbnb/react-dates/pull/503)) -- [new] Adds a `readOnly` prop on the DRP and SDP ([#501](https://github.com/airbnb/react-dates/pull/501)) -- [fix] Disable hover when `focusedInput` is falsy ([#483](https://github.com/airbnb/react-dates/pull/483)) +- [fix] Patch issues with vertical scrollable datepickers, after-hovered-start and month transitions ([#503](https://github.com/react-dates/react-dates/pull/503)) +- [new] Adds a `readOnly` prop on the DRP and SDP ([#501](https://github.com/react-dates/react-dates/pull/501)) +- [fix] Disable hover when `focusedInput` is falsy ([#483](https://github.com/react-dates/react-dates/pull/483)) ## v11.0.1 -- [fix] Fixes small modifier issues in the DRP after rearchitecture ([#489](https://github.com/airbnb/react-dates/pull/489)) +- [fix] Fixes small modifier issues in the DRP after rearchitecture ([#489](https://github.com/react-dates/react-dates/pull/489)) ## v11.0.0 -- [breaking] Dramatic rearchitecture of modifiers with the goal of improved performance ([#450](https://github.com/airbnb/react-dates/pull/450)) +- [breaking] Dramatic rearchitecture of modifiers with the goal of improved performance ([#450](https://github.com/react-dates/react-dates/pull/450)) ## v10.2.0 -- [new] Add RTL support to the DRP and the SDP with the `isRTL` prop ([#454](https://github.com/airbnb/react-dates/pull/454)) -- [new] Add `renderMonth` prop to DRP and SDP([#449](https://github.com/airbnb/react-dates/pull/449)) +- [new] Add RTL support to the DRP and the SDP with the `isRTL` prop ([#454](https://github.com/react-dates/react-dates/pull/454)) +- [new] Add `renderMonth` prop to DRP and SDP([#449](https://github.com/react-dates/react-dates/pull/449)) ## v10.1.3 - [Fix] OutsideClickHandler: ensure this.childNode exists (#437) ## v10.1.2 -- [fix] Remove unused scss variables ([#475](https://github.com/airbnb/react-dates/pull/475)) -- [fix] Address some issues introduced by the accessibility PR in v10.0.0 ([#477](https://github.com/airbnb/react-dates/pull/477)) -- [fix] Only update phrase object in the DRP when necessary ([#448](https://github.com/airbnb/react-dates/pull/448)) +- [fix] Remove unused scss variables ([#475](https://github.com/react-dates/react-dates/pull/475)) +- [fix] Address some issues introduced by the accessibility PR in v10.0.0 ([#477](https://github.com/react-dates/react-dates/pull/477)) +- [fix] Only update phrase object in the DRP when necessary ([#448](https://github.com/react-dates/react-dates/pull/448)) ## v10.1.1 - [fix] Remove unnecessary `onClose` instances on the `SDPInput` and `DateInput` components ## v10.1.0 -- [new] Add `onClose` callback ([#397](https://github.com/airbnb/react-dates/pull/397)) +- [new] Add `onClose` callback ([#397](https://github.com/react-dates/react-dates/pull/397)) ## v10.0.1 -- [fix] Fix a few nits as a result of the accessibility PR ([#429](https://github.com/airbnb/react-dates/pull/429)) +- [fix] Fix a few nits as a result of the accessibility PR ([#429](https://github.com/react-dates/react-dates/pull/429)) ## v10.0.0 -- [breaking] Add keyboard accessibility to react-dates ([#301](https://github.com/airbnb/react-dates/pull/301)) +- [breaking] Add keyboard accessibility to react-dates ([#301](https://github.com/react-dates/react-dates/pull/301)) ## v9.0.1 -- [fix] Fixes `withPortal` implementation in Firefox ([#421](https://github.com/airbnb/react-dates/pull/421)) +- [fix] Fixes `withPortal` implementation in Firefox ([#421](https://github.com/react-dates/react-dates/pull/421)) ## v9.0.0 -- [fix] Only send down relevant modifiers down the tree ([#412](https://github.com/airbnb/react-dates/pull/412)) -- [fix] Optimise `isSameDay` method ([#415](https://github.com/airbnb/react-dates/pull/415)) -- [fix] Blur input for portal implementations (and on touch devices) ([#410](https://github.com/airbnb/react-dates/pull/410)) -- [breaking] Add `daySize` prop to scale the pickers properly ([#406](https://github.com/airbnb/react-dates/pull/406)) +- [fix] Only send down relevant modifiers down the tree ([#412](https://github.com/react-dates/react-dates/pull/412)) +- [fix] Optimise `isSameDay` method ([#415](https://github.com/react-dates/react-dates/pull/415)) +- [fix] Blur input for portal implementations (and on touch devices) ([#410](https://github.com/react-dates/react-dates/pull/410)) +- [breaking] Add `daySize` prop to scale the pickers properly ([#406](https://github.com/react-dates/react-dates/pull/406)) ## v8.2.1 -- [fix] Add `needsclick` to inputs to disable fastclick ([#377](https://github.com/airbnb/react-dates/pull/377)) -- [deps] Update `style-loader`, `sinon`, `babel-loader`, `coveralls`, and `karma-webpack` ([#379](https://github.com/airbnb/react-dates/pull/379), [#372](https://github.com/airbnb/react-dates/pull/372), [#373](https://github.com/airbnb/react-dates/pull/373)) +- [fix] Add `needsclick` to inputs to disable fastclick ([#377](https://github.com/react-dates/react-dates/pull/377)) +- [deps] Update `style-loader`, `sinon`, `babel-loader`, `coveralls`, and `karma-webpack` ([#379](https://github.com/react-dates/react-dates/pull/379), [#372](https://github.com/react-dates/react-dates/pull/372), [#373](https://github.com/react-dates/react-dates/pull/373)) ## v8.2.0 -- [new] Add `renderCalendarInfo` prop to DRP and SDP ([#341](https://github.com/airbnb/react-dates/pull/341)) +- [new] Add `renderCalendarInfo` prop to DRP and SDP ([#341](https://github.com/react-dates/react-dates/pull/341)) ## v8.1.1 -- [fix] Add missing `customCloseIcon` propType declarations ([#367](https://github.com/airbnb/react-dates/pull/367)) +- [fix] Add missing `customCloseIcon` propType declarations ([#367](https://github.com/react-dates/react-dates/pull/367)) ## v8.1.0 -- [new] Add `customCloseIcon` prop ([#356](https://github.com/airbnb/react-dates/pull/356)) +- [new] Add `customCloseIcon` prop ([#356](https://github.com/react-dates/react-dates/pull/356)) ## v8.0.0 -- [fix] Remove `$react-dates-width-day-picker` variable from `CalendarMonthGrid.scss`, allowing overrides ([#352](https://github.com/airbnb/react-dates/pull/352)) -- [new] Create `defaultPhrases` file for i18n ([#351](https://github.com/airbnb/react-dates/pull/351)) -- [fix] Set `isTouchDevice` on `componentDidMount` ([#336](https://github.com/airbnb/react-dates/pull/336)) -- [fix] Change `CalendarMonthGrid` background to use `$react-dates-color-white` ([#342](https://github.com/airbnb/react-dates/pull/342)) -- [breaking] Make `onFocusChange` and `onDate(s)Change` props required and `forbidExtraProps` on all components ([#332](https://github.com/airbnb/react-dates/pull/332)) -- [fix] Fix caption alignment when using bootstrap ([#323](https://github.com/airbnb/react-dates/pull/323)) +- [fix] Remove `$react-dates-width-day-picker` variable from `CalendarMonthGrid.scss`, allowing overrides ([#352](https://github.com/react-dates/react-dates/pull/352)) +- [new] Create `defaultPhrases` file for i18n ([#351](https://github.com/react-dates/react-dates/pull/351)) +- [fix] Set `isTouchDevice` on `componentDidMount` ([#336](https://github.com/react-dates/react-dates/pull/336)) +- [fix] Change `CalendarMonthGrid` background to use `$react-dates-color-white` ([#342](https://github.com/react-dates/react-dates/pull/342)) +- [breaking] Make `onFocusChange` and `onDate(s)Change` props required and `forbidExtraProps` on all components ([#332](https://github.com/react-dates/react-dates/pull/332)) +- [fix] Fix caption alignment when using bootstrap ([#323](https://github.com/react-dates/react-dates/pull/323)) ## v7.0.1 -- [fix] Fix minimum nights issues for startDates/endDates with time ([#310](https://github.com/airbnb/react-dates/pull/310)) +- [fix] Fix minimum nights issues for startDates/endDates with time ([#310](https://github.com/react-dates/react-dates/pull/310)) ## v7.0.0 -- [breaking] Simplify `CalendarDay` DOM ([#291](https://github.com/airbnb/react-dates/pull/291)) +- [breaking] Simplify `CalendarDay` DOM ([#291](https://github.com/react-dates/react-dates/pull/291)) ## v6.1.0 -- [fix] Revert "Simplify `CalendarDay` DOM ([#291](https://github.com/airbnb/react-dates/pull/291))" -- [new] Add `renderDay` prop to customize the content of the `CalendarDay` component ([#307](https://github.com/airbnb/react-dates/pull/307)) +- [fix] Revert "Simplify `CalendarDay` DOM ([#291](https://github.com/react-dates/react-dates/pull/291))" +- [new] Add `renderDay` prop to customize the content of the `CalendarDay` component ([#307](https://github.com/react-dates/react-dates/pull/307)) ## v6.0.2 -- [fix] Fix `day` prop type warning to `CalendarDay` ([#305](https://github.com/airbnb/react-dates/pull/305)) -- [fix] Remove blinking cursor in iOS ([#304](https://github.com/airbnb/react-dates/pull/304)) -- [fix] Do not render `DayPicker` when not visible ([#286](https://github.com/airbnb/react-dates/pull/286)) -- [breaking] Simplify `CalendarDay` DOM ([#291](https://github.com/airbnb/react-dates/pull/291)) +- [fix] Fix `day` prop type warning to `CalendarDay` ([#305](https://github.com/react-dates/react-dates/pull/305)) +- [fix] Remove blinking cursor in iOS ([#304](https://github.com/react-dates/react-dates/pull/304)) +- [fix] Do not render `DayPicker` when not visible ([#286](https://github.com/react-dates/react-dates/pull/286)) +- [breaking] Simplify `CalendarDay` DOM ([#291](https://github.com/react-dates/react-dates/pull/291)) ## v6.0.1 - - [fix] Attached SDP closes on outside click again ([#288](https://github.com/airbnb/react-dates/pull/288)) - - [fix] SDP display value defaults to moment's `L` format again instead of ISO ([#285](https://github.com/airbnb/react-dates/pull/285)) + - [fix] Attached SDP closes on outside click again ([#288](https://github.com/react-dates/react-dates/pull/288)) + - [fix] SDP display value defaults to moment's `L` format again instead of ISO ([#285](https://github.com/react-dates/react-dates/pull/285)) ## v6.0.0 - - [breaking] Remove hidden `label` element in favor of an `aria-label` property ([#280](https://github.com/airbnb/react-dates/pull/280)) - - [new] Add `customArrowIcon` prop ([#277](https://github.com/airbnb/react-dates/pull/277)) - - [breaking] Remove mousedown/mouseup/touchstart/touchend/touchtap handlers in favor of click ([#275](https://github.com/airbnb/react-dates/pull/275)) - - [fix] Fix duplicate months created when increasing `numberOfMonths` and include year in `CalendarMonth` key ([#279](https://github.com/airbnb/react-dates/pull/279)) - - [new] Add `screenReaderInputMessage` to populate the `aria-describedby` attribute on the input ([#266](https://github.com/airbnb/react-dates/pull/266)) + - [breaking] Remove hidden `label` element in favor of an `aria-label` property ([#280](https://github.com/react-dates/react-dates/pull/280)) + - [new] Add `customArrowIcon` prop ([#277](https://github.com/react-dates/react-dates/pull/277)) + - [breaking] Remove mousedown/mouseup/touchstart/touchend/touchtap handlers in favor of click ([#275](https://github.com/react-dates/react-dates/pull/275)) + - [fix] Fix duplicate months created when increasing `numberOfMonths` and include year in `CalendarMonth` key ([#279](https://github.com/react-dates/react-dates/pull/279)) + - [new] Add `screenReaderInputMessage` to populate the `aria-describedby` attribute on the input ([#266](https://github.com/react-dates/react-dates/pull/266)) ## v5.2.0 - - [new] Add `VERTICAL_SCROLLABLE` orientation to the `DayPickerRangeController` and child components ([#250](https://github.com/airbnb/react-dates/pull/250)) + - [new] Add `VERTICAL_SCROLLABLE` orientation to the `DayPickerRangeController` and child components ([#250](https://github.com/react-dates/react-dates/pull/250)) ## v5.1.1 - - [fix] Fix regression where user was no longer able to type into input ([#269](https://github.com/airbnb/react-dates/pull/269)) + - [fix] Fix regression where user was no longer able to type into input ([#269](https://github.com/react-dates/react-dates/pull/269)) ## v5.1.0 - - [new] Add `showDefaultInputIcon` and `customInputIcon` prop to show an icon at the beginning of the input field ([#222](https://github.com/airbnb/react-dates/pull/222)) + - [new] Add `showDefaultInputIcon` and `customInputIcon` prop to show an icon at the beginning of the input field ([#222](https://github.com/react-dates/react-dates/pull/222)) ## v5.0.0 - - [breaking] Update input value to use ISO format instead of the display format ([#229](https://github.com/airbnb/react-dates/pull/229)) - - [breaking] Performance improvements, including the removal of the modifiers prop from `CalendarDay` ([#217](https://github.com/airbnb/react-dates/pull/217)) + - [breaking] Update input value to use ISO format instead of the display format ([#229](https://github.com/react-dates/react-dates/pull/229)) + - [breaking] Performance improvements, including the removal of the modifiers prop from `CalendarDay` ([#217](https://github.com/react-dates/react-dates/pull/217)) ## v4.3.3 - - [fix] Force DayPicker and CalendarMonthGrid alignment to the left ([#257](https://github.com/airbnb/react-dates/pull/257),[#258](https://github.com/airbnb/react-dates/pull/258)) + - [fix] Force DayPicker and CalendarMonthGrid alignment to the left ([#257](https://github.com/react-dates/react-dates/pull/257),[#258](https://github.com/react-dates/react-dates/pull/258)) ## v4.3.2 - - [fix] Finish refactor from 471bd602302f4dfe4f1e66b79d50b22f7511d8ba ([#233](https://github.com/airbnb/react-dates/pull/233)) + - [fix] Finish refactor from 471bd602302f4dfe4f1e66b79d50b22f7511d8ba ([#233](https://github.com/react-dates/react-dates/pull/233)) ## v4.3.1 (unpublished) - - [fix] Don’t create an unnecessary array from a NodeList, which avoids needing `Array.from` ([#233](https://github.com/airbnb/react-dates/pull/233)) + - [fix] Don’t create an unnecessary array from a NodeList, which avoids needing `Array.from` ([#233](https://github.com/react-dates/react-dates/pull/233)) ## v4.3.0 - - [new] Add today modifier to the `SingleDatePicker` component ([#218](https://github.com/airbnb/react-dates/pull/218)) - - [fix] Fix week header alignment when `numberOfMonths` is greater than 2 ([#221](https://github.com/airbnb/react-dates/pull/221)) - - [fix] Fix `transition`/`transform` prefixing on `.CalendarMonthGrid--animating` class ([#220](https://github.com/airbnb/react-dates/pull/220)) - - [fix] Do not allow `pointer-events` on invisible first month ([#227](https://github.com/airbnb/react-dates/pull/227)) - - [fix] Remove `maxLength` attribute from inputs ([#219](https://github.com/airbnb/react-dates/pull/219)) + - [new] Add today modifier to the `SingleDatePicker` component ([#218](https://github.com/react-dates/react-dates/pull/218)) + - [fix] Fix week header alignment when `numberOfMonths` is greater than 2 ([#221](https://github.com/react-dates/react-dates/pull/221)) + - [fix] Fix `transition`/`transform` prefixing on `.CalendarMonthGrid--animating` class ([#220](https://github.com/react-dates/react-dates/pull/220)) + - [fix] Do not allow `pointer-events` on invisible first month ([#227](https://github.com/react-dates/react-dates/pull/227)) + - [fix] Remove `maxLength` attribute from inputs ([#219](https://github.com/react-dates/react-dates/pull/219)) ## v4.2.0 - - [new] Add `isDayHighlighted` prop to the DRP/SDP which applies a `highlighted-calendar` to the relevant days ([#206](https://github.com/airbnb/react-dates/pull/206)) - - [new] Add `today` modifier to the `DayPickerRangeController` component ([#213](https://github.com/airbnb/react-dates/pull/213)) + - [new] Add `isDayHighlighted` prop to the DRP/SDP which applies a `highlighted-calendar` to the relevant days ([#206](https://github.com/react-dates/react-dates/pull/206)) + - [new] Add `today` modifier to the `DayPickerRangeController` component ([#213](https://github.com/react-dates/react-dates/pull/213)) ## v4.1.2 - - [fix] `DayPicker` now has initial width set, even before any other interaction ([#215](https://github.com/airbnb/react-dates/pull/215)) + - [fix] `DayPicker` now has initial width set, even before any other interaction ([#215](https://github.com/react-dates/react-dates/pull/215)) ## v4.1.1 - - [fix] Fix issue where the DayPicker height and width were not always being set initially ([#196](https://github.com/airbnb/react-dates/pull/196)) - - [fix] Fix closed DRP/SDP refocus issue on window blur and refocus ([#212](https://github.com/airbnb/react-dates/pull/212)) + - [fix] Fix issue where the DayPicker height and width were not always being set initially ([#196](https://github.com/react-dates/react-dates/pull/196)) + - [fix] Fix closed DRP/SDP refocus issue on window blur and refocus ([#212](https://github.com/react-dates/react-dates/pull/212)) ## v4.1.0 - - [new] Separate out date range input event handling logic into the `DateRangePickerInputController` component ([#180](https://github.com/airbnb/react-dates/pull/180)) - - [fix] Only responsivize the DRP and SDP when `withPortal` and `withFullScreenPortal` options are false ([#183](https://github.com/airbnb/react-dates/pull/183)) - - [new] Separate out date range calendar event handling logic and styles into the `DayPickerRangeController` component ([#167](https://github.com/airbnb/react-dates/pull/167)) + - [new] Separate out date range input event handling logic into the `DateRangePickerInputController` component ([#180](https://github.com/react-dates/react-dates/pull/180)) + - [fix] Only responsivize the DRP and SDP when `withPortal` and `withFullScreenPortal` options are false ([#183](https://github.com/react-dates/react-dates/pull/183)) + - [new] Separate out date range calendar event handling logic and styles into the `DayPickerRangeController` component ([#167](https://github.com/react-dates/react-dates/pull/167)) ## v4.0.2 - - [patch] Revert [#176](https://github.com/airbnb/react-dates/pull/176) ([#189](https://github.com/airbnb/react-dates/pull/189)) + - [patch] Revert [#176](https://github.com/react-dates/react-dates/pull/176) ([#189](https://github.com/react-dates/react-dates/pull/189)) ## v4.0.1 - - [patch] `initialVisibleMonth` prop will now be called every time the `DayPicker` is opened ([#176](https://github.com/airbnb/react-dates/pull/176)) - - [patch] Use the `readOnly` prop on inputs instead of the `disabled` prop on touch devices ([#174](https://github.com/airbnb/react-dates/pull/174)) + - [patch] `initialVisibleMonth` prop will now be called every time the `DayPicker` is opened ([#176](https://github.com/react-dates/react-dates/pull/176)) + - [patch] Use the `readOnly` prop on inputs instead of the `disabled` prop on touch devices ([#174](https://github.com/react-dates/react-dates/pull/174)) ## v4.0.0 - - [breaking] Cut the tether dependency from react-dates ([#163](https://github.com/airbnb/react-dates/pull/163)) + - [breaking] Cut the tether dependency from react-dates ([#163](https://github.com/react-dates/react-dates/pull/163)) ## v3.6.0 - - [new] Add `navPrev`/`navNext` props for custom month navigation ([#161](https://github.com/airbnb/react-dates/pull/161)) - - [fix] Add missing right border on caret ([#160](https://github.com/airbnb/react-dates/pull/160)) - - [fix] Adjust `DayPicker` height when `initialVisibleMonth` height is different from the current month's ([#159](https://github.com/airbnb/react-dates/pull/159)) - - [new] Add `keepOpenOnDateSelect` prop to the `DateRangePicker` and `SingleDatePicker` ([#157](https://github.com/airbnb/react-dates/pull/157)) + - [new] Add `navPrev`/`navNext` props for custom month navigation ([#161](https://github.com/react-dates/react-dates/pull/161)) + - [fix] Add missing right border on caret ([#160](https://github.com/react-dates/react-dates/pull/160)) + - [fix] Adjust `DayPicker` height when `initialVisibleMonth` height is different from the current month's ([#159](https://github.com/react-dates/react-dates/pull/159)) + - [new] Add `keepOpenOnDateSelect` prop to the `DateRangePicker` and `SingleDatePicker` ([#157](https://github.com/react-dates/react-dates/pull/157)) ## v3.5.0 - - [new] Add support for clear date button on the `SingleDatePicker` ([#155](https://github.com/airbnb/react-dates/pull/155)) - - [fix] Fix focus behavior for vertically attached datepickers ([#121](https://github.com/airbnb/react-dates/pull/121)) + - [new] Add support for clear date button on the `SingleDatePicker` ([#155](https://github.com/react-dates/react-dates/pull/155)) + - [fix] Fix focus behavior for vertically attached datepickers ([#121](https://github.com/react-dates/react-dates/pull/121)) ## v3.4.0 - - [new] Add support for `required` attribute on inputs ([#142](https://github.com/airbnb/react-dates/pull/142)) + - [new] Add support for `required` attribute on inputs ([#142](https://github.com/react-dates/react-dates/pull/142)) ## v3.3.4 - - [fix] Fix same tether overlay issue for the `SingleDatePicker` component ([#133](https://github.com/airbnb/react-dates/pull/133)) + - [fix] Fix same tether overlay issue for the `SingleDatePicker` component ([#133](https://github.com/react-dates/react-dates/pull/133)) ## v3.3.3 - - [fix] Allow for elements to be interacted with when rendered beneath the tether component ([#131](https://github.com/airbnb/react-dates/pull/131)) + - [fix] Allow for elements to be interacted with when rendered beneath the tether component ([#131](https://github.com/react-dates/react-dates/pull/131)) ## v3.3.2 - - [fix] Responsive the `DateRangePicker` and `SingleDatePicker` components ([#80](https://github.com/airbnb/react-dates/pull/83)) + - [fix] Responsive the `DateRangePicker` and `SingleDatePicker` components ([#80](https://github.com/react-dates/react-dates/pull/83)) ## v3.3.1 - - [fix] Update all days to use noon as their time stamp to fix a number of DST issues ([#114](https://github.com/airbnb/react-dates/pull/114)) + - [fix] Update all days to use noon as their time stamp to fix a number of DST issues ([#114](https://github.com/react-dates/react-dates/pull/114)) ## v3.3.0 - - [new] Add `anchorDirection` prop to the SingleDatePicker and DateRangePicker components ([#72](https://github.com/airbnb/react-dates/pull/72)) + - [new] Add `anchorDirection` prop to the SingleDatePicker and DateRangePicker components ([#72](https://github.com/react-dates/react-dates/pull/72)) ## v3.2.0 - - [new] Add `initialVisibleMonth` prop to the SingleDatePicker, DateRangePicker, and DayPicker components ([#70](https://github.com/airbnb/react-dates/pull/70)) + - [new] Add `initialVisibleMonth` prop to the SingleDatePicker, DateRangePicker, and DayPicker components ([#70](https://github.com/react-dates/react-dates/pull/70)) ## v3.1.1 - [fix] Fix moment dependencies to allow v2.10 - v2.14 ## v3.1.0 - - [new] Allow `displayFormat` prop to take a function as well as a string ([#98](https://github.com/airbnb/react-dates/pull/98)) - - [fix] Default value for `displayFormat` now actually returns moment's `L` format based on the locale ([#98](https://github.com/airbnb/react-dates/pull/98))) + - [new] Allow `displayFormat` prop to take a function as well as a string ([#98](https://github.com/react-dates/react-dates/pull/98)) + - [fix] Default value for `displayFormat` now actually returns moment's `L` format based on the locale ([#98](https://github.com/react-dates/react-dates/pull/98))) ## v3.0.0 - - [breaking] Move the constants file to the top-level ([#53](https://github.com/airbnb/react-dates/pull/53)) - - [breaking] Add `reopenPickerOnClearDates` prop so that the DateRangePicker no longer automatically reopens when clearing dates ([#75](https://github.com/airbnb/react-dates/pull/75)) + - [breaking] Move the constants file to the top-level ([#53](https://github.com/react-dates/react-dates/pull/53)) + - [breaking] Add `reopenPickerOnClearDates` prop so that the DateRangePicker no longer automatically reopens when clearing dates ([#75](https://github.com/react-dates/react-dates/pull/75)) ## v2.2.0 - - [fix] Fix height issue where an extra table row was being rendered for some months ([#57](https://github.com/airbnb/react-dates/pull/57)) - - [fix] Disables user-select on navigation ([#74](https://github.com/airbnb/react-dates/pull/74)) - - [new] Allows for a custom date display format ([#52](https://github.com/airbnb/react-dates/pull/52)) + - [fix] Fix height issue where an extra table row was being rendered for some months ([#57](https://github.com/react-dates/react-dates/pull/57)) + - [fix] Disables user-select on navigation ([#74](https://github.com/react-dates/react-dates/pull/74)) + - [new] Allows for a custom date display format ([#52](https://github.com/react-dates/react-dates/pull/52)) ## v2.1.1 - [fix] Fix initial day of month to utc to fix daylight savings time problem in Brazil and other locales diff --git a/INTHEWILD.md b/INTHEWILD.md index 636d59c2cc..741d8e9856 100644 --- a/INTHEWILD.md +++ b/INTHEWILD.md @@ -1,4 +1,4 @@ -Please use [pull requests](https://github.com/airbnb/react-dates/pull/new/master) to add your organization and/or project to this document! +Please use [pull requests](https://github.com/react-dates/react-dates/pull/new) to add your organization and/or project to this document! Organizations ---------- diff --git a/README.md b/README.md index da8f6c36e2..0a37ef1f3b 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ > An easily internationalizable, accessible, mobile-friendly datepicker library for the web. -![react-dates in action](https://raw.githubusercontent.com/airbnb/react-dates/master/react-dates-demo.gif) +![react-dates in action](https://raw.githubusercontent.com/react-dates/react-dates/HEAD/react-dates-demo.gif) ## Live Playground @@ -113,7 +113,7 @@ This would override the background and text colors applied to highlighted calend ### Make some awesome datepickers We provide a handful of components for your use. If you supply essential props to each component, you'll get a full featured interactive date picker. With additional optional props, you can customize the look and feel of the inputs, calendar, etc. You can see what each of the props do in the [live demo](http://airbnb.io/react-dates/) or explore -how to properly wrap the pickers in the [examples folder](https://github.com/airbnb/react-dates/tree/master/examples). +how to properly wrap the pickers in the [examples folder](https://github.com/react-dates/react-dates/tree/HEAD/examples). #### DateRangePicker The `DateRangePicker` is a fully controlled component that allows users to select a date range. You can control the selected @@ -356,7 +356,7 @@ The following is a list of other *OPTIONAL* props you may provide to the `DayPic moment.locale('pl'); // Polish ``` -However, this only solves date localization. For complete internationalization of the components, `react-dates` defines a certain amount of [user interface strings](https://github.com/airbnb/react-dates/blob/master/src/defaultPhrases.js) in English which can be changed through the `phrases` prop (explore the [storybook](http://airbnb.io/react-dates/?selectedKind=DateRangePicker%20%28DRP%29&selectedStory=non-english%20locale&full=0&addons=1&stories=1&panelRight=0&addonPanel=kadirahq%2Fstorybook-addon-actions%2Factions-panel) for examples). For accessibility and usability concerns, **all these UI elements should be translated**. +However, this only solves date localization. For complete internationalization of the components, `react-dates` defines a certain amount of [user interface strings](https://github.com/react-dates/react-dates/blob/HEAD/src/defaultPhrases.js) in English which can be changed through the `phrases` prop (explore the [storybook](http://airbnb.io/react-dates/?selectedKind=DateRangePicker%20%28DRP%29&selectedStory=non-english%20locale&full=0&addons=1&stories=1&panelRight=0&addonPanel=kadirahq%2Fstorybook-addon-actions%2Factions-panel) for examples). For accessibility and usability concerns, **all these UI elements should be translated**. ## Advanced @@ -364,7 +364,7 @@ However, this only solves date localization. For complete internationalization o ### Interfaces -The `react-dates/initialize` script actually relies on [react-with-styles-interface-css](https://github.com/airbnb/react-with-styles-interface-css) under the hood. If you are interested in a different solution for styling in your project, you can do your own initialization of a another [interface](https://github.com/airbnb/react-with-styles/blob/master/README.md#interfaces). At Airbnb, for instance, we rely on [Aphrodite](https://github.com/Khan/aphrodite) under the hood and therefore use the Aphrodite interface for `react-with-styles`. If you want to do the same, you would use the following pattern: +The `react-dates/initialize` script actually relies on [react-with-styles-interface-css](https://github.com/airbnb/react-with-styles-interface-css) under the hood. If you are interested in a different solution for styling in your project, you can do your own initialization of a another [interface](https://github.com/airbnb/react-with-styles/blob/HEAD/README.md#interfaces). At Airbnb, for instance, we rely on [Aphrodite](https://github.com/Khan/aphrodite) under the hood and therefore use the Aphrodite interface for `react-with-styles`. If you want to do the same, you would use the following pattern: ```js import ThemedStyleSheet from 'react-with-styles/lib/ThemedStyleSheet'; import aphroditeInterface from 'react-with-styles-interface-aphrodite'; @@ -377,7 +377,7 @@ ThemedStyleSheet.registerTheme(DefaultTheme); The above code has to be run before any `react-dates` component is imported. Otherwise, you will get an error. Also note that if you register any custom interface manually, you *must* also manually register a theme. ### Theming -`react-dates` also now supports a different way to theme. You can see the default theme values in [this file](https://github.com/airbnb/react-dates/blob/master/src/theme/DefaultTheme.js) and you would override them in the following manner: +`react-dates` also now supports a different way to theme. You can see the default theme values in [this file](https://github.com/react-dates/react-dates/blob/HEAD/src/theme/DefaultTheme.js) and you would override them in the following manner: ```js import ThemedStyleSheet from 'react-with-styles/lib/ThemedStyleSheet'; import aphroditeInterface from 'react-with-styles-interface-aphrodite'; @@ -405,16 +405,16 @@ ThemedStyleSheet.registerTheme({ The above code would use shades of green instead of shades of yellow for the highlight color on `CalendarDay` components. Note that you *must* register an interface if you manually register a theme. One will not work without the other. #### A note on using `react-with-styles-interface-css` -The default interface that `react-dates` ships with is the [CSS interface](https://github.com/airbnb/react-with-styles-interface-css). If you want to use this interface along with the theme registration method, you will need to rebuild the core `_datepicker.css` file. We do not currently expose a utility method to build this file, but you can follow along with the code in https://github.com/airbnb/react-dates/blob/master/scripts/buildCSS.js to build your own custom themed CSS file. +The default interface that `react-dates` ships with is the [CSS interface](https://github.com/airbnb/react-with-styles-interface-css). If you want to use this interface along with the theme registration method, you will need to rebuild the core `_datepicker.css` file. We do not currently expose a utility method to build this file, but you can follow along with the code in https://github.com/react-dates/react-dates/blob/HEAD/scripts/buildCSS.js to build your own custom themed CSS file. [package-url]: https://npmjs.org/package/react-dates -[npm-version-svg]: http://versionbadg.es/airbnb/react-dates.svg -[travis-svg]: https://travis-ci.org/airbnb/react-dates.svg -[travis-url]: https://travis-ci.org/airbnb/react-dates -[deps-svg]: https://david-dm.org/airbnb/react-dates.svg -[deps-url]: https://david-dm.org/airbnb/react-dates -[dev-deps-svg]: https://david-dm.org/airbnb/react-dates/dev-status.svg -[dev-deps-url]: https://david-dm.org/airbnb/react-dates#info=devDependencies +[npm-version-svg]: http://versionbadg.es/react-dates/react-dates.svg +[travis-svg]: https://travis-ci.org/react-dates/react-dates.svg +[travis-url]: https://travis-ci.org/react-dates/react-dates +[deps-svg]: https://david-dm.org/react-dates/react-dates.svg +[deps-url]: https://david-dm.org/react-dates/react-dates +[dev-deps-svg]: https://david-dm.org/react-dates/react-dates/dev-status.svg +[dev-deps-url]: https://david-dm.org/react-dates/react-dates#info=devDependencies [npm-badge-png]: https://nodei.co/npm/react-dates.png?downloads=true&stars=true [license-image]: http://img.shields.io/npm/l/react-dates.svg [license-url]: LICENSE diff --git a/package.json b/package.json index b052d45f12..519e8886be 100644 --- a/package.json +++ b/package.json @@ -40,14 +40,14 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/airbnb/react-dates.git" + "url": "git+https://github.com/react-dates/react-dates.git" }, "author": "Maja Wichrowska ", "license": "MIT", "bugs": { - "url": "https://github.com/airbnb/react-dates/issues" + "url": "https://github.com/react-dates/react-dates/issues" }, - "homepage": "https://github.com/airbnb/react-dates#readme", + "homepage": "https://github.com/react-dates/react-dates#readme", "devDependencies": { "@babel/cli": "^7.12.10", "@babel/core": "^7.12.10", diff --git a/stories/DayPickerRangeController.js b/stories/DayPickerRangeController.js index 47b4ecca04..39bb9716a9 100644 --- a/stories/DayPickerRangeController.js +++ b/stories/DayPickerRangeController.js @@ -24,7 +24,7 @@ const dayPickerRangeControllerInfo = `The ${monospace('DayPickerRangeController' ${monospace('startDate')}, and ${monospace('endDate')} values in state and then pass these down as props along with ${monospace('onFocusChange')} and ${monospace('onDatesChange')} callbacks that update them appropriately. You can see an example of this implementation + "https://github.com/react-dates/react-dates/blob/HEAD/examples/DayPickerRangeControllerWrapper.jsx"> here.

    Note that the ${monospace('focusedInput')} prop may be ${monospace('null')}, but if this is the case, dates are not selectable. As a result, in the example wrapper, we always force diff --git a/stories/DayPickerSingleDateController.js b/stories/DayPickerSingleDateController.js index ef05c08305..7126f75420 100644 --- a/stories/DayPickerSingleDateController.js +++ b/stories/DayPickerSingleDateController.js @@ -22,7 +22,7 @@ const dayPickerSingleDateControllerInfo = `The ${monospace('DayPickerSingleDateC ${monospace('date')} values in state and then pass these down as props along with ${monospace('onFocusChange')} and ${monospace('onDateChange')} callbacks that update them appropriately. You can see an example of this implementation + "https://github.com/react-dates/react-dates/blob/HEAD/examples/DayPickerSingleDateControllerWrapper.jsx"> here.

    Note that the ${monospace('focused')} prop may be ${monospace('false')}, but if this is the case, dates are not selectable. As a result, in the example wrapper, we always force diff --git a/stories/PresetDateRangePicker.js b/stories/PresetDateRangePicker.js index 0063a3a257..2d2660327a 100644 --- a/stories/PresetDateRangePicker.js +++ b/stories/PresetDateRangePicker.js @@ -11,9 +11,9 @@ const presetDateRangePickerControllerInfo = `The ${monospace('PresetDateRangePic exported by ${monospace('react-dates')}. It is instead an example of how you might use the ${monospace('DateRangePicker')} along with the ${monospace('renderCalendarInfo')} prop in order to add preset range buttons for easy range selection. You can see the example code - + here and - + here.`; const today = moment(); From 0061fadfe321a42beeb7ec35d6ff8fab2898a227 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sat, 22 Jan 2022 08:43:16 -0800 Subject: [PATCH 609/618] [Dev Deps] update `@babel/cli`, `@babel/core`, `@babel/register`, `@babel/runtime` --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 519e8886be..fe55816653 100644 --- a/package.json +++ b/package.json @@ -49,10 +49,10 @@ }, "homepage": "https://github.com/react-dates/react-dates#readme", "devDependencies": { - "@babel/cli": "^7.12.10", - "@babel/core": "^7.12.10", - "@babel/register": "^7.12.10", - "@babel/runtime": "^7.12.5", + "@babel/cli": "^7.16.8", + "@babel/core": "^7.16.12", + "@babel/register": "^7.16.9", + "@babel/runtime": "^7.16.7", "@storybook/addon-actions": "^5.3.21", "@storybook/addon-info": "^5.3.21", "@storybook/addon-links": "^5.3.21", From 9cf2c97a8794d5f45ee074e0f8414728ee761b74 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sat, 22 Jan 2022 08:49:04 -0800 Subject: [PATCH 610/618] [Dev Deps] use `@babel/eslint-parser` instead of `babel-eslint` --- .eslintignore | 4 ---- .eslintrc | 19 +++++++++++++------ package.json | 2 +- src/components/DateRangePickerInput.jsx | 16 +++++++--------- 4 files changed, 21 insertions(+), 20 deletions(-) delete mode 100644 .eslintignore diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index edfe81afba..0000000000 --- a/.eslintignore +++ /dev/null @@ -1,4 +0,0 @@ -lib/ -.storybook/ -test/_helpers/ -webpack.config.js diff --git a/.eslintrc b/.eslintrc index bcb6102a29..5961c32bbb 100644 --- a/.eslintrc +++ b/.eslintrc @@ -3,19 +3,26 @@ "extends": [ "airbnb", - "plugin:react-with-styles/recommended" + "plugin:react-with-styles/recommended", ], "plugins": [ - "react-with-styles" + "react-with-styles", ], "env": { "browser": true, - "node": true + "node": true, }, - "parser": "babel-eslint", + "ignorePatterns": [ + "lib/", + ".storybook/", + "test/_helpers/", + "webpack.config.js", + ], + + "parser": "@babel/eslint-parser", "rules": { "react/forbid-foreign-prop-types": 2, // For babel-plugin-transform-react-remove-prop-types @@ -44,7 +51,7 @@ //"devDependencies": true //}], "indent": [2, 2, { - "MemberExpression": "off" + "MemberExpression": "off", }], }, }, @@ -52,5 +59,5 @@ "settings": { "propWrapperFunctions": ["forbidExtraProps", "exact", "Object.freeze"], - } + }, } diff --git a/package.json b/package.json index fe55816653..530d17ef0c 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "devDependencies": { "@babel/cli": "^7.16.8", "@babel/core": "^7.16.12", + "@babel/eslint-parser": "^7.16.5", "@babel/register": "^7.16.9", "@babel/runtime": "^7.16.7", "@storybook/addon-actions": "^5.3.21", @@ -62,7 +63,6 @@ "@welldone-software/why-did-you-render": "^3.6.0", "airbnb-js-shims": "^2.2.1", "aphrodite": "^2.4.0", - "babel-eslint": "^10.1.0", "babel-loader": "^8.2.2", "babel-plugin-inline-react-svg": "^1.1.2", "babel-plugin-inline-svg": "^1.2.0", diff --git a/src/components/DateRangePickerInput.jsx b/src/components/DateRangePickerInput.jsx index 09e2ef7d82..b175915310 100644 --- a/src/components/DateRangePickerInput.jsx +++ b/src/components/DateRangePickerInput.jsx @@ -260,15 +260,13 @@ function DateRangePickerInput({ {!isEndDateFocused && children} - { - - } + Date: Fri, 21 Jan 2022 18:14:34 -0800 Subject: [PATCH 611/618] [Dev Deps] update `eslint`, `eslint-config-airbnb`, `eslint-plugin-import`, `eslint-plugin-jsx-a11y`, `eslint-plugin-react`, `eslint-plugin-react-hooks`, `eslint-plugin-react-with-styles` --- .eslintrc | 3 +++ package.json | 14 +++++++------- src/components/DayPickerRangeController.jsx | 4 +--- src/components/DayPickerSingleDateController.jsx | 4 +--- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/.eslintrc b/.eslintrc index 5961c32bbb..d5780077d5 100644 --- a/.eslintrc +++ b/.eslintrc @@ -25,6 +25,8 @@ "parser": "@babel/eslint-parser", "rules": { + "max-len": "off", + "react/forbid-foreign-prop-types": 2, // For babel-plugin-transform-react-remove-prop-types "jsx-a11y/click-events-have-key-events": 1, // TODO: enable @@ -53,6 +55,7 @@ "indent": [2, 2, { "MemberExpression": "off", }], + "react/function-component-definition": "off", }, }, ], diff --git a/package.json b/package.json index 530d17ef0c..1d6f8a97cc 100644 --- a/package.json +++ b/package.json @@ -74,13 +74,13 @@ "cross-env": "^5.2.1", "enzyme": "^3.11.0", "enzyme-adapter-react-helper": "^1.3.9", - "eslint": "^7.18.0", - "eslint-config-airbnb": "^18.2.1", - "eslint-plugin-import": "^2.22.1", - "eslint-plugin-jsx-a11y": "^6.4.1", - "eslint-plugin-react": "^7.22.0", - "eslint-plugin-react-hooks": "^4.2.0", - "eslint-plugin-react-with-styles": "^2.3.0", + "eslint": "^8.7.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.28.0", + "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-react-with-styles": "^2.4.0", "git-directory-deploy": "^1.5.1", "imports-loader": "^0.8.0", "in-publish": "^2.0.1", diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 881c4e75ea..6b5cb732b1 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -1023,9 +1023,7 @@ export default class DayPickerRangeController extends React.PureComponent { const { currentMonth, visibleDays } = this.state; const firstPreviousMonth = currentMonth.clone().subtract(numberOfMonths, 'month'); - const newVisibleDays = getVisibleDays( - firstPreviousMonth, numberOfMonths, enableOutsideDays, true, - ); + const newVisibleDays = getVisibleDays(firstPreviousMonth, numberOfMonths, enableOutsideDays, true); this.setState({ currentMonth: firstPreviousMonth.clone(), diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx index d4fae53608..1bf1a1d719 100644 --- a/src/components/DayPickerSingleDateController.jsx +++ b/src/components/DayPickerSingleDateController.jsx @@ -529,9 +529,7 @@ export default class DayPickerSingleDateController extends React.PureComponent { const { currentMonth, visibleDays } = this.state; const firstPreviousMonth = currentMonth.clone().subtract(numberOfMonths, 'month'); - const newVisibleDays = getVisibleDays( - firstPreviousMonth, numberOfMonths, enableOutsideDays, true, - ); + const newVisibleDays = getVisibleDays(firstPreviousMonth, numberOfMonths, enableOutsideDays, true); this.setState({ currentMonth: firstPreviousMonth.clone(), From d96761af0a204a56a51089ef5d4375bad222a4ff Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sat, 22 Jan 2022 11:31:14 -0800 Subject: [PATCH 612/618] [Dev Deps] update `karma` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1d6f8a97cc..a227f5ce22 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "git-directory-deploy": "^1.5.1", "imports-loader": "^0.8.0", "in-publish": "^2.0.1", - "karma": "^4.2.0", + "karma": "^6.3.11", "karma-chai": "^0.1.0", "karma-firefox-launcher": "^1.2.0", "karma-mocha": "^1.3.0", From 116cc83da34c3570644305dcc1deb1b5a27dc403 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sat, 22 Jan 2022 11:31:54 -0800 Subject: [PATCH 613/618] [Dev Deps] update `karma-mocha` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a227f5ce22..96b06cd2ae 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "karma": "^6.3.11", "karma-chai": "^0.1.0", "karma-firefox-launcher": "^1.2.0", - "karma-mocha": "^1.3.0", + "karma-mocha": "^2.0.1", "karma-sinon": "^1.0.5", "karma-webpack": "^4.0.2", "mkdirp": "^0.5.5", From fede38af4655075bab1cc35d3dadc38e314e4a88 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sat, 22 Jan 2022 11:34:24 -0800 Subject: [PATCH 614/618] [Dev Deps] update `sinon` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 96b06cd2ae..13ce0456d8 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "rimraf": "^2.7.1", "safe-publish-latest": "^1.1.4", "sass-loader": "^7.3.1", - "sinon": "^7.5.0", + "sinon": "^8.1.1", "sinon-sandbox": "^2.0.6", "style-loader": "^0.20.3", "typescript": "*", From 9c570e6745d5444c721bd5f21619bb8d559fc631 Mon Sep 17 00:00:00 2001 From: Mohsen Azimi Date: Sat, 22 Jan 2022 00:06:13 -0300 Subject: [PATCH 615/618] [readme] Update the link to demo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0a37ef1f3b..ffa3f4973d 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ ## Live Playground -For examples of the datepicker in action, go to http://airbnb.io/react-dates. +For examples of the datepicker in action, go to [react-dates.github.io](https://react-dates.github.io/react-dates/). OR From 6a65043b79871c60b97ac9d19eb1f5b62e684ab2 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 23 Jan 2022 14:15:01 -0800 Subject: [PATCH 616/618] [Fix] pin `color2k` to ~1.1, to restore node 4 compat See https://github.com/ricokahler/color2k/issues/413 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 13ce0456d8..ad6bfc80d3 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ }, "dependencies": { "airbnb-prop-types": "^2.16.0", - "color2k": "^1.2.4", + "color2k": "~1.1.1", "consolidated-events": "^1.1.1 || ^2.0.0", "enzyme-shallow-equal": "^1.0.4", "is-touch-device": "^1.0.1", From 2cb51c1f68e54ce1b58e6c5d58c38d4c97fe6d85 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Wed, 26 Jan 2022 12:58:53 -0800 Subject: [PATCH 617/618] [Tests] actually test on react versions in the matrix; add some more versions --- .github/workflows/node.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml index 64fd778928..7b9fb66f96 100644 --- a/.github/workflows/node.yml +++ b/.github/workflows/node.yml @@ -27,9 +27,14 @@ jobs: matrix: node-version: ${{ fromJson(needs.matrix.outputs.latest) }} react: - - 16 - - 15 - - 0.14 + - '16' + - '16.9' + - '16.3' + - '16.0' + - '15' + - '15.5' + - '15.0' + # - '0.14' # TODO: fix pure-component-fallbacl steps: - uses: actions/checkout@v2 @@ -38,6 +43,8 @@ jobs: with: node-version: ${{ matrix.node-version }} - run: npm run tests-only + env: + REACT: ${{ matrix.react }} - uses: codecov/codecov-action@v2 node: From b7bad38dcff024d374ee98f972b55b3de9e61289 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Wed, 26 Jan 2022 12:44:40 -0800 Subject: [PATCH 618/618] [Tests] build tests first, so CI can build/test in different stages --- .babelrc | 8 ++++++++ .github/workflows/node.yml | 42 +++++++++++++++++++++++++++++++++----- .gitignore | 1 + .npmignore | 1 + .nycrc | 1 - package.json | 15 ++++++++------ test/mocha.opts | 3 +-- 7 files changed, 57 insertions(+), 14 deletions(-) diff --git a/.babelrc b/.babelrc index 4e91c4584c..cc5885df62 100644 --- a/.babelrc +++ b/.babelrc @@ -3,6 +3,14 @@ "test": { "presets": [["airbnb", { looseClasses: true }]], "plugins": [ + ["import-path-replace", { + "rules": [ + { + "match": "../src/", + "replacement": "../lib/" + } + ] + }], ["inline-react-svg", { "svgo": false }], diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml index 7b9fb66f96..1f5467ef3e 100644 --- a/.github/workflows/node.yml +++ b/.github/workflows/node.yml @@ -14,11 +14,31 @@ jobs: with: type: 'majors' versionsAsRoot: true - preset: '>=6' - # to test in node < 6, we need to run the build in one stage, including the tests, and then run the tests in the desired node version later + preset: '>=4' + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: ljharb/actions/node/install@main + with: + node-version: 'lts/*' + - uses: actions/cache@v2 + id: cache + with: + path: | + lib + esm + test-build + key: ${{ runner.os }}-${{ hashFiles('package.json', 'src/**', 'test/**', 'scripts/buildCSS.js') }} + - run: npm run build + if: steps.cache.outputs.cache-hit != 'true' + - run: npm run build:test + if: steps.cache.outputs.cache-hit != 'true' + majors: - needs: [matrix] + needs: [build, matrix] name: 'latest minors' runs-on: ubuntu-latest @@ -34,7 +54,7 @@ jobs: - '15' - '15.5' - '15.0' - # - '0.14' # TODO: fix pure-component-fallbacl + - '0.14' steps: - uses: actions/checkout@v2 @@ -42,13 +62,25 @@ jobs: name: 'nvm install ${{ matrix.node-version }} && npm install' with: node-version: ${{ matrix.node-version }} + skip-ls-check: ${{ !startsWith(matrix.node-version, '5.') && !startsWith(matrix.node-version, '4.') && 'true' || 'false' }} + - uses: actions/cache@v2 + id: cache + with: + path: | + lib + esm + test-build + key: ${{ runner.os }}-${{ hashFiles('package.json', 'src/**', 'test/**', 'scripts/buildCSS.js') }} + - run: npm run react + env: + REACT: ${{ matrix.react }} - run: npm run tests-only env: REACT: ${{ matrix.react }} - uses: codecov/codecov-action@v2 node: - name: 'node 6+' + name: 'node.js' needs: [majors] runs-on: ubuntu-latest steps: diff --git a/.gitignore b/.gitignore index 6374bef436..3d7cb404f7 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ node_modules # build folder lib esm +test-build .idea # gh-pages diff --git a/.npmignore b/.npmignore index f88e53c229..f27011d803 100644 --- a/.npmignore +++ b/.npmignore @@ -44,5 +44,6 @@ public react-dates-demo.gif stories test +test-build css !lib/css diff --git a/.nycrc b/.nycrc index 90d79055a2..33fbfd6d2a 100644 --- a/.nycrc +++ b/.nycrc @@ -7,7 +7,6 @@ "src" ], "require": [ - "@babel/register" ], "reporter": [ "text", diff --git a/package.json b/package.json index ad6bfc80d3..d6c29274c5 100644 --- a/package.json +++ b/package.json @@ -5,22 +5,23 @@ "main": "index.js", "scripts": { "prebuild": "npm run clean", - "build": "npm run build:cjs && npm run build:esm && npm run build:css -- --optimize ", + "build": "npm run build:cjs && npm run build:esm && npm run build:css -- --optimize", + "postbuild": "npm run build:test", "build:cjs": "BABEL_ENV=cjs babel src/ -d lib/", "build:esm": "BABEL_ENV=esm babel src/ -d esm/", + "build:test": "BABEL_ENV=test babel test/ -d test-build/", "prebuild:css": "rimraf lib/css && mkdirp lib/css", "build:css": "node scripts/buildCSS.js", - "clean": "rimraf lib esm", + "clean": "rimraf lib esm test-build", "lint": "eslint --ext .js,.jsx src test", - "mocha": "mocha ./test/_helpers", + "mocha": "mocha ./test-build/_helpers", "storybook:uninstall": "npm uninstall --no-save @storybook/react && rimraf node_modules/@storybook node_modules/react-modal node_modules/react-dom-factories", "react": "NPM_CONFIG_LEGACY_PEER_DEPS=true enzyme-adapter-react-install 16", "pretest": "npm run --silent lint", - "pretests-only": "npm run react", "tests-only": "cross-env NODE_ENV=test nyc npm run mocha --silent", "pretests-karma": "npm run react", "tests-karma": "karma start", - "test": "npm run build && npm run tests-only", + "test": "npm run react && npm run build && npm run build:test && npm run tests-only", "storybook": "start-storybook -p 6006", "storybook:css": "npm run build:css && start-storybook -p 6006 -c .storybook-css", "tag": "git tag v$npm_package_version", @@ -64,12 +65,14 @@ "airbnb-js-shims": "^2.2.1", "aphrodite": "^2.4.0", "babel-loader": "^8.2.2", + "babel-plugin-import-path-replace": "^0.1.0", "babel-plugin-inline-react-svg": "^1.1.2", "babel-plugin-inline-svg": "^1.2.0", "babel-plugin-istanbul": "^5.2.0", "babel-plugin-transform-replace-object-assign": "^2.0.0", "babel-preset-airbnb": "^4.5.0", "chai": "^4.2.0", + "cheerio": "=1.0.0-rc.3", "clean-css": "^4.2.3", "cross-env": "^5.2.1", "enzyme": "^3.11.0", @@ -104,7 +107,7 @@ "rimraf": "^2.7.1", "safe-publish-latest": "^1.1.4", "sass-loader": "^7.3.1", - "sinon": "^8.1.1", + "sinon": "^7.5.0", "sinon-sandbox": "^2.0.6", "style-loader": "^0.20.3", "typescript": "*", diff --git a/test/mocha.opts b/test/mocha.opts index 6f1ab8e673..22acddc5ba 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,3 +1,2 @@ ---require @babel/register --require airbnb-js-shims -test/**/*.{js,jsx} +test-build/**/*.js