Skip to content

Commit a104b94

Browse files
Merge pull request #5808 from Hacker0x01/feat/jest-axe-accessibility-improvements
Add jest-axe Accessibility Testing and Fix ARIA Structure Issues
2 parents c7a14dd + 607fd60 commit a104b94

File tree

9 files changed

+506
-42
lines changed

9 files changed

+506
-42
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"@testing-library/user-event": "14.6.1",
5656
"@types/eslint": "^9.6.1",
5757
"@types/jest": "^30.0.0",
58+
"@types/jest-axe": "^3.5.9",
5859
"@types/node": "22.15.30",
5960
"@types/react": "^19.1.0",
6061
"@types/react-dom": "^19.1.2",
@@ -73,6 +74,7 @@
7374
"eslint-plugin-unused-imports": "^4.1.4",
7475
"husky": "9.1.7",
7576
"jest": "^30.0.5",
77+
"jest-axe": "^10.0.0",
7678
"jest-canvas-mock": "^2.5.2",
7779
"jest-environment-jsdom": "^29.7.0",
7880
"lint-staged": "^16.0.0",

src/calendar.tsx

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,12 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
839839
);
840840
};
841841

842+
renderDayNamesHeader = (monthDate: Date) => (
843+
<div className="react-datepicker__day-names" role="row">
844+
{this.header(monthDate)}
845+
</div>
846+
);
847+
842848
renderDefaultHeader = ({ monthDate, i }: { monthDate: Date; i: number }) => (
843849
<div
844850
className={`react-datepicker__header ${
@@ -856,9 +862,6 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
856862
{this.renderMonthYearDropdown(i !== 0)}
857863
{this.renderYearDropdown(i !== 0)}
858864
</div>
859-
<div className="react-datepicker__day-names" role="row">
860-
{this.header(monthDate)}
861-
</div>
862865
</div>
863866
);
864867

@@ -906,11 +909,6 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
906909
this.props,
907910
);
908911

909-
const showDayNames =
910-
!this.props.showMonthYearPicker &&
911-
!this.props.showQuarterYearPicker &&
912-
!this.props.showYearPicker;
913-
914912
return (
915913
<div
916914
className="react-datepicker__header react-datepicker__header--custom"
@@ -932,11 +930,6 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
932930
prevYearButtonDisabled,
933931
nextYearButtonDisabled,
934932
})}
935-
{showDayNames && (
936-
<div className="react-datepicker__day-names">
937-
{this.header(monthDate)}
938-
</div>
939-
)}
940933
</div>
941934
);
942935
};
@@ -1030,6 +1023,7 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
10301023
selectingDate={this.state.selectingDate}
10311024
monthShowsDuplicateDaysEnd={monthShowsDuplicateDaysEnd}
10321025
monthShowsDuplicateDaysStart={monthShowsDuplicateDaysStart}
1026+
dayNamesHeader={this.renderDayNamesHeader(monthDate)}
10331027
/>
10341028
</div>,
10351029
);

src/day.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ export default class Day extends Component<DayProps> {
597597
}
598598
tabIndex={this.getTabIndex()}
599599
aria-label={this.getAriaLabel()}
600-
role="option"
600+
role="gridcell"
601601
title={this.getTitle()}
602602
aria-disabled={this.isDisabled()}
603603
aria-current={this.isCurrentDay() ? "date" : undefined}

src/month.tsx

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ interface MonthProps
140140
weekAriaLabelPrefix?: WeekProps["ariaLabelPrefix"];
141141
chooseDayAriaLabelPrefix?: WeekProps["chooseDayAriaLabelPrefix"];
142142
disabledDayAriaLabelPrefix?: WeekProps["disabledDayAriaLabelPrefix"];
143+
dayNamesHeader?: React.ReactNode;
143144
}
144145

145146
/**
@@ -1101,23 +1102,45 @@ export default class Month extends Component<MonthProps> {
11011102
? ariaLabelPrefix.trim() + " "
11021103
: "";
11031104

1105+
const shouldUseListboxRole = showMonthYearPicker || showQuarterYearPicker;
1106+
1107+
if (shouldUseListboxRole) {
1108+
return (
1109+
<div
1110+
className={this.getClassNames()}
1111+
onMouseLeave={
1112+
!this.props.usePointerEvent ? this.handleMouseLeave : undefined
1113+
}
1114+
onPointerLeave={
1115+
this.props.usePointerEvent ? this.handleMouseLeave : undefined
1116+
}
1117+
aria-label={`${formattedAriaLabelPrefix}${formatDate(day, "MMMM, yyyy", this.props.locale)}`}
1118+
role="listbox"
1119+
>
1120+
{showMonthYearPicker ? this.renderMonths() : this.renderQuarters()}
1121+
</div>
1122+
);
1123+
}
1124+
1125+
// For regular calendar view, use table structure
11041126
return (
1105-
<div
1106-
className={this.getClassNames()}
1107-
onMouseLeave={
1108-
!this.props.usePointerEvent ? this.handleMouseLeave : undefined
1109-
}
1110-
onPointerLeave={
1111-
this.props.usePointerEvent ? this.handleMouseLeave : undefined
1112-
}
1113-
aria-label={`${formattedAriaLabelPrefix}${formatDate(day, "MMMM, yyyy", this.props.locale)}`}
1114-
role="listbox"
1115-
>
1116-
{showMonthYearPicker
1117-
? this.renderMonths()
1118-
: showQuarterYearPicker
1119-
? this.renderQuarters()
1120-
: this.renderWeeks()}
1127+
<div role="table">
1128+
{this.props.dayNamesHeader && (
1129+
<div role="rowgroup">{this.props.dayNamesHeader}</div>
1130+
)}
1131+
<div
1132+
className={this.getClassNames()}
1133+
onMouseLeave={
1134+
!this.props.usePointerEvent ? this.handleMouseLeave : undefined
1135+
}
1136+
onPointerLeave={
1137+
this.props.usePointerEvent ? this.handleMouseLeave : undefined
1138+
}
1139+
aria-label={`${formattedAriaLabelPrefix}${formatDate(day, "MMMM, yyyy", this.props.locale)}`}
1140+
role="rowgroup"
1141+
>
1142+
{this.renderWeeks()}
1143+
</div>
11211144
</div>
11221145
);
11231146
}

0 commit comments

Comments
 (0)