Skip to content

Commit

Permalink
fix(snack-bar): set appropriate role based on passed in politeness (#…
Browse files Browse the repository at this point in the history
…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.
  • Loading branch information
crisbeto authored and jelbourn committed Nov 6, 2018
1 parent d2af80b commit 33d3cb3
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 5 deletions.
15 changes: 14 additions & 1 deletion src/lib/snack-bar/snack-bar-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)'
Expand All @@ -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>,
Expand All @@ -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. */
Expand Down
26 changes: 22 additions & 4 deletions src/lib/snack-bar/snack-bar.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down

0 comments on commit 33d3cb3

Please sign in to comment.