Skip to content

Commit 50f999b

Browse files
crisbetojelbourn
authored andcommitted
fix(snack-bar): set appropriate role based on passed in politeness (#13864)
Currently we always have a `role` of `alert` on the snack bar container which has an implicit politeness of `assertive` and which can override the message announced by the live announcer. These changes set the role to `status` if it's set to `polite` or remove the role completely if the politeness is turned off. Fixes #13493.
1 parent 99d2512 commit 50f999b

File tree

2 files changed

+36
-5
lines changed

2 files changed

+36
-5
lines changed

src/lib/snack-bar/snack-bar-container.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import {MatSnackBarConfig} from './snack-bar-config';
4444
encapsulation: ViewEncapsulation.None,
4545
animations: [matSnackBarAnimations.snackBarState],
4646
host: {
47-
'role': 'alert',
47+
'[attr.role]': '_role',
4848
'class': 'mat-snack-bar-container',
4949
'[@state]': '_animationState',
5050
'(@state.done)': 'onAnimationEnd($event)'
@@ -66,6 +66,9 @@ export class MatSnackBarContainer extends BasePortalOutlet implements OnDestroy
6666
/** The state of the snack bar animations. */
6767
_animationState = 'void';
6868

69+
/** ARIA role for the snack bar container. */
70+
_role: 'alert' | 'status' | null;
71+
6972
constructor(
7073
private _ngZone: NgZone,
7174
private _elementRef: ElementRef<HTMLElement>,
@@ -74,6 +77,16 @@ export class MatSnackBarContainer extends BasePortalOutlet implements OnDestroy
7477
public snackBarConfig: MatSnackBarConfig) {
7578

7679
super();
80+
81+
// Based on the ARIA spec, `alert` and `status` roles have an
82+
// implicit `assertive` and `polite` politeness respectively.
83+
if (snackBarConfig.politeness === 'assertive') {
84+
this._role = 'alert';
85+
} else if (snackBarConfig.politeness === 'polite') {
86+
this._role = 'status';
87+
} else {
88+
this._role = null;
89+
}
7790
}
7891

7992
/** Attach a component portal as content to this snack bar container. */

src/lib/snack-bar/snack-bar.spec.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,31 @@ describe('MatSnackBar', () => {
6767
testViewContainerRef = viewContainerFixture.componentInstance.childViewContainer;
6868
});
6969

70-
it('should have the role of alert', () => {
71-
snackBar.open(simpleMessage, simpleActionLabel);
70+
it('should have the role of `alert` with an `assertive` politeness', () => {
71+
snackBar.open(simpleMessage, simpleActionLabel, {politeness: 'assertive'});
72+
viewContainerFixture.detectChanges();
7273

73-
let containerElement = overlayContainerElement.querySelector('snack-bar-container')!;
74+
const containerElement = overlayContainerElement.querySelector('snack-bar-container')!;
7475
expect(containerElement.getAttribute('role'))
7576
.toBe('alert', 'Expected snack bar container to have role="alert"');
76-
});
77+
});
78+
79+
it('should have the role of `status` with a `polite` politeness', () => {
80+
snackBar.open(simpleMessage, simpleActionLabel, {politeness: 'polite'});
81+
viewContainerFixture.detectChanges();
82+
83+
const containerElement = overlayContainerElement.querySelector('snack-bar-container')!;
84+
expect(containerElement.getAttribute('role'))
85+
.toBe('status', 'Expected snack bar container to have role="status"');
86+
});
87+
88+
it('should remove the role if the politeness is turned off', () => {
89+
snackBar.open(simpleMessage, simpleActionLabel, {politeness: 'off'});
90+
viewContainerFixture.detectChanges();
91+
92+
const containerElement = overlayContainerElement.querySelector('snack-bar-container')!;
93+
expect(containerElement.getAttribute('role')).toBeFalsy('Expected role to be removed');
94+
});
7795

7896
it('should open and close a snackbar without a ViewContainerRef', fakeAsync(() => {
7997
let snackBarRef = snackBar.open('Snack time!', 'Chew');

0 commit comments

Comments
 (0)