Skip to content

Commit

Permalink
Clean up accessibility phrases based on new copy
Browse files Browse the repository at this point in the history
  • Loading branch information
Maja Wichrowska committed Apr 7, 2017
1 parent 5183125 commit 780522e
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 51 deletions.
2 changes: 0 additions & 2 deletions css/CalendarDay.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
box-sizing: border-box;
color: $react-dates-color-gray;
cursor: pointer;
width: 39px;
height: 38px;
}

.CalendarDay__button {
Expand Down
22 changes: 15 additions & 7 deletions src/components/CalendarDay.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import cx from 'classnames';

import { CalendarDayPhrases } from '../defaultPhrases';
import getPhrasePropTypes from '../utils/getPhrasePropTypes';
import getPhrase from '../utils/getPhrase';

import { BLOCKED_MODIFIER, DAY_SIZE } from '../../constants';

Expand Down Expand Up @@ -83,7 +84,10 @@ export default class CalendarDay extends React.Component {
modifiers,
renderDay,
tabIndex,
phrases: { unavailable, available },
phrases: {
chooseAvailableDate,
dateIsUnavailable,
},
} = this.props;

if (!day) return <td />;
Expand All @@ -95,25 +99,29 @@ export default class CalendarDay extends React.Component {
}, modifiersForDay.map(mod => `CalendarDay--${mod}`));


