Skip to content

Commit 3cc2495

Browse files
author
Maja Wichrowska
committed
Add other keyboard interactions
1 parent 771adbc commit 3cc2495

12 files changed

+323
-139
lines changed

css/CalendarDay.scss

+19-16
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,5 @@
11
@import "variables";
22

3-
// position: relative;
4-
// height: 100%;
5-
// width: 100%;
6-
// text-align: center;
7-
// background: none;
8-
// border: 0;
9-
// margin: 0;
10-
// padding: 0;
11-
// color: inherit;
12-
// font: inherit;
13-
// line-height: normal;
14-
// overflow: visible;
15-
// cursor: pointer;
16-
// box-sizing: border-box;
17-
18-
// This order is important.
193
.CalendarDay {
204
border: 1px solid lighten($react-dates-color-border-light, 3);
215
padding: 0;
@@ -24,12 +8,31 @@
248
cursor: pointer;
259
width: 39px;
2610
height: 38px;
11+
}
12+
13+
.CalendarDay__button {
14+
position: relative;
15+
height: 100%;
16+
width: 100%;
17+
text-align: center;
18+
background: none;
19+
border: 0;
20+
margin: 0;
21+
padding: 0;
22+
color: inherit;
23+
font: inherit;
24+
line-height: normal;
25+
overflow: visible;
26+
cursor: pointer;
27+
box-sizing: border-box;
2728

2829
&:active {
2930
background: darken($react-dates-color-white, 5%);
31+
outline: 0;
3032
}
3133
}
3234

35+
// This order is important.
3336
.CalendarDay--highlighted-calendar {
3437
background: $react-dates-color-highlighted;
3538
color: $react-dates-color-gray;

css/DayPickerNavigation.scss

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
&:active {
2525
background: darken($react-dates-color-white, 5%);
26+
outline: 0;
2627
}
2728
}
2829

src/components/CalendarDay.jsx

+7-4
Original file line numberDiff line numberDiff line change
@@ -64,22 +64,25 @@ export default class CalendarDay extends React.Component {
6464
isOutsideDay,
6565
modifiers,
6666
renderDay,
67+
isFocused,
6768
} = this.props;
6869

6970
const className = cx('CalendarDay', {
7071
'CalendarDay--outside': !day || isOutsideDay,
7172
}, getModifiersForDay(modifiers, day).map(mod => `CalendarDay--${mod}`));
7273

