diff --git a/.istanbul.yml b/.istanbul.yml index 906b0d0971..2c38c40b53 100644 --- a/.istanbul.yml +++ b/.istanbul.yml @@ -7,6 +7,7 @@ instrumentation: "public/**", "test/**", "coverage/**", + "src/defaultPhrases.js" ] check: global: diff --git a/src/components/DateRangePicker.jsx b/src/components/DateRangePicker.jsx index 5c738756bf..67c77ec3ab 100644 --- a/src/components/DateRangePicker.jsx +++ b/src/components/DateRangePicker.jsx @@ -7,6 +7,8 @@ import Portal from 'react-portal'; import { forbidExtraProps } from 'airbnb-prop-types'; import { addEventListener, removeEventListener } from 'consolidated-events'; +import { DateRangePickerPhrases } from '../defaultPhrases'; + import OutsideClickHandler from './OutsideClickHandler'; import getResponsiveContainerStyles from '../utils/getResponsiveContainerStyles'; @@ -77,10 +79,7 @@ const defaultProps = { // internationalization displayFormat: () => moment.localeData().longDateFormat('L'), monthFormat: 'MMMM YYYY', - phrases: { - closeDatePicker: 'Close', - clearDates: 'Clear Dates', - }, + phrases: DateRangePickerPhrases, }; export default class DateRangePicker extends React.Component { diff --git a/src/components/DateRangePickerInput.jsx b/src/components/DateRangePickerInput.jsx index 55353b62e0..c3c12a57fa 100644 --- a/src/components/DateRangePickerInput.jsx +++ b/src/components/DateRangePickerInput.jsx @@ -2,6 +2,9 @@ import React, { PropTypes } from 'react'; import { forbidExtraProps } from 'airbnb-prop-types'; import cx from 'classnames'; +import { DateRangePickerInputPhrases } from '../defaultPhrases'; +import getPhrasePropTypes from '../utils/getPhrasePropTypes'; + import DateInput from './DateInput'; import RightArrow from '../svg/arrow-right.svg'; import CloseButton from '../svg/close.svg'; @@ -41,9 +44,7 @@ const propTypes = forbidExtraProps({ customArrowIcon: PropTypes.node, // i18n - phrases: PropTypes.shape({ - clearDates: PropTypes.node, - }), + phrases: PropTypes.shape(getPhrasePropTypes(DateRangePickerInputPhrases)), }); const defaultProps = { @@ -76,9 +77,7 @@ const defaultProps = { customArrowIcon: null, // i18n - phrases: { - clearDates: 'Clear Dates', - }, + phrases: DateRangePickerInputPhrases, }; export default class DateRangePickerInput extends React.Component { @@ -145,13 +144,16 @@ export default class DateRangePickerInput extends React.Component { })} > {(showDefaultInputIcon || customInputIcon !== null) && - {inputIcon} - + } + {!isVerticalScrollable && @@ -80,6 +91,7 @@ export default function DayPickerNavigation(props) { } diff --git a/src/components/DayPickerRangeController.jsx b/src/components/DayPickerRangeController.jsx index 39dab68bee..160a191120 100644 --- a/src/components/DayPickerRangeController.jsx +++ b/src/components/DayPickerRangeController.jsx @@ -3,6 +3,9 @@ import momentPropTypes from 'react-moment-proptypes'; import { forbidExtraProps } from 'airbnb-prop-types'; import moment from 'moment'; +import { DayPickerPhrases } from '../defaultPhrases'; +import getPhrasePropTypes from '../utils/getPhrasePropTypes'; + import isTouchDevice from '../utils/isTouchDevice'; import isInclusivelyAfterDay from '../utils/isInclusivelyAfterDay'; @@ -51,6 +54,7 @@ const propTypes = forbidExtraProps({ // i18n monthFormat: PropTypes.string, + phrases: PropTypes.shape(getPhrasePropTypes(DayPickerPhrases)), }); const defaultProps = { @@ -86,6 +90,7 @@ const defaultProps = { // i18n monthFormat: 'MMMM YYYY', + phrases: DayPickerPhrases, }; export default class DayPickerRangeController extends React.Component { diff --git a/src/components/SingleDatePicker.jsx b/src/components/SingleDatePicker.jsx index 942f217afd..b068a43e73 100644 --- a/src/components/SingleDatePicker.jsx +++ b/src/components/SingleDatePicker.jsx @@ -5,6 +5,8 @@ import Portal from 'react-portal'; import { forbidExtraProps } from 'airbnb-prop-types'; import { addEventListener, removeEventListener } from 'consolidated-events'; +import { SingleDatePickerPhrases } from '../defaultPhrases'; + import OutsideClickHandler from './OutsideClickHandler'; import toMomentObject from '../utils/toMomentObject'; import toLocalizedDateString from '../utils/toLocalizedDateString'; @@ -70,10 +72,7 @@ const defaultProps = { // internationalization props displayFormat: () => moment.localeData().longDateFormat('L'), monthFormat: 'MMMM YYYY', - phrases: { - closeDatePicker: 'Close', - clearDate: 'Clear Date', - }, + phrases: SingleDatePickerPhrases, }; export default class SingleDatePicker extends React.Component { diff --git a/src/components/SingleDatePickerInput.jsx b/src/components/SingleDatePickerInput.jsx index d45bffbd0f..958e44a5ec 100644 --- a/src/components/SingleDatePickerInput.jsx +++ b/src/components/SingleDatePickerInput.jsx @@ -2,6 +2,9 @@ import React, { PropTypes } from 'react'; import { forbidExtraProps } from 'airbnb-prop-types'; import cx from 'classnames'; +import { SingleDatePickerInputPhrases } from '../defaultPhrases'; +import getPhrasePropTypes from '../utils/getPhrasePropTypes'; + import DateInput from './DateInput'; import CloseButton from '../svg/close.svg'; @@ -24,9 +27,7 @@ const propTypes = forbidExtraProps({ onKeyDownTab: PropTypes.func, // i18n - phrases: PropTypes.shape({ - clearDate: PropTypes.node, - }), + phrases: PropTypes.shape(getPhrasePropTypes(SingleDatePickerInputPhrases)), }); const defaultProps = { @@ -47,9 +48,7 @@ const defaultProps = { onKeyDownTab() {}, // i18n - phrases: { - clearDate: 'Clear Date', - }, + phrases: SingleDatePickerInputPhrases, }; export default class SingleDatePickerInput extends React.Component { @@ -122,13 +121,11 @@ export default class SingleDatePickerInput extends React.Component { 'SingleDatePickerInput__clear-date--hide': !displayValue, 'SingleDatePickerInput__clear-date--hover': isClearDateHovered, })} + aria-label={phrases.clearDate} onMouseEnter={this.onClearDateMouseEnter} onMouseLeave={this.onClearDateMouseLeave} onClick={onClearDate} > - - {phrases.clearDate} - } diff --git a/src/defaultPhrases.js b/src/defaultPhrases.js new file mode 100644 index 0000000000..f572af5ce5 --- /dev/null +++ b/src/defaultPhrases.js @@ -0,0 +1,49 @@ +const closeDatePicker = 'Close'; +const focusStartDate = 'Focus on start date'; +const clearDate = 'Clear Date'; +const clearDates = 'Clear Dates'; +const jumpToPrevMonth = 'Jump to previous month'; +const jumpToNextMonth = 'Jump to next month'; + +export default { + closeDatePicker, + focusStartDate, + clearDate, + clearDates, + jumpToPrevMonth, + jumpToNextMonth, +}; + +export const DateRangePickerPhrases = { + closeDatePicker, + clearDates, + focusStartDate, + jumpToPrevMonth, + jumpToNextMonth, +}; + +export const DateRangePickerInputPhrases = { + focusStartDate, + clearDates, +}; + +export const SingleDatePickerPhrases = { + closeDatePicker, + clearDate, + jumpToPrevMonth, + jumpToNextMonth, +}; + +export const SingleDatePickerInputPhrases = { + clearDate, +}; + +export const DayPickerPhrases = { + jumpToPrevMonth, + jumpToNextMonth, +}; + +export const DayPickerNavigationPhrases = { + jumpToPrevMonth, + jumpToNextMonth, +}; diff --git a/src/shapes/DateRangePickerShape.js b/src/shapes/DateRangePickerShape.js index c3d86a5bf1..cdd811e118 100644 --- a/src/shapes/DateRangePickerShape.js +++ b/src/shapes/DateRangePickerShape.js @@ -1,6 +1,9 @@ import { PropTypes } from 'react'; import momentPropTypes from 'react-moment-proptypes'; +import { DateRangePickerPhrases } from '../defaultPhrases'; +import getPhrasePropTypes from '../utils/getPhrasePropTypes'; + import FocusedInputShape from '../shapes/FocusedInputShape'; import OrientationShape from '../shapes/OrientationShape'; import anchorDirectionShape from '../shapes/AnchorDirectionShape'; @@ -55,8 +58,5 @@ export default { // internationalization props displayFormat: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), monthFormat: PropTypes.string, - phrases: PropTypes.shape({ - closeDatePicker: PropTypes.node, - clearDates: PropTypes.node, - }), + phrases: PropTypes.shape(getPhrasePropTypes(DateRangePickerPhrases)), }; diff --git a/src/shapes/SingleDatePickerShape.js b/src/shapes/SingleDatePickerShape.js index e2fe4635b8..394fc64607 100644 --- a/src/shapes/SingleDatePickerShape.js +++ b/src/shapes/SingleDatePickerShape.js @@ -1,6 +1,9 @@ import { PropTypes } from 'react'; import momentPropTypes from 'react-moment-proptypes'; +import { SingleDatePickerPhrases } from '../defaultPhrases'; +import getPhrasePropTypes from '../utils/getPhrasePropTypes'; + import OrientationShape from '../shapes/OrientationShape'; import anchorDirectionShape from '../shapes/AnchorDirectionShape'; @@ -47,7 +50,5 @@ export default { // internationalization props displayFormat: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), monthFormat: PropTypes.string, - phrases: PropTypes.shape({ - closeDatePicker: PropTypes.node, - }), + phrases: PropTypes.shape(getPhrasePropTypes(SingleDatePickerPhrases)), }; diff --git a/src/utils/getPhrasePropTypes.js b/src/utils/getPhrasePropTypes.js new file mode 100644 index 0000000000..afa6054e7c --- /dev/null +++ b/src/utils/getPhrasePropTypes.js @@ -0,0 +1,6 @@ +import { PropTypes } from 'react'; + +export default function getPhrasePropTypes(defaultPhrases) { + return Object.keys(defaultPhrases) + .reduce((phrases, key) => ({ ...phrases, [key]: PropTypes.node }), {}); +} diff --git a/test/utils/getPhrasePropTypes_spec.js b/test/utils/getPhrasePropTypes_spec.js new file mode 100644 index 0000000000..b9f0776875 --- /dev/null +++ b/test/utils/getPhrasePropTypes_spec.js @@ -0,0 +1,26 @@ +import { expect } from 'chai'; +import { PropTypes } from 'react'; + +import getPhrasePropTypes from '../../src/utils/getPhrasePropTypes'; + +const PhraseObject = { + foo: 'x', + bar: 'y', + baz: 'z', +}; + +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); + }); + }); + + it('each value is equal to PropTypes.node', () => { + const propTypes = getPhrasePropTypes(PhraseObject); + Object.values(propTypes).forEach((value) => { + expect(value).to.equal(PropTypes.node); + }); + }); +});