Skip to content

Commit a106eba

Browse files
authored
fix(material/list): communicate current active page with aria-current (#25681)
For listitems that are anchor tags, communicate the activated state to screen readers using aria-current. Add aria-current="page" to listitems that are both activated and anchor tags. Fix a11y issue where the activated state is not communicated to screen readers.
1 parent 17e217a commit a106eba

File tree

3 files changed

+44
-0
lines changed

3 files changed

+44
-0
lines changed

src/material/list/list.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ describe('MDC-based MatList', () => {
1919
ListWithMultipleItems,
2020
ListWithManyLines,
2121
NavListWithOneAnchorItem,
22+
NavListWithActivatedItem,
2223
ActionListWithoutType,
2324
ActionListWithType,
2425
ListWithDisabledItems,
@@ -133,6 +134,21 @@ describe('MDC-based MatList', () => {
133134
.toBe('group');
134135
});
135136

137+
it('should apply aria-current="page" to activated list items', () => {
138+
const fixture = TestBed.createComponent(NavListWithActivatedItem);
139+
fixture.detectChanges();
140+
141+
const items = fixture.componentInstance.listItems;
142+
expect(items.length)
143+
.withContext('expected list to have at least two items')
144+
.toBeGreaterThanOrEqual(2);
145+
const inactiveItem = items.get(0)!._elementRef.nativeElement;
146+
const activeItem = items.get(1)!._elementRef.nativeElement;
147+
148+
expect(inactiveItem.hasAttribute('aria-current')).toBe(false);
149+
expect(activeItem.getAttribute('aria-current')).toBe('page');
150+
});
151+
136152
it('should not show ripples for non-nav lists', fakeAsync(() => {
137153
const fixture = TestBed.createComponent(ListWithOneAnchorItem);
138154
fixture.detectChanges();
@@ -392,6 +408,24 @@ class NavListWithOneAnchorItem extends BaseTestList {
392408
disableListRipple: boolean = false;
393409
}
394410

411+
@Component({
412+
template: `
413+
<mat-nav-list [disableRipple]="disableListRipple">
414+
<a *ngFor="let item of items; let index = index" mat-list-item [disableRipple]="disableItemRipple"
415+
[activated]="index === activatedIndex">
416+
{{item.name}}
417+
</a>
418+
</mat-nav-list>`,
419+
})
420+
class NavListWithActivatedItem extends BaseTestList {
421+
@ViewChildren(MatListItem) listItems: QueryList<MatListItem>;
422+
disableItemRipple: boolean = false;
423+
disableListRipple: boolean = false;
424+
425+
/** Index of the activated list item in `this.items`. Set to -1 if no item is selected. */
426+
activatedIndex = 1;
427+
}
428+
395429
@Component({
396430
template: `
397431
<mat-action-list [disableRipple]="disableListRipple">

src/material/list/list.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export class MatList extends MatListBase {}
5858
'[class.mdc-list-item--with-leading-icon]': '_icons.length !== 0',
5959
'[class.mdc-list-item--with-trailing-meta]': '_meta.length !== 0',
6060
'[class._mat-animation-noopable]': '_noopAnimations',
61+
'[attr.aria-current]': '_getAriaCurrent()',
6162
},
6263
templateUrl: 'list-item.html',
6364
encapsulation: ViewEncapsulation.None,
@@ -90,4 +91,12 @@ export class MatListItem extends MatListItemBase {
9091
) {
9192
super(element, ngZone, listBase, platform, globalRippleOptions, animationMode);
9293
}
94+
95+
/**
96+
* Determine the value of `aria-current`. Return 'page' if this item is an activated anchor tag.
97+
* Otherwise, return `null`. This method is safe to use with server-side rendering.
98+
*/
99+
_getAriaCurrent(): string | null {
100+
return this._hostElement.nodeName === 'A' && this._activated ? 'page' : null;
101+
}
93102
}

tools/public_api_guard/material/list.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export class MatListItem extends MatListItemBase {
6363
set activated(activated: boolean);
6464
// (undocumented)
6565
_activated: boolean;
66+
_getAriaCurrent(): string | null;
6667
// (undocumented)
6768
_itemText: ElementRef<HTMLElement>;
6869
// (undocumented)

0 commit comments

Comments
 (0)