diff --git a/src/time.jsx b/src/time.jsx
index a79f3b0bc..692d9bacb 100644
--- a/src/time.jsx
+++ b/src/time.jsx
@@ -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);
}
@@ -175,27 +190,39 @@ export default class Time extends React.Component {
}
}
- return times.map((time, i) => (
-
{
- 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 (
+ {
+ 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)}
-
- ));
+ >
+ {formatDate(time, format, this.props.locale)}
+
+ );
+ });
};
render() {
@@ -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()}
diff --git a/test/time_format_test.js b/test/time_format_test.js
index 1d79ef390..e7a4e612a 100644
--- a/test/time_format_test.js
+++ b/test/time_format_test.js
@@ -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(
+
+ );
+
+ 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(
{
expect(timeListItem.at(0).prop("aria-selected")).to.be.undefined;
});
+ it("should disable keyboard focus on a regular item", () => {
+ var timeComponent = mount(
+
+ );
+
+ 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(
+
+ );
+
+ 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(