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.
This commit is contained in:
Kristiyan Kostadinov
2018-11-03 01:21:24 +01:00
committed by Jeremy Elbourn
parent 99d2512888
commit 50f999b5e5
2 changed files with 36 additions and 5 deletions
+14 -1
View File
@@ -44,7 +44,7 @@ import {MatSnackBarConfig} from './snack-bar-config';
encapsulation: ViewEncapsulation.None,
animations: [matSnackBarAnimations.snackBarState],
host: {
'role': 'alert',
'[attr.role]': '_role',
'class': 'mat-snack-bar-container',
'[@state]': '_animationState',
'(@state.done)': 'onAnimationEnd($event)'
@@ -66,6 +66,9 @@ export class MatSnackBarContainer extends BasePortalOutlet implements OnDestroy
/** The state of the snack bar animations. */
_animationState = 'void';
/** ARIA role for the snack bar container. */
_role: 'alert' | 'status' | null;
constructor(
private _ngZone: NgZone,
private _elementRef: ElementRef<HTMLElement>,
@@ -74,6 +77,16 @@ export class MatSnackBarContainer extends BasePortalOutlet implements OnDestroy
public snackBarConfig: MatSnackBarConfig) {
super();
// Based on the ARIA spec, `alert` and `status` roles have an
// implicit `assertive` and `polite` politeness respectively.
if (snackBarConfig.politeness === 'assertive') {
this._role = 'alert';
} else if (snackBarConfig.politeness === 'polite') {
this._role = 'status';
} else {
this._role = null;
}
}
/** Attach a component portal as content to this snack bar container. */
+22 -4
View File
@@ -67,13 +67,31 @@ describe('MatSnackBar', () => {
testViewContainerRef = viewContainerFixture.componentInstance.childViewContainer;
});
it('should have the role of alert', () => {
snackBar.open(simpleMessage, simpleActionLabel);
it('should have the role of `alert` with an `assertive` politeness', () => {
snackBar.open(simpleMessage, simpleActionLabel, {politeness: 'assertive'});
viewContainerFixture.detectChanges();
let containerElement = overlayContainerElement.querySelector('snack-bar-container')!;
const containerElement = overlayContainerElement.querySelector('snack-bar-container')!;
expect(containerElement.getAttribute('role'))
.toBe('alert', 'Expected snack bar container to have role="alert"');
});
});
it('should have the role of `status` with a `polite` politeness', () => {
snackBar.open(simpleMessage, simpleActionLabel, {politeness: 'polite'});
viewContainerFixture.detectChanges();
const containerElement = overlayContainerElement.querySelector('snack-bar-container')!;
expect(containerElement.getAttribute('role'))
.toBe('status', 'Expected snack bar container to have role="status"');
});
it('should remove the role if the politeness is turned off', () => {
snackBar.open(simpleMessage, simpleActionLabel, {politeness: 'off'});
viewContainerFixture.detectChanges();
const containerElement = overlayContainerElement.querySelector('snack-bar-container')!;
expect(containerElement.getAttribute('role')).toBeFalsy('Expected role to be removed');
});
it('should open and close a snackbar without a ViewContainerRef', fakeAsync(() => {
let snackBarRef = snackBar.open('Snack time!', 'Chew');