Skip to content

Commit 1fc0893

Browse files
committed
♿️ Add accessibility for month dropdown
Closes #6223
1 parent 548a1f3 commit 1fc0893

File tree

3 files changed

+75
-2
lines changed

3 files changed

+75
-2
lines changed

src/month_dropdown.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ export default class MonthDropdown extends Component<
3636
renderSelectOptions = (monthNames: string[]): React.ReactElement[] =>
3737
monthNames.map<React.ReactElement>(
3838
(m: string, i: number): React.ReactElement => (
39-
<option key={m} value={i}>
39+
<option
40+
key={m}
41+
value={i}
42+
aria-label={`Select Month ${m}`}
43+
aria-selected={i === this.props.month ? "true" : "false"}
44+
>
4045
{m}
4146
</option>
4247
),
@@ -47,6 +52,7 @@ export default class MonthDropdown extends Component<
4752
value={this.props.month}
4853
className="react-datepicker__month-select"
4954
onChange={(e) => this.onChange(parseInt(e.target.value))}
55+
aria-label="Select Month"
5056
>
5157
{this.renderSelectOptions(monthNames)}
5258
</select>
@@ -62,8 +68,14 @@ export default class MonthDropdown extends Component<
6268
style={{ visibility: visible ? "visible" : "hidden" }}
6369
className="react-datepicker__month-read-view"
6470
onClick={this.toggleDropdown}
71+
aria-label="Select Month"
72+
aria-expanded={this.state.dropdownVisible}
73+
aria-haspopup="listbox"
6574
>
66-
<span className="react-datepicker__month-read-view--down-arrow" />
75+
<span
76+
className="react-datepicker__month-read-view--down-arrow"
77+
aria-hidden="true"
78+
/>
6779
<span className="react-datepicker__month-read-view--selected-month">
6880
{monthNames[this.props.month]}
6981
</span>

src/month_dropdown_options.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export default class MonthDropdownOptions extends Component<MonthDropdownOptions
5050
}
5151
}}
5252
role="button"
53+
aria-label={`Select Month ${month}`}
5354
tabIndex={0}
5455
className={
5556
this.isSelectedMonth(i)

src/test/month_dropdown_test.test.tsx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,40 @@ describe("MonthDropdown", () => {
4444
monthDropdown = getMonthDropdown();
4545
});
4646

47+
it("sets proper ARIA on read view button and toggles aria-expanded", () => {
48+
const monthReadView = safeQuerySelector(
49+
monthDropdown,
50+
".react-datepicker__month-read-view",
51+
);
52+
expect(monthReadView.getAttribute("aria-label")).toBe("Select Month");
53+
expect(monthReadView.getAttribute("aria-haspopup")).toBe("listbox");
54+
expect(monthReadView.getAttribute("aria-expanded")).toBe("false");
55+
56+
fireEvent.click(monthReadView);
57+
58+
const monthReadViewAfterOpen = safeQuerySelector(
59+
monthDropdown,
60+
".react-datepicker__month-read-view",
61+
);
62+
expect(monthReadViewAfterOpen.getAttribute("aria-expanded")).toBe("true");
63+
});
64+
65+
it("applies aria-label to each month option in scroll dropdown", () => {
66+
const monthReadView = safeQuerySelector(
67+
monthDropdown,
68+
".react-datepicker__month-read-view",
69+
);
70+
fireEvent.click(monthReadView);
71+
72+
const firstOption = safeQuerySelector(
73+
monthDropdown,
74+
".react-datepicker__month-option",
75+
);
76+
expect(firstOption.getAttribute("aria-label")).toBe(
77+
"Select Month January",
78+
);
79+
});
80+
4781
it("shows the selected month in the initial view", () => {
4882
expect(monthDropdown?.textContent).toContain("December");
4983
});
@@ -307,6 +341,14 @@ describe("MonthDropdown", () => {
307341
);
308342
});
309343

344+
it("adds aria-label to select element", () => {
345+
monthDropdown = getMonthDropdown({ dropdownMode: "select" });
346+
const select = monthDropdown.querySelector<HTMLSelectElement>(
347+
".react-datepicker__month-select",
348+
);
349+
expect(select?.getAttribute("aria-label")).toBe("Select Month");
350+
});
351+
310352
it("renders month options with default locale", () => {
311353
monthDropdown = getMonthDropdown({ dropdownMode: "select" });
312354
const options = monthDropdown.querySelectorAll("option");
@@ -325,6 +367,24 @@ describe("MonthDropdown", () => {
325367
"December",
326368
]);
327369
});
370+
// Accessibility of options
371+
it("adds aria-label and aria-selected to options in select mode", () => {
372+
monthDropdown = getMonthDropdown({ dropdownMode: "select", month: 11 });
373+
const select = monthDropdown.querySelector<HTMLSelectElement>(
374+
".react-datepicker__month-select",
375+
);
376+
const options = Array.from(
377+
select?.querySelectorAll("option") ?? [],
378+
) as HTMLOptionElement[];
379+
expect(options[0]?.getAttribute("aria-label")).toBe(
380+
"Select Month January",
381+
);
382+
expect(options[11]?.getAttribute("aria-label")).toBe(
383+
"Select Month December",
384+
);
385+
expect(options[11]?.getAttribute("aria-selected")).toBe("true");
386+
expect(options[0]?.getAttribute("aria-selected")).toBe("false");
387+
});
328388
// Short Month Names
329389
it("renders month options with short name and default locale", () => {
330390
monthDropdown = getMonthDropdown({

0 commit comments

Comments
 (0)