Skip to content

Commit ac223c4

Browse files
committed
fix(material/datepicker): correct structure for grid role
Previously, the grid in mat-datepicker was not strictly following the structure expected for the grid role. Additionally, this change adds an `<abbr>` element to the column headers such that VoiceOver can correctly read column headers. Fixes #21357
1 parent 4caf039 commit ac223c4

File tree

8 files changed

+23
-14
lines changed

8 files changed

+23
-14
lines changed

src/material/datepicker/calendar-body.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,6 @@ export interface MatCalendarUserEvent<D> {
5858
styleUrls: ['calendar-body.css'],
5959
host: {
6060
'class': 'mat-calendar-body',
61-
'role': 'grid',
62-
'aria-readonly': 'true'
6361
},
6462
exportAs: 'matCalendarBody',
6563
encapsulation: ViewEncapsulation.None,

src/material/datepicker/calendar.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,7 @@ $calendar-next-icon-transform: translateX(-2px) rotate(45deg);
122122
height: $calendar-header-divider-width;
123123
}
124124
}
125+
126+
.mat-calendar-abbr {
127+
text-decoration: none;
128+
}

src/material/datepicker/month-view.html

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
<table class="mat-calendar-table" role="presentation">
2-
<thead class="mat-calendar-table-header">
3-
<tr>
4-
<th scope="col" *ngFor="let day of _weekdays" [attr.aria-label]="day.long">{{day.narrow}}</th>
1+
<table class="mat-calendar-table" role="grid">
2+
<thead role="rowgroup" class="mat-calendar-table-header">
3+
<tr role="row">
4+
<!-- For the day-of-the-week column header, we use an `<abbr>` element because VoiceOver
5+
ignores the `aria-label`. ChromeVox, however, does not read the full name
6+
for the `<abbr>`, so we still set `aria-label` on the header element. -->
7+
<th role="columnheader" scope="col" *ngFor="let day of _weekdays" [attr.aria-label]="day.long">
8+
<abbr class="mat-calendar-abbr" [attr.title]="day.long">{{day.narrow}}</abbr>
9+
</th>
510
</tr>
6-
<tr><th class="mat-calendar-table-header-divider" colspan="7" aria-hidden="true"></th></tr>
11+
<tr><th aria-hidden="true" class="mat-calendar-table-header-divider" colspan="7"></th></tr>
712
</thead>
813
<tbody mat-calendar-body
14+
role="rowgroup"
915
[label]="_monthLabel"
1016
[rows]="_weeks"
1117
[todayValue]="_todayDate!"

src/material/datepicker/month-view.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ describe('MatMonthView', () => {
114114
describe('a11y', () => {
115115
it('should set the correct role on the internal table node', () => {
116116
const table = monthViewNativeElement.querySelector('table')!;
117-
expect(table.getAttribute('role')).toBe('presentation');
117+
expect(table.getAttribute('role')).toBe('grid');
118118
});
119119

120120
it('should set the correct scope on the table headers', () => {

src/material/datepicker/multi-year-view.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
<table class="mat-calendar-table" role="presentation">
2-
<thead class="mat-calendar-table-header">
1+
<table class="mat-calendar-table" role="grid">
2+
<thead aria-hidden="true" class="mat-calendar-table-header">
33
<tr><th class="mat-calendar-table-header-divider" colspan="4"></th></tr>
44
</thead>
55
<tbody mat-calendar-body

src/material/datepicker/multi-year-view.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ describe('MatMultiYearView', () => {
104104
describe('a11y', () => {
105105
it('should set the correct role on the internal table node', () => {
106106
const table = multiYearViewNativeElement.querySelector('table')!;
107-
expect(table.getAttribute('role')).toBe('presentation');
107+
expect(table.getAttribute('role')).toBe('grid');
108108
});
109109

110110
describe('calendar body', () => {

src/material/datepicker/year-view.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
<table class="mat-calendar-table" role="presentation">
2-
<thead class="mat-calendar-table-header">
1+
<table class="mat-calendar-table" role="grid">
2+
<thead aria-hidden="true" class="mat-calendar-table-header">
33
<tr><th class="mat-calendar-table-header-divider" colspan="4"></th></tr>
44
</thead>
55
<tbody mat-calendar-body
6+
role="rowgroup"
67
[label]="_yearLabel"
78
[rows]="_months"
89
[todayValue]="_todayMonth!"

src/material/datepicker/year-view.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ describe('MatYearView', () => {
118118
describe('a11y', () => {
119119
it('should set the correct role on the internal table node', () => {
120120
const table = yearViewNativeElement.querySelector('table')!;
121-
expect(table.getAttribute('role')).toBe('presentation');
121+
expect(table.getAttribute('role')).toBe('grid');
122122
});
123123

124124
describe('calendar body', () => {

0 commit comments

Comments
 (0)