Skip to content

Commit c6c0fe7

Browse files
committed
Keyboard accessibility improvement #245 #260 #290
1 parent 929e617 commit c6c0fe7

File tree

17 files changed

+257
-122
lines changed

17 files changed

+257
-122
lines changed

README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -621,28 +621,34 @@ export default function Example() {
621621
</tr>
622622
<tr>
623623
<td>mobileButtons</td>
624-
<td style="text-align:center"> HTMLButtonElement[]</td>
624+
<td style="text-align:center">HTMLButtonElement[]</td>
625625
<td style="text-align:center">[]</td>
626626
<td>DatePicker</td>
627627
</tr>
628628
<tr>
629629
<td>dateSeparator</td>
630-
<td style="text-align:center"> string</td>
630+
<td style="text-align:center">String</td>
631631
<td style="text-align:center">'~' in range mode, ',' in multiple mode</td>
632632
<td>DatePicker</td>
633633
</tr>
634634
<tr>
635635
<td>multipleRangeSeparator</td>
636-
<td style="text-align:center"> string</td>
636+
<td style="text-align:center">String</td>
637637
<td style="text-align:center">','</td>
638638
<td>DatePicker</td>
639639
</tr>
640-
<tr>
640+
<tr>
641641
<td>typingTimeout</td>
642-
<td style="text-align:center"> string</td>
642+
<td style="text-align:center">String</td>
643643
<td style="text-align:center">700</td>
644644
<td>DatePicker</td>
645645
</tr>
646+
<tr>
647+
<td>autoFocus</td>
648+
<td style="text-align:center">Boolean</td>
649+
<td style="text-align:center">false</td>
650+
<td>Calendar</td>
651+
</tr>
646652
</tbody>
647653
</table>
648654

changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 4.5.0
2+
3+
- Keyboard accessibility improvement [#290](https://github.com/shahabyazdi/react-multi-date-picker/issues/290), [#245](https://github.com/shahabyazdi/react-multi-date-picker/issues/245), [#260](https://github.com/shahabyazdi/react-multi-date-picker/issues/260)
4+
15
# 4.4.2
26

37
- Added isOpen type to DatePickerRef [#246](https://github.com/shahabyazdi/react-multi-date-picker/issues/246)

index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ declare module "react-multi-date-picker" {
329329
formatYear?: (year: string, month: string) => string;
330330
highlightToday?: boolean;
331331
headerOrder?: Array<HeaderItem>;
332+
autoFocus?: boolean;
332333
}
333334

334335
export interface DatePickerProps {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-multi-date-picker",
3-
"version": "4.4.2",
3+
"version": "4.5.0",
44
"description": "A simple React datepicker component for working with gregorian, persian, arabic and indian calendars with the ability to select the date by single, multiple, range and multiple range pickers.",
55
"main": "./build/index.js",
66
"types": "index.d.ts",

src/components/arrow/arrow.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import React from "react";
22

3-
export default function Arrow({ direction, onClick, disabled }) {
3+
export default function Arrow({ direction, onClick, disabled, onKeyDown }) {
44
return (
55
<button
66
type="button"
77
className={`rmdp-arrow-container ${direction} ${
88
disabled ? "disabled" : ""
99
}`}
1010
onClick={onClick}
11+
onKeyDown={onKeyDown}
1112
aria-roledescription={`button to navigate ${direction.replace(
1213
"rmdp-",
1314
""

src/components/calendar/calendar.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,18 @@
7373
color: black;
7474
}
7575

76+
.rmdp-calendar *:focus {
77+
outline-color: #00539c;
78+
}
79+
80+
.rmdp-day:not(.rmdp-range):focus {
81+
border-radius: 50%;
82+
}
83+
84+
.rmdp-ym .rmdp-day:not(.rmdp-range):focus {
85+
border-radius: 15px;
86+
}
87+
7688
.rmdp-week-day {
7789
cursor: default;
7890
color: var(--rmdp-primary);

src/components/calendar/calendar.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import isArray from "../../shared/isArray";
1717
import check from "../../shared/check";
1818
import toLocaleDigits from "../../shared/toLocaleDigits";
1919
import isRTL from "../../shared/isRTL";
20+
import { findFocusable } from "../../shared/handleFocus";
2021
import "./calendar.css";
2122

2223
function Calendar(
@@ -75,6 +76,7 @@ function Calendar(
7576
highlightToday = true,
7677
headerOrder = ["LEFT_BUTTON", "MONTH_YEAR", "RIGHT_BUTTON"],
7778
style = {},
79+
autoFocus = false,
7880
},
7981
outerRef
8082
) {
@@ -302,6 +304,14 @@ function Calendar(
302304
},
303305
{ datePickerProps, DatePicker, ...calendarProps } = arguments[0];
304306

307+
useEffect(() => {
308+
const { Calendar } = ref.current;
309+
310+
if (!Calendar) return;
311+
312+
findFocusable(Calendar, undefined, autoFocus && !DatePicker);
313+
}, [autoFocus, state.today, DatePicker]);
314+
305315
initPlugins();
306316

307317
return state.today ? (

src/components/day_picker/day_picker.js

Lines changed: 12 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
1-
import React, { useMemo, useRef } from "react";
1+
import React, { useMemo, useRef, useState } from "react";
22
import DateObject from "react-date-object";
33
import WeekDays from "../week_days/week_days";
44
import selectDate from "../../shared/selectDate";
55
import isSameDate from "../../shared/isSameDate";
66
import getRangeClass from "../../shared/getRangeClass";
77
import getRangeHoverClass from "../../shared/getRangeHoverClass";
8-
import { useState } from "react";
8+
import handleFocus from "../../shared/handleFocus";
99

1010
const ariaLabelFormat = "dddd MMMM DD of YYYY";
1111

12-
let timeout;
13-
1412
export default function DayPicker({
1513
state,
16-
setState,
1714
onChange,
1815
showOtherDays = false,
1916
mapDays,
@@ -44,11 +41,11 @@ export default function DayPicker({
4441
selectedDate,
4542
onlyMonthPicker,
4643
onlyYearPicker,
44+
mustShowMonthPicker,
45+
mustShowYearPicker,
4746
} = state,
4847
mustShowDayPicker = !onlyMonthPicker && !onlyYearPicker,
49-
[dateHovered, setDateHovered] = useState(),
50-
isDateSelected =
51-
multiple || range ? selectedDate?.length > 0 : !!selectedDate;
48+
[dateHovered, setDateHovered] = useState();
5249

5350
ref.current.date = date;
5451

@@ -80,6 +77,9 @@ export default function DayPicker({
8077
className={`rmdp-day-picker ${fullYear ? "rmdp-full-year" : ""}`}
8178
style={{ display: fullYear ? "grid" : "flex" }}
8279
onMouseLeave={() => rangeHover && setDateHovered()}
80+
data-active={
81+
mustShowDayPicker && !mustShowMonthPicker && !mustShowYearPicker
82+
}
8383
>
8484
{months.map((weeks, monthIndex) => (
8585
<div
@@ -134,23 +134,20 @@ export default function DayPicker({
134134
className = className.replace("sd", "");
135135
}
136136

137-
const hasTabIndex = isDateSelected
138-
? parentClassName.includes("selected") ||
139-
parentClassName.includes("range")
140-
: parentClassName.includes("today");
141-
142137
return (
143138
<div
144139
key={i}
145-
tabIndex={hasTabIndex ? 0 : -1}
140+
tabIndex={-1}
146141
aria-label={`Choose ${object.date.format(
147142
ariaLabelFormat
148143
)}`}
149144
className={parentClassName}
150145
onMouseEnter={() =>
151146
rangeHover && setDateHovered(object.date)
152147
}
153-
onKeyDown={(e) => handleKeyDown(e, object)}
148+
onKeyDown={(e) =>
149+
handleFocus(e, object, { format: ariaLabelFormat })
150+
}
154151
onClick={() => {
155152
if (!mustDisplayDay(object) || object.disabled) {
156153
return;
@@ -304,49 +301,6 @@ export default function DayPicker({
304301

305302
return allProps;
306303
}
307-
308-
function handleKeyDown(e, object) {
309-
const { currentTarget, key, code } = e;
310-
const numbers = { ArrowRight: 1, ArrowLeft: -1, ArrowUp: -7, ArrowDown: 7 };
311-
312-
if (code === "Space" || key === " ") {
313-
e.preventDefault();
314-
currentTarget.click();
315-
} else if (Object.keys(numbers).includes(key)) {
316-
e.preventDefault();
317-
318-
const number = numbers[key];
319-
const date = new DateObject(object.date).add(number, "day");
320-
const div = getNode(date);
321-
322-
focus(div);
323-
324-
function focus(node) {
325-
if (!node) return next();
326-
327-
const classes = node.getAttribute("class");
328-
329-
if (!classes.includes("hidden") && !classes.includes("disabled")) {
330-
node.focus();
331-
} else {
332-
next();
333-
}
334-
}
335-
336-
function next() {
337-
setState({ ...state, date });
338-
clearTimeout(timeout);
339-
340-
timeout = setTimeout(() => focus(getNode(date)), 100);
341-
}
342-
}
343-
}
344-
345-
function getNode(date) {
346-
return divRef.current.querySelector(
347-
`[aria-label*='${date.format(ariaLabelFormat)}']`
348-
);
349-
}
350304
}
351305

352306
function getMonths(date, showOtherDays, numberOfMonths, weekStartDayIndex) {

0 commit comments

Comments
 (0)