Skip to content

Commit

Permalink
Merge pull request #4181 from theopolisme/feature/time-input-accessib…
Browse files Browse the repository at this point in the history
…ility

Improve time picker accessibility
  • Loading branch information
martijnrusschen authored Jul 31, 2023
2 parents 2f840eb + be6af4c commit e7ee394
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 21 deletions.
70 changes: 49 additions & 21 deletions src/time.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,21 @@ export default class Time extends React.Component {
event.key = "Enter";
}

if (
(event.key === "ArrowUp" || event.key === "ArrowLeft") &&
event.target.previousSibling
) {
event.preventDefault();
event.target.previousSibling.focus();
}
if (
(event.key === "ArrowDown" || event.key === "ArrowRight") &&
event.target.nextSibling
) {
event.preventDefault();
event.target.nextSibling.focus();
}

if (event.key === "Enter") {
this.handleClick(time);
}
Expand Down Expand Up @@ -175,27 +190,39 @@ export default class Time extends React.Component {
}
}

return times.map((time, i) => (
<li
key={i}
onClick={this.handleClick.bind(this, time)}
className={this.liClasses(time, currH, currM)}
ref={(li) => {
if (isBefore(time, activeTime) || isEqual(time, activeTime)) {
this.centerLi = li;
// Determine which time to focus and scroll into view when component mounts
const timeToFocus = times.reduce((prev, time) => {
if (isBefore(time, activeTime) || isEqual(time, activeTime)) {
return time;
} else {
return prev;
}
}, times[0]);

return times.map((time, i) => {
return (
<li
key={i}
onClick={this.handleClick.bind(this, time)}
className={this.liClasses(time, currH, currM)}
ref={(li) => {
if (time === timeToFocus) {
this.centerLi = li;
}
}}
onKeyDown={(ev) => {
this.handleOnKeyDown(ev, time);
}}
tabIndex={time === timeToFocus ? "0" : "-1"}
role="option"
aria-selected={
this.isSelectedTime(time, currH, currM) ? "true" : undefined
}
}}
onKeyDown={(ev) => {
this.handleOnKeyDown(ev, time);
}}
tabIndex="0"
aria-selected={
this.isSelectedTime(time, currH, currM) ? "true" : undefined
}
>
{formatDate(time, format, this.props.locale)}
</li>
));
>
{formatDate(time, format, this.props.locale)}
</li>
);
});
};

render() {
Expand Down Expand Up @@ -231,7 +258,8 @@ export default class Time extends React.Component {
this.list = list;
}}
style={height ? { height } : {}}
tabIndex="0"
role="listbox"
aria-label={this.props.timeCaption}
>
{this.renderTimes()}
</ul>
Expand Down
48 changes: 48 additions & 0 deletions test/time_format_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,21 @@ describe("TimeComponent", () => {
expect(timeListItem.at(0).prop("aria-selected")).to.eq("true");
});

it("should enable keyboard focus on the selected item", () => {
var timeComponent = mount(
<TimeComponent
format="HH:mm"
selected={new Date("1990-06-14 08:00")}
openToDate={new Date("1990-06-14 09:00")}
/>
);

var timeListItem = timeComponent.find(
".react-datepicker__time-list-item--selected"
);
expect(timeListItem.at(0).prop("tabIndex")).to.equal("0");
});

it("should not add the aria-selected property to a regular item", () => {
var timeComponent = mount(
<TimeComponent
Expand All @@ -125,6 +140,39 @@ describe("TimeComponent", () => {
expect(timeListItem.at(0).prop("aria-selected")).to.be.undefined;
});

it("should disable keyboard focus on a regular item", () => {
var timeComponent = mount(
<TimeComponent
format="HH:mm"
selected={new Date("1990-06-14 08:00")}
openToDate={new Date("1990-06-14 09:00")}
/>
);

var timeListItem = timeComponent.find(
".react-datepicker__time-list-item"
);
expect(timeListItem.at(0).prop("tabIndex")).to.equal("-1");
});

it("when no selected time, should focus the time closest to the opened time", () => {
var timeComponent = mount(
<TimeComponent
format="HH:mm"
openToDate={new Date("1990-06-14 09:11")}
/>
);

var timeListItem = timeComponent.find(
".react-datepicker__time-list-item"
);
expect(
timeListItem
.findWhere((node) => node.type() && node.text() === "09:00")
.prop("tabIndex")
).to.equal("0");
});

it("when no selected time, should call calcCenterPosition with centerLi ref, closest to the opened time", () => {
mount(
<TimeComponent
Expand Down

0 comments on commit e7ee394

Please sign in to comment.