Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/month_year_dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,9 @@ export default class MonthYearDropdown extends Component<
);

return (
<div
<button
key="read"
type="button"
style={{ visibility: visible ? "visible" : "hidden" }}
className="react-datepicker__month-year-read-view"
onClick={this.toggleDropdown}
Expand All @@ -101,7 +102,7 @@ export default class MonthYearDropdown extends Component<
<span className="react-datepicker__month-year-read-view--selected-month-year">
{yearMonth}
</span>
</div>
</button>
);
};

Expand Down
40 changes: 39 additions & 1 deletion src/month_year_dropdown_options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,23 +72,61 @@ export default class MonthYearDropdownOptions extends Component<
};
}

monthYearOptionButtonsRef: Record<number, HTMLDivElement | null> = {};

handleOptionKeyDown = (
i: number,
monthYearPoint: number,
e: React.KeyboardEvent,
): void => {
switch (e.key) {
case "Enter":
e.preventDefault();
this.onChange(monthYearPoint);
break;
case "Escape":
e.preventDefault();
this.props.onCancel();
break;
case "ArrowUp":
case "ArrowDown": {
e.preventDefault();
const newIndex = i + (e.key === "ArrowUp" ? -1 : 1);
this.monthYearOptionButtonsRef[newIndex]?.focus();
break;
}
}
};

renderOptions = (): React.ReactElement[] => {
// Clear refs to prevent memory leaks on re-render
this.monthYearOptionButtonsRef = {};

return this.state.monthYearsList.map<React.ReactElement>(
(monthYear: Date): React.ReactElement => {
(monthYear: Date, i: number): React.ReactElement => {
const monthYearPoint = getTime(monthYear);
const isSameMonthYear =
isSameYear(this.props.date, monthYear) &&
isSameMonth(this.props.date, monthYear);

return (
<div
ref={(el) => {
this.monthYearOptionButtonsRef[i] = el;
if (isSameMonthYear) {
el?.focus();
}
}}
role="button"
tabIndex={0}
className={
isSameMonthYear
? "react-datepicker__month-year-option--selected_month-year"
: "react-datepicker__month-year-option"
}
key={monthYearPoint}
onClick={this.onChange.bind(this, monthYearPoint)}
onKeyDown={this.handleOptionKeyDown.bind(this, i, monthYearPoint)}
aria-selected={isSameMonthYear ? "true" : undefined}
>
{isSameMonthYear ? (
Expand Down
130 changes: 130 additions & 0 deletions src/test/month_year_dropdown_test.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ describe("MonthYearDropdown", () => {
);
});

it("renders the read view as a button so it is reachable via Tab, matching MonthDropdown/YearDropdown", () => {
const monthYearReadView = safeQuerySelector(
monthYearDropdown,
".react-datepicker__month-year-read-view",
);
expect(monthYearReadView.tagName).toBe("BUTTON");
});

it("opens a list when read view is clicked", () => {
const monthYearReadView = safeQuerySelector(
monthYearDropdown,
Expand Down Expand Up @@ -201,6 +209,128 @@ describe("MonthYearDropdown", () => {
expect(handleChangeResult?.toString()).toBe(expected_date.toString());
});

it("focuses the selected month year option when the dropdown opens", () => {
const monthYearReadView = safeQuerySelector(
monthYearDropdown,
".react-datepicker__month-year-read-view",
);
fireEvent.click(monthYearReadView);

const selectedOption = safeQuerySelector(
monthYearDropdown,
".react-datepicker__month-year-option--selected_month-year",
);
expect(document.activeElement).toEqual(selectedOption);
});

it("calls the supplied onChange function when a month year is selected using arrows and enter key", () => {
const monthYearReadView = safeQuerySelector(
monthYearDropdown,
".react-datepicker__month-year-read-view",
);
fireEvent.click(monthYearReadView);

const monthYearOptions = safeQuerySelectorAll(
monthYearDropdown,
'.react-datepicker__month-year-dropdown [role="button"]',
12,
);

const selectedOption = monthYearOptions[6]!;
fireEvent.keyDown(selectedOption, { key: "ArrowDown" });

const nextOption = monthYearOptions[7];
expect(document.activeElement).toEqual(nextOption);

fireEvent.keyDown(document.activeElement!, { key: "Enter" });
expect(handleChangeResult?.toString()).toBe(
newDate("2018-02").toString(),
);
});

it("handles ArrowUp key navigation correctly", () => {
const monthYearReadView = safeQuerySelector(
monthYearDropdown,
".react-datepicker__month-year-read-view",
);
fireEvent.click(monthYearReadView);

const monthYearOptions = safeQuerySelectorAll(
monthYearDropdown,
'.react-datepicker__month-year-dropdown [role="button"]',
12,
);

const selectedOption = monthYearOptions[6]!;
fireEvent.keyDown(selectedOption, { key: "ArrowUp" });

const prevOption = monthYearOptions[5];
expect(document.activeElement).toEqual(prevOption);
});

it("handles Escape key to cancel dropdown", () => {
const monthYearReadView = safeQuerySelector(
monthYearDropdown,
".react-datepicker__month-year-read-view",
);
fireEvent.click(monthYearReadView);

const monthYearOptions = safeQuerySelectorAll(
monthYearDropdown,
'.react-datepicker__month-year-dropdown [role="button"]',
12,
);

const selectedOption = monthYearOptions[6]!;
fireEvent.keyDown(selectedOption, { key: "Escape" });

expect(
monthYearDropdown?.querySelectorAll(
".react-datepicker__month-year-dropdown",
),
).toHaveLength(0);
});

it("does not move focus past the last option when pressing ArrowDown", () => {
const monthYearReadView = safeQuerySelector(
monthYearDropdown,
".react-datepicker__month-year-read-view",
);
fireEvent.click(monthYearReadView);

const monthYearOptions = safeQuerySelectorAll(
monthYearDropdown,
'.react-datepicker__month-year-dropdown [role="button"]',
12,
);

const lastOption = monthYearOptions[11]!;
lastOption.focus();
fireEvent.keyDown(lastOption, { key: "ArrowDown" });

expect(document.activeElement).toEqual(lastOption);
});

it("does not move focus before the first option when pressing ArrowUp", () => {
const monthYearReadView = safeQuerySelector(
monthYearDropdown,
".react-datepicker__month-year-read-view",
);
fireEvent.click(monthYearReadView);

const monthYearOptions = safeQuerySelectorAll(
monthYearDropdown,
'.react-datepicker__month-year-dropdown [role="button"]',
12,
);

const firstOption = monthYearOptions[0]!;
firstOption.focus();
fireEvent.keyDown(firstOption, { key: "ArrowUp" });

expect(document.activeElement).toEqual(firstOption);
});

it("should use dateFormat to display date in dropdown", () => {
registerLocale("fi", fi);
let dropdownDateFormat = getMonthYearDropdown({
Expand Down
Loading