7374
return (day ?
74-
<td>
75+
<td className={className}>
7576
<button
76-
ref={(ref) => { this.buttonRef = ref; }}
7777
type="button"
78-
className={className}
79-
aria-label={day.format('LL')}
78+
ref={(ref) => { this.buttonRef = ref; }}
79+
className="CalendarDay__button"
80+
aria-label={`${day.format('dddd')}. ${day.format('LL')}`}
8081
onMouseEnter={e => this.onDayMouseEnter(day, e)}
8182
onMouseLeave={e => this.onDayMouseLeave(day, e)}
83+
onMouseUp={e => e.currentTarget.blur()}
8284
onClick={e => this.onDayClick(day, e)}
85+
tabIndex={isFocused ? 0 : -1}
8386
>
8487
{renderDay ? renderDay(day) : day.format('D')}
8588
</button>

src/components/CalendarMonthGrid.jsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -161,15 +161,17 @@ export default class CalendarMonthGrid extends React.Component {
161161

162162
const { months } = this.state;
163163

164+
const isHorizontal = orientation === HORIZONTAL_ORIENTATION;
165+
164166
const className = cx('CalendarMonthGrid', {
165-
'CalendarMonthGrid--horizontal': orientation === HORIZONTAL_ORIENTATION,
167+
'CalendarMonthGrid--horizontal': isHorizontal,
166168
'CalendarMonthGrid--vertical': orientation === VERTICAL_ORIENTATION,
167169
'CalendarMonthGrid--vertical-scrollable': orientation === VERTICAL_SCROLLABLE,
168170
'CalendarMonthGrid--animating': isAnimating,
169171
});
170172

171173
const style = Object.assign({
172-
width: (numberOfMonths + 1) * calendarMonthWidth,
174+
width: isHorizontal ? (numberOfMonths + 1) * calendarMonthWidth : calendarMonthWidth,
173175
}, getTransformStyles(transformValue));
174176

175177
return (

src/components/DateInput.jsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,10 @@ export default class DateInput extends React.Component {
6161
}
6262

6363
componentDidUpdate(prevProps) {
64-
const { focused } = this.props;
65-
if (prevProps.focused === focused) return;
64+
const { focused, hasFocus } = this.props;
65+
if (prevProps.focused === focused && prevProps.hasFocus === hasFocus) return;
6666

67-
if (focused) {
67+
if (focused && hasFocus) {
6868
this.inputRef.focus();
6969
this.inputRef.select();
7070
} else {

src/components/DateRangePicker.jsx

+43-10
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ const defaultProps = {
8080
phrases: {
8181
closeDatePicker: 'Close',
8282
clearDates: 'Clear Dates',
83+
jumpToPrevMonth: 'Jump to previous month',
84+
jumpToNextMonth: 'Jump to next month',
8385
},
8486
};
8587

@@ -88,12 +90,14 @@ export default class DateRangePicker extends React.Component {
8890
super(props);
8991
this.state = {
9092
dayPickerContainerStyles: {},
93+
isDateRangePickerInputFocused: false,
9194
isDayPickerFocused: false,
9295
};
9396

9497
this.onOutsideClick = this.onOutsideClick.bind(this);
95-
this.onFocusDayPicker = this.onFocusDayPicker.bind(this);
96-
this.onBlurDayPicker = this.onBlurDayPicker.bind(this);
98+
this.onDateRangePickerInputFocus = this.onDateRangePickerInputFocus.bind(this);
99+
this.onDayPickerFocus = this.onDayPickerFocus.bind(this);
100+
this.onDayPickerBlur = this.onDayPickerBlur.bind(this);
97101

98102
this.responsivizePickerPosition = this.responsivizePickerPosition.bind(this);
99103
}
@@ -108,6 +112,10 @@ export default class DateRangePicker extends React.Component {
108112
this.responsivizePickerPosition();
109113
}
110114

115+
componentWillReceiveProps(nextProps) {
116+
// set daterangepickinputfocused to true if focusedinput went from nothing to something
117+
}
118+
111119
shouldComponentUpdate(nextProps, nextState) {
112120
return shallowCompare(this, nextProps, nextState);
113121
}
@@ -127,17 +135,37 @@ export default class DateRangePicker extends React.Component {
127135
const { onFocusChange } = this.props;
128136
if (!this.isOpened()) return;
129137

138+
this.setState({
139+
isDateRangePickerInputFocused: false,
140+
isDayPickerFocused: false,
141+
});
142+
130143
onFocusChange(null);
131144
}
132145

133-
onFocusDayPicker() {
146+
onDateRangePickerInputFocus(focusedInput) {
147+
const { onFocusChange } = this.props;
148+
149+
if (focusedInput) {
150+
this.setState({
151+
isDateRangePickerInputFocused: true,
152+
isDayPickerFocused: false,
153+
});
154+
}
155+
156+
onFocusChange(focusedInput);
157+
}
158+
159+
onDayPickerFocus() {
134160
this.setState({
161+
isDateRangePickerInputFocused: false,
135162
isDayPickerFocused: true,
136163
});
137164
}
138165

139-
onBlurDayPicker() {
166+
onDayPickerBlur() {
140167
this.setState({
168+
isDateRangePickerInputFocused: true,
141169
isDayPickerFocused: false,
142170
});
143171
}
@@ -238,6 +266,7 @@ export default class DateRangePicker extends React.Component {
238266
keepOpenOnDateSelect,
239267
renderDay,
240268
initialVisibleMonth,
269+
phrases: { jumpToPrevMonth, jumpToNextMonth },
241270
} = this.props;
242271
const { dayPickerContainerStyles, isDayPickerFocused } = this.state;
243272

@@ -278,7 +307,11 @@ export default class DateRangePicker extends React.Component {
278307
keepOpenOnDateSelect={keepOpenOnDateSelect}
279308
renderDay={renderDay}
280309
isFocused={isDayPickerFocused}
281-
onBlur={this.onBlurDayPicker}
310+
onBlur={this.onDayPickerBlur}
311+
phrases={{
312+
jumpToPrevMonth,
313+
jumpToNextMonth,
314+
}}
282315
/>
283316

284317
{withFullScreenPortal &&
@@ -321,10 +354,10 @@ export default class DateRangePicker extends React.Component {
321354
reopenPickerOnClearDates,
322355
keepOpenOnDateSelect,
323356
onDatesChange,
324-
onFocusChange,
325-
renderDay,
326357
} = this.props;
327358

359+
const { isDateRangePickerInputFocused } = this.state;
360+
328361
const onOutsideClick = (!withPortal && !withFullScreenPortal) ? this.onOutsideClick : undefined;
329362

330363
return (
@@ -352,11 +385,11 @@ export default class DateRangePicker extends React.Component {
352385
isOutsideRange={isOutsideRange}
353386
withFullScreenPortal={withFullScreenPortal}
354387
onDatesChange={onDatesChange}
355-
onFocusChange={onFocusChange}
356-
renderDay={renderDay}
357-
onArrowDown={this.onFocusDayPicker}
388+
onFocusChange={this.onDateRangePickerInputFocus}
389+
onArrowDown={this.onDayPickerFocus}
358390
phrases={phrases}
359391
screenReaderMessage={screenReaderInputMessage}
392+
isFocused={isDateRangePickerInputFocused}
360393
/>
361394

362395
{this.maybeRenderDayPickerWithPortal()}

src/components/DateRangePickerInput.jsx

+16-8
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ const propTypes = forbidExtraProps({
2626
onClearDates: PropTypes.func,
2727
onArrowDown: PropTypes.func,
2828

29-
3029
startDate: PropTypes.string,
3130
startDateValue: PropTypes.string,
3231
endDate: PropTypes.string,
@@ -44,6 +43,7 @@ const propTypes = forbidExtraProps({
4443

4544
// i18n
4645
phrases: PropTypes.shape({
46+
focusStartDate: PropTypes.node,
4747
clearDates: PropTypes.node,
4848
}),
4949
});
@@ -80,7 +80,8 @@ const defaultProps = {
8080

8181
// i18n
8282
phrases: {
83-
clearDates: 'Clear Dates',
83+
focusStartDate: 'Focus on start date',
84+
clearDates: 'Clear dates',
8485
},
8586
};
8687

@@ -137,6 +138,7 @@ export default class DateRangePickerInput extends React.Component {
137138
customInputIcon,
138139
customArrowIcon,
139140
phrases,
141+
isFocused,
140142
} = this.props;
141143

142144
const inputIcon = customInputIcon || (<CalendarIcon />);
@@ -149,12 +151,14 @@ export default class DateRangePickerInput extends React.Component {
149151
})}
150152
>
151153
{(showDefaultInputIcon || customInputIcon !== null) &&
152-
<span
154+
<button
155+
type="button"
153156
className="DateRangePickerInput__calendar-icon"
157+
aria-label={phrases.focusStartDate}
154158
onClick={onStartDateFocus}
155159
>
156160
{inputIcon}
157-
</span>
161+
</button>
158162
}
159163
<DateInput
160164
id={startDateId}
@@ -163,6 +167,7 @@ export default class DateRangePickerInput extends React.Component {
163167
inputValue={startDateValue}
164168
screenReaderMessage={screenReaderMessage}
165169
focused={isStartDateFocused}
170+
hasFocus={isFocused}
166171
disabled={disabled}
167172
required={required}
168173
showCaret={showCaret}
@@ -173,7 +178,11 @@ export default class DateRangePickerInput extends React.Component {
173178
onKeyDownArrowDown={onArrowDown}
174179
/>
175180

176-
<div className="DateRangePickerInput__arrow">
181+
<div
182+
className="DateRangePickerInput__arrow"
183+
aria-hidden="true"
184+
role="presentation"
185+
>
177186
{arrowIcon}
178187
</div>
179188

@@ -184,6 +193,7 @@ export default class DateRangePickerInput extends React.Component {
184193
inputValue={endDateValue}
185194
screenReaderMessage={screenReaderMessage}
186195
focused={isEndDateFocused}
196+
hasFocus={isFocused}
187197
disabled={disabled}
188198
required={required}
189199
showCaret={showCaret}
@@ -197,6 +207,7 @@ export default class DateRangePickerInput extends React.Component {
197207
{showClearDates &&
198208
<button
199209
type="button"
210+
aria-label={phrases.clearDates}
200211
className={cx('DateRangePickerInput__clear-dates', {
201212
'DateRangePickerInput__clear-dates--hide': !(startDate || endDate),
202213
'DateRangePickerInput__clear-dates--hover': isClearDatesHovered,
@@ -205,9 +216,6 @@ export default class DateRangePickerInput extends React.Component {
205216
onMouseLeave={this.onClearDatesMouseLeave}
206217
onClick={onClearDates}
207218
>
208-
<span className="screen-reader-only">
209-
{phrases.clearDates}
210-
</span>
211219
<CloseButton />
212220
</button>
213221
}

src/components/DateRangePickerInputController.jsx

+2
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ export default class DateRangePickerInputWithHandlers extends React.Component {
210210
required,
211211
phrases,
212212
onArrowDown,
213+
isFocused,
213214
} = this.props;
214215

215216
const startDateString = this.getDateString(startDate);
@@ -246,6 +247,7 @@ export default class DateRangePickerInputWithHandlers extends React.Component {
246247
onClearDates={this.clearDates}
247248
screenReaderMessage={screenReaderMessage}
248249
onArrowDown={onArrowDown}
250+
isFocused={isFocused}
249251
/>
250252
);
251253
}

0 commit comments

Comments
 (0)