Skip to content

Commit 32da038

Browse files
crisbetojelbourn
authored andcommitted
fix(datepicker): all cells being read out as selected (#12006)
Currently we omit the `aria-selected` attribute on the individual date cells which causes NVDA to read out all cells as being selected. These changes set the attribute in order to allow for screen readers to pick up the selected state correctly.
1 parent cb6c621 commit 32da038

File tree

2 files changed

+27
-17
lines changed

2 files changed

+27
-17
lines changed

src/lib/datepicker/calendar-body.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
[class.mat-calendar-body-active]="_isActiveCell(rowIndex, colIndex)"
3636
[attr.aria-label]="item.ariaLabel"
3737
[attr.aria-disabled]="!item.enabled || null"
38+
[attr.aria-selected]="selectedValue === item.value"
3839
(click)="_cellClicked(item)"
3940
[style.width.%]="100 / numCols"
4041
[style.paddingTop.%]="50 * cellAspectRatio / numCols"

src/lib/datepicker/calendar-body.spec.ts

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,21 @@ describe('MatCalendarBody', () => {
2323
let fixture: ComponentFixture<StandardCalendarBody>;
2424
let testComponent: StandardCalendarBody;
2525
let calendarBodyNativeElement: Element;
26-
let rowEls: NodeListOf<Element>;
27-
let labelEls: NodeListOf<Element>;
28-
let cellEls: NodeListOf<Element>;
26+
let rowEls: Element[];
27+
let labelEls: Element[];
28+
let cellEls: Element[];
2929

30-
let refreshElementLists = () => {
31-
rowEls = calendarBodyNativeElement.querySelectorAll('tr');
32-
labelEls = calendarBodyNativeElement.querySelectorAll('.mat-calendar-body-label');
33-
cellEls = calendarBodyNativeElement.querySelectorAll('.mat-calendar-body-cell');
34-
};
30+
function refreshElementLists() {
31+
rowEls = Array.from(calendarBodyNativeElement.querySelectorAll('tr'));
32+
labelEls = Array.from(calendarBodyNativeElement.querySelectorAll('.mat-calendar-body-label'));
33+
cellEls = Array.from(calendarBodyNativeElement.querySelectorAll('.mat-calendar-body-cell'));
34+
}
3535

3636
beforeEach(() => {
3737
fixture = TestBed.createComponent(StandardCalendarBody);
3838
fixture.detectChanges();
3939

40-
let calendarBodyDebugElement = fixture.debugElement.query(By.directive(MatCalendarBody));
40+
const calendarBodyDebugElement = fixture.debugElement.query(By.directive(MatCalendarBody));
4141
calendarBodyNativeElement = calendarBodyDebugElement.nativeElement;
4242
testComponent = fixture.componentInstance;
4343

@@ -51,17 +51,26 @@ describe('MatCalendarBody', () => {
5151
});
5252

5353
it('highlights today', () => {
54-
let todayCell = calendarBodyNativeElement.querySelector('.mat-calendar-body-today')!;
54+
const todayCell = calendarBodyNativeElement.querySelector('.mat-calendar-body-today')!;
5555
expect(todayCell).not.toBeNull();
5656
expect(todayCell.innerHTML.trim()).toBe('3');
5757
});
5858

5959
it('highlights selected', () => {
60-
let selectedCell = calendarBodyNativeElement.querySelector('.mat-calendar-body-selected')!;
60+
const selectedCell = calendarBodyNativeElement.querySelector('.mat-calendar-body-selected')!;
6161
expect(selectedCell).not.toBeNull();
6262
expect(selectedCell.innerHTML.trim()).toBe('4');
6363
});
6464

65+
it('should set aria-selected correctly', () => {
66+
const selectedCells = cellEls.filter(c => c.getAttribute('aria-selected') === 'true');
67+
const deselectedCells = cellEls.filter(c => c.getAttribute('aria-selected') === 'false');
68+
69+
expect(selectedCells.length).toBe(1, 'Expected one cell to be marked as selected.');
70+
expect(deselectedCells.length)
71+
.toBe(cellEls.length - 1, 'Expected remaining cells to be marked as deselected.');
72+
});
73+
6574
it('places label in first row if space is available', () => {
6675
testComponent.rows[0] = testComponent.rows[0].slice(3);
6776
testComponent.rows = testComponent.rows.slice();
@@ -77,7 +86,7 @@ describe('MatCalendarBody', () => {
7786
});
7887

7988
it('cell should be selected on click', () => {
80-
let todayElement =
89+
const todayElement =
8190
calendarBodyNativeElement.querySelector('.mat-calendar-body-today') as HTMLElement;
8291
todayElement.click();
8392
fixture.detectChanges();
@@ -96,28 +105,28 @@ describe('MatCalendarBody', () => {
96105
let fixture: ComponentFixture<CalendarBodyWithDisabledCells>;
97106
let testComponent: CalendarBodyWithDisabledCells;
98107
let calendarBodyNativeElement: Element;
99-
let cellEls: NodeListOf<Element>;
108+
let cellEls: HTMLElement[];
100109

101110
beforeEach(() => {
102111
fixture = TestBed.createComponent(CalendarBodyWithDisabledCells);
103112
fixture.detectChanges();
104113

105-
let calendarBodyDebugElement = fixture.debugElement.query(By.directive(MatCalendarBody));
114+
const calendarBodyDebugElement = fixture.debugElement.query(By.directive(MatCalendarBody));
106115
calendarBodyNativeElement = calendarBodyDebugElement.nativeElement;
107116
testComponent = fixture.componentInstance;
108-
cellEls = calendarBodyNativeElement.querySelectorAll('.mat-calendar-body-cell');
117+
cellEls = Array.from(calendarBodyNativeElement.querySelectorAll('.mat-calendar-body-cell'));
109118
});
110119

111120
it('should only allow selection of disabled cells when allowDisabledSelection is true', () => {
112-
(cellEls[0] as HTMLElement).click();
121+
cellEls[0].click();
113122
fixture.detectChanges();
114123

115124
expect(testComponent.selected).toBeFalsy();
116125

117126
testComponent.allowDisabledSelection = true;
118127
fixture.detectChanges();
119128

120-
(cellEls[0] as HTMLElement).click();
129+
cellEls[0].click();
121130
fixture.detectChanges();
122131

123132
expect(testComponent.selected).toBe(1);

0 commit comments

Comments
 (0)