From 32da0388cedcd9894d654e8056f4c10894339b5a Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Thu, 12 Jul 2018 16:47:53 +0200 Subject: [PATCH] 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. --- src/lib/datepicker/calendar-body.html | 1 + src/lib/datepicker/calendar-body.spec.ts | 43 ++++++++++++++---------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/lib/datepicker/calendar-body.html b/src/lib/datepicker/calendar-body.html index 01f2684b77e..50b784bb8b5 100644 --- a/src/lib/datepicker/calendar-body.html +++ b/src/lib/datepicker/calendar-body.html @@ -35,6 +35,7 @@ [class.mat-calendar-body-active]="_isActiveCell(rowIndex, colIndex)" [attr.aria-label]="item.ariaLabel" [attr.aria-disabled]="!item.enabled || null" + [attr.aria-selected]="selectedValue === item.value" (click)="_cellClicked(item)" [style.width.%]="100 / numCols" [style.paddingTop.%]="50 * cellAspectRatio / numCols" diff --git a/src/lib/datepicker/calendar-body.spec.ts b/src/lib/datepicker/calendar-body.spec.ts index ea5052d7b66..30ea688a378 100644 --- a/src/lib/datepicker/calendar-body.spec.ts +++ b/src/lib/datepicker/calendar-body.spec.ts @@ -23,21 +23,21 @@ describe('MatCalendarBody', () => { let fixture: ComponentFixture; let testComponent: StandardCalendarBody; let calendarBodyNativeElement: Element; - let rowEls: NodeListOf; - let labelEls: NodeListOf; - let cellEls: NodeListOf; + let rowEls: Element[]; + let labelEls: Element[]; + let cellEls: Element[]; - let refreshElementLists = () => { - rowEls = calendarBodyNativeElement.querySelectorAll('tr'); - labelEls = calendarBodyNativeElement.querySelectorAll('.mat-calendar-body-label'); - cellEls = calendarBodyNativeElement.querySelectorAll('.mat-calendar-body-cell'); - }; + function refreshElementLists() { + rowEls = Array.from(calendarBodyNativeElement.querySelectorAll('tr')); + labelEls = Array.from(calendarBodyNativeElement.querySelectorAll('.mat-calendar-body-label')); + cellEls = Array.from(calendarBodyNativeElement.querySelectorAll('.mat-calendar-body-cell')); + } beforeEach(() => { fixture = TestBed.createComponent(StandardCalendarBody); fixture.detectChanges(); - let calendarBodyDebugElement = fixture.debugElement.query(By.directive(MatCalendarBody)); + const calendarBodyDebugElement = fixture.debugElement.query(By.directive(MatCalendarBody)); calendarBodyNativeElement = calendarBodyDebugElement.nativeElement; testComponent = fixture.componentInstance; @@ -51,17 +51,26 @@ describe('MatCalendarBody', () => { }); it('highlights today', () => { - let todayCell = calendarBodyNativeElement.querySelector('.mat-calendar-body-today')!; + const todayCell = calendarBodyNativeElement.querySelector('.mat-calendar-body-today')!; expect(todayCell).not.toBeNull(); expect(todayCell.innerHTML.trim()).toBe('3'); }); it('highlights selected', () => { - let selectedCell = calendarBodyNativeElement.querySelector('.mat-calendar-body-selected')!; + const selectedCell = calendarBodyNativeElement.querySelector('.mat-calendar-body-selected')!; expect(selectedCell).not.toBeNull(); expect(selectedCell.innerHTML.trim()).toBe('4'); }); + it('should set aria-selected correctly', () => { + const selectedCells = cellEls.filter(c => c.getAttribute('aria-selected') === 'true'); + const deselectedCells = cellEls.filter(c => c.getAttribute('aria-selected') === 'false'); + + expect(selectedCells.length).toBe(1, 'Expected one cell to be marked as selected.'); + expect(deselectedCells.length) + .toBe(cellEls.length - 1, 'Expected remaining cells to be marked as deselected.'); + }); + it('places label in first row if space is available', () => { testComponent.rows[0] = testComponent.rows[0].slice(3); testComponent.rows = testComponent.rows.slice(); @@ -77,7 +86,7 @@ describe('MatCalendarBody', () => { }); it('cell should be selected on click', () => { - let todayElement = + const todayElement = calendarBodyNativeElement.querySelector('.mat-calendar-body-today') as HTMLElement; todayElement.click(); fixture.detectChanges(); @@ -96,20 +105,20 @@ describe('MatCalendarBody', () => { let fixture: ComponentFixture; let testComponent: CalendarBodyWithDisabledCells; let calendarBodyNativeElement: Element; - let cellEls: NodeListOf; + let cellEls: HTMLElement[]; beforeEach(() => { fixture = TestBed.createComponent(CalendarBodyWithDisabledCells); fixture.detectChanges(); - let calendarBodyDebugElement = fixture.debugElement.query(By.directive(MatCalendarBody)); + const calendarBodyDebugElement = fixture.debugElement.query(By.directive(MatCalendarBody)); calendarBodyNativeElement = calendarBodyDebugElement.nativeElement; testComponent = fixture.componentInstance; - cellEls = calendarBodyNativeElement.querySelectorAll('.mat-calendar-body-cell'); + cellEls = Array.from(calendarBodyNativeElement.querySelectorAll('.mat-calendar-body-cell')); }); it('should only allow selection of disabled cells when allowDisabledSelection is true', () => { - (cellEls[0] as HTMLElement).click(); + cellEls[0].click(); fixture.detectChanges(); expect(testComponent.selected).toBeFalsy(); @@ -117,7 +126,7 @@ describe('MatCalendarBody', () => { testComponent.allowDisabledSelection = true; fixture.detectChanges(); - (cellEls[0] as HTMLElement).click(); + cellEls[0].click(); fixture.detectChanges(); expect(testComponent.selected).toBe(1);