let availabilityText = '';
if (BLOCKED_MODIFIER in modifiers) {
availabilityText = modifiers[BLOCKED_MODIFIER](day) ? unavailable : available;
const formattedDate = `${day.format('dddd')}, ${day.format('LL')}`;

let ariaLabel = getPhrase(chooseAvailableDate, {
checkin_date: formattedDate,
checkout_date: formattedDate,
});

if (BLOCKED_MODIFIER in modifiers && modifiers[BLOCKED_MODIFIER](day)) {
ariaLabel = getPhrase(dateIsUnavailable, { date: formattedDate });
}

const ariaLabel = `${availabilityText} ${day.format('dddd')}. ${day.format('LL')}`;
const daySizeStyles = {
width: daySize,
height: daySize - 1,
};

return (
<td className={className}>
<td className={className} style={daySizeStyles}>
<button
type="button"
ref={(ref) => { this.buttonRef = ref; }}
className="CalendarDay__button"
aria-label={ariaLabel}
style={daySizeStyles}
onMouseEnter={(e) => { this.onDayMouseEnter(day, e); }}
onMouseLeave={(e) => { this.onDayMouseLeave(day, e); }}
onMouseUp={(e) => { e.currentTarget.blur(); }}
Expand Down
11 changes: 6 additions & 5 deletions src/components/DayPicker.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ export default class DayPicker extends React.Component {
}

onKeyDown(e) {
e.stopPropagation();
if (e) e.stopPropagation();

const { onBlur } = this.props;
const { focusedDate, showKeyboardShortcuts } = this.state;
Expand All @@ -291,10 +291,12 @@ export default class DayPicker extends React.Component {

switch (e.key) {
case 'ArrowUp':
if (e) e.preventDefault();
newFocusedDate.subtract(1, 'week');
didTransitionMonth = this.maybeTransitionPrevMonth(newFocusedDate);
break;
case 'ArrowLeft':
if (e) e.preventDefault();
newFocusedDate.subtract(1, 'day');
didTransitionMonth = this.maybeTransitionPrevMonth(newFocusedDate);
break;
Expand All @@ -308,10 +310,12 @@ export default class DayPicker extends React.Component {
break;

case 'ArrowDown':
if (e) e.preventDefault();
newFocusedDate.add(1, 'week');
didTransitionMonth = this.maybeTransitionNextMonth(newFocusedDate);
break;
case 'ArrowRight':
if (e) e.preventDefault();
newFocusedDate.add(1, 'day');
didTransitionMonth = this.maybeTransitionNextMonth(newFocusedDate);
break;
Expand Down Expand Up @@ -392,14 +396,11 @@ export default class DayPicker extends React.Component {
}

getFocusedDay(newMonth) {
// TODO(maja): figure out which month var I should be using
const { getFirstFocusableDay } = this.props;
const { currentMonth } = this.state;

let focusedDate;
if (getFirstFocusableDay) {
const month = newMonth || currentMonth;
focusedDate = getFirstFocusableDay(month);
focusedDate = getFirstFocusableDay(newMonth);
}

if (newMonth && (!focusedDate || !this.isDayVisible(focusedDate, newMonth))) {
Expand Down
13 changes: 12 additions & 1 deletion src/components/DayPickerRangeController.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,17 @@ export default class DayPickerRangeController extends React.Component {
},
};

// set the appropriate CalendarDay phrase based on focusedInput
const chooseAvailableDate =
focusedInput === START_DATE
? phrases.chooseAvailableStartDate
: phrases.chooseAvailableEndDate;

const calendarDayPhrases = {
...phrases,
chooseAvailableDate,
};

return (
<DayPicker
ref={(ref) => { this.dayPicker = ref; }}
Expand All @@ -356,7 +367,7 @@ export default class DayPickerRangeController extends React.Component {
getFirstFocusableDay={this.getFirstFocusableDay}
onBlur={onBlur}
showKeyboardShortcuts={showKeyboardShortcuts}
phrases={phrases}
phrases={calendarDayPhrases}
/>
);
}
Expand Down
38 changes: 24 additions & 14 deletions src/defaultPhrases.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ const closeDatePicker = 'Close';
const focusStartDate = 'Interact with the calendar and add the check-in date for your trip.';
const clearDate = 'Clear Date';
const clearDates = 'Clear Dates';
const jumpToPrevMonth = 'Move forward to switch to the previous month';
const jumpToNextMonth = 'Move backward to switch to the next month';
const jumpToPrevMonth = 'Move backward to switch to the previous month';
const jumpToNextMonth = 'Move forward to switch to the next month';
const keyboardShortcuts = 'Keyboard Shortcuts';
const showKeyboardShortcutsPanel = 'Open the keyboard shortcuts panel';
const hideKeyboardShortcutsPanel = 'Close the shortcuts panel';
Expand All @@ -23,8 +23,14 @@ const moveFocustoStartAndEndOfWeek = 'Go to the first or last day of a week';
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.`;
const available = 'Available.';
const unavailable = 'Unavailable.';

// eslint-disable-next-line camelcase
const chooseAvailableStartDate = ({ checkin_date }) => `Choose ${checkin_date} as your check-in date. It's available.`;

// eslint-disable-next-line camelcase
const chooseAvailableEndDate = ({ checkout_date }) => `Choose ${checkout_date} as your check-out date. It's available.`;
const dateIsUnavailable = ({ date }) => `Not available. ${date}`;


export default {
closeDatePicker,
Expand All @@ -51,8 +57,10 @@ export default {
moveFocustoStartAndEndOfWeek,
returnFocusToInput,
keyboardNavigationInstructions,
available,
unavailable,

chooseAvailableStartDate,
chooseAvailableEndDate,
dateIsUnavailable,
};

export const DateRangePickerPhrases = {
Expand All @@ -79,8 +87,9 @@ export const DateRangePickerPhrases = {
moveFocustoStartAndEndOfWeek,
returnFocusToInput,
keyboardNavigationInstructions,
available,
unavailable,
chooseAvailableStartDate,
chooseAvailableEndDate,
dateIsUnavailable,
};

export const DateRangePickerInputPhrases = {
Expand Down Expand Up @@ -112,8 +121,9 @@ export const SingleDatePickerPhrases = {
moveFocustoStartAndEndOfWeek,
returnFocusToInput,
keyboardNavigationInstructions,
available,
unavailable,
chooseAvailableStartDate,
chooseAvailableEndDate,
dateIsUnavailable,
};

export const SingleDatePickerInputPhrases = {
Expand Down Expand Up @@ -141,8 +151,8 @@ export const DayPickerPhrases = {
moveFocusByOneMonth,
moveFocustoStartAndEndOfWeek,
returnFocusToInput,
available,
unavailable,
chooseAvailableDate: chooseAvailableStartDate,
dateIsUnavailable,
};

export const DayPickerKeyboardShortcutsPhrases = {
Expand Down Expand Up @@ -171,6 +181,6 @@ export const DayPickerNavigationPhrases = {
};

export const CalendarDayPhrases = {
available,
unavailable,
chooseAvailableDate: chooseAvailableStartDate,
dateIsUnavailable,
};
9 changes: 9 additions & 0 deletions src/utils/getPhrase.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';

export default function getPhrase(phrase, args) {
if (typeof phrase === 'string') return phrase;

if (typeof phrase === 'function') return phrase(args);

return <phrase {...args} />;
}
5 changes: 4 additions & 1 deletion src/utils/getPhrasePropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@ import { PropTypes } from 'react';

export default function getPhrasePropTypes(defaultPhrases) {
return Object.keys(defaultPhrases)
.reduce((phrases, key) => ({ ...phrases, [key]: PropTypes.node }), {});
.reduce((phrases, key) => ({
...phrases,
[key]: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.node]),
}), {});
}
14 changes: 1 addition & 13 deletions test/components/DayPicker_spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ describe('DayPicker', () => {
});

it('calls closeKeyboardShortcutsPanel if state.showKeyboardShortcuts === true', () => {
const closeKeyboardShortcutsPanelSpy = sinon.stub(DayPicker.prototype, 'closeKeyboardShortcutsPanelSpy');
const closeKeyboardShortcutsPanelSpy = sinon.stub(DayPicker.prototype, 'closeKeyboardShortcutsPanel');
const wrapper = shallow(<DayPicker />);
wrapper.setState({
focusedDate: today,
Expand Down Expand Up @@ -512,18 +512,6 @@ describe('DayPicker', () => {
expect(getFirstFocusableDayStub.getCall(0).args[0].isSame(today, 'day')).to.equal(true);
});

it('calls getFirstFocusableDay with state.currentMonth if no arg', () => {
const test = moment().add(3, 'months');
const getFirstFocusableDayStub = sinon.stub();
const wrapper = shallow(<DayPicker getFirstFocusableDay={getFirstFocusableDayStub} />);
wrapper.setState({
currentMonth: test,
});
getFirstFocusableDayStub.reset(); // getFirstFocusableDay gets called in the constructor
wrapper.instance().getFocusedDay();
expect(getFirstFocusableDayStub.getCall(0).args[0].isSame(test, 'day')).to.equal(true);
});

it('returns getFirstFocusableDay() value', () => {
const getFirstFocusableDayStub = sinon.stub().returns(today);
const wrapper = shallow(<DayPicker getFirstFocusableDay={getFirstFocusableDayStub} />);
Expand Down
8 changes: 0 additions & 8 deletions test/utils/getPhrasePropTypes_spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { expect } from 'chai';
import { PropTypes } from 'react';

import getPhrasePropTypes from '../../src/utils/getPhrasePropTypes';

Expand All @@ -16,11 +15,4 @@ describe('#getPhrasePropTypes', () => {
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);
});
});
});

0 comments on commit 780522e

Please sign in to comment.