Skip to content

Commit

Permalink
Merge pull request #3726 from RoryStokes/master
Browse files Browse the repository at this point in the history
Enable keyboard navigation of quarters (fixes #3464)
  • Loading branch information
martijnrusschen authored Jan 30, 2023
2 parents 90802d2 + c49fb8e commit d67b2ba
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 2 deletions.
5 changes: 4 additions & 1 deletion src/date_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import addHours from "date-fns/addHours";
import addDays from "date-fns/addDays";
import addWeeks from "date-fns/addWeeks";
import addMonths from "date-fns/addMonths";
import addQuarters from "date-fns/addQuarters";
import addYears from "date-fns/addYears";
import subMinutes from "date-fns/subMinutes";
import subHours from "date-fns/subHours";
import subDays from "date-fns/subDays";
import subWeeks from "date-fns/subWeeks";
import subMonths from "date-fns/subMonths";
import subQuarters from "date-fns/subQuarters";
import subYears from "date-fns/subYears";
import getSeconds from "date-fns/getSeconds";
import getMinutes from "date-fns/getMinutes";
Expand Down Expand Up @@ -221,7 +223,7 @@ export function getEndOfMonth(date) {

// *** Addition ***

export { addMinutes, addDays, addWeeks, addMonths, addYears };
export { addMinutes, addDays, addWeeks, addMonths, addQuarters, addYears };

// *** Subtraction ***

Expand All @@ -232,6 +234,7 @@ export {
subDays,
subWeeks,
subMonths,
subQuarters,
subYears,
};

Expand Down
64 changes: 63 additions & 1 deletion src/month.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export default class Month extends React.Component {
};

MONTH_REFS = [...Array(12)].map(() => React.createRef());
QUARTER_REFS = [...Array(4)].map(() => React.createRef());

isDisabled = (date) => utils.isDayDisabled(date, this.props);

Expand Down Expand Up @@ -141,6 +142,10 @@ export default class Month extends React.Component {
utils.getYear(day) === utils.getYear(utils.newDate()) &&
m === utils.getMonth(utils.newDate());

isCurrentQuarter = (day, q) =>
utils.getYear(day) === utils.getYear(utils.newDate()) &&
q === utils.getQuarter(utils.newDate());

isSelectedMonth = (day, m, selected) =>
utils.getMonth(day) === m && utils.getYear(day) === utils.getYear(selected);

Expand Down Expand Up @@ -290,6 +295,37 @@ export default class Month extends React.Component {
);
};

handleQuarterNavigation = (newQuarter, newDate) => {
if (this.isDisabled(newDate) || this.isExcluded(newDate)) return;
this.props.setPreSelection(newDate);
this.QUARTER_REFS[newQuarter - 1].current &&
this.QUARTER_REFS[newQuarter - 1].current.focus();
};

onQuarterKeyDown = (event, quarter) => {
const eventKey = event.key;
if (!this.props.disabledKeyboardNavigation) {
switch (eventKey) {
case "Enter":
this.onQuarterClick(event, quarter);
this.props.setPreSelection(this.props.selected);
break;
case "ArrowRight":
this.handleQuarterNavigation(
quarter === 4 ? 1 : quarter + 1,
utils.addQuarters(this.props.preSelection, 1)
);
break;
case "ArrowLeft":
this.handleQuarterNavigation(
quarter === 1 ? 4 : quarter - 1,
utils.subQuarters(this.props.preSelection, 1)
);
break;
}
}
};

getMonthClassNames = (m) => {
const {
day,
Expand Down Expand Up @@ -343,6 +379,16 @@ export default class Month extends React.Component {
return tabIndex;
};

getQuarterTabIndex = (q) => {
const preSelectedQuarter = utils.getQuarter(this.props.preSelection);
const tabIndex =
!this.props.disabledKeyboardNavigation && q === preSelectedQuarter
? "0"
: "-1";

return tabIndex;
};

getAriaLabel = (month) => {
const {
chooseDayAriaLabelPrefix = "Choose",
Expand All @@ -360,7 +406,15 @@ export default class Month extends React.Component {
};

getQuarterClassNames = (q) => {
const { day, startDate, endDate, selected, minDate, maxDate } = this.props;
const {
day,
startDate,
endDate,
selected,
minDate,
maxDate,
preSelection,
} = this.props;
return classnames(
"react-datepicker__quarter-text",
`react-datepicker__quarter-${q}`,
Expand All @@ -373,6 +427,8 @@ export default class Month extends React.Component {
q,
selected
),
"react-datepicker__quarter-text--keyboard-selected":
utils.getQuarter(preSelection) === q,
"react-datepicker__quarter--in-range": utils.isQuarterInRange(
startDate,
endDate,
Expand Down Expand Up @@ -454,12 +510,18 @@ export default class Month extends React.Component {
{quarters.map((q, j) => (
<div
key={j}
ref={this.QUARTER_REFS[j]}
role="option"
onClick={(ev) => {
this.onQuarterClick(ev, q);
}}
onKeyDown={(ev) => {
this.onQuarterKeyDown(ev, q);
}}
className={this.getQuarterClassNames(q)}
aria-selected={this.isSelectedQuarter(day, q, selected)}
tabIndex={this.getQuarterTabIndex(q)}
aria-current={this.isCurrentQuarter(day, q) ? "date" : undefined}
>
{utils.getQuarterShortInLocale(q, this.props.locale)}
</div>
Expand Down
66 changes: 66 additions & 0 deletions test/month_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,20 @@ describe("Month", () => {
);
});

it("should enable keyboard focus on the preselected component", () => {
const monthComponent = mount(
<Month
preSelection={utils.newDate("2015-02-01")}
day={utils.newDate("2015-02-01")}
startDate={utils.newDate("2015-01-01")}
endDate={utils.newDate("2015-08-01")}
showQuarterYearPicker
/>
);
const quarter = monthComponent.find(".react-datepicker__quarter-1");
expect(quarter.prop("tabIndex")).to.equal("0");
});

it("should render full month name", () => {
const monthComponent = mount(
<Month
Expand All @@ -572,6 +586,58 @@ describe("Month", () => {
expect(month.text()).to.equal("Feb");
});

describe("Keyboard navigation", () => {
const renderQuarters = (props) =>
shallow(<Month showQuarterYearPicker {...props} />);

it("should trigger setPreSelection and set Q3 as pre-selected on arrowRight", () => {
let preSelected = false;
const setPreSelection = (param) => {
preSelected = param;
};

const quartersComponent = renderQuarters({
selected: utils.newDate("2015-04-01"),
day: utils.newDate("2015-04-01"),
setPreSelection: setPreSelection,
preSelection: utils.newDate("2015-04-01"),
});
quartersComponent
.find(".react-datepicker__quarter-2")
.simulate("keydown", getKey("Tab"));
quartersComponent
.find(".react-datepicker__quarter-2")
.simulate("keydown", getKey("ArrowRight"));

expect(preSelected.toString()).to.equal(
utils.newDate("2015-07-01").toString()
);
});

it("should trigger setPreSelection and set Q1 as pre-selected on arrowLeft", () => {
let preSelected = false;
const setPreSelection = (param) => {
preSelected = param;
};
const quartersComponent = renderQuarters({
selected: utils.newDate("2015-04-01"),
day: utils.newDate("2015-04-01"),
setPreSelection: setPreSelection,
preSelection: utils.newDate("2015-04-01"),
});
quartersComponent
.find(".react-datepicker__quarter-2")
.simulate("keydown", getKey("Tab"));
quartersComponent
.find(".react-datepicker__quarter-2")
.simulate("keydown", getKey("ArrowLeft"));

expect(preSelected.toString()).to.equal(
utils.newDate("2015-01-01").toString()
);
});
});

describe("Keyboard navigation", () => {
const renderMonth = (props) =>
mount(<Month showMonthYearPicker {...props} />);
Expand Down

0 comments on commit d67b2ba

Please sign in to comment.