Skip to content

Commit

Permalink
feat(cdk/a11y): add input to control the duration of the aria live di…
Browse files Browse the repository at this point in the history
…rective (#15275)

Adds an input that allows the consumer to control how long it takes before the messages
that are announced by `CdkAriaLive` to be cleared from the DOM.
  • Loading branch information
crisbeto committed Feb 26, 2022
1 parent 09a906a commit 7d0bca7
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 10 deletions.
32 changes: 24 additions & 8 deletions src/cdk/a11y/live-announcer/live-announcer.spec.ts
@@ -1,8 +1,8 @@
import {MutationObserverFactory} from '@angular/cdk/observers';
import {Component, Input} from '@angular/core';
import {Component} from '@angular/core';
import {ComponentFixture, fakeAsync, flush, inject, TestBed, tick} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {A11yModule, AriaLivePoliteness} from '../index';
import {A11yModule} from '../index';
import {LiveAnnouncer} from './live-announcer';
import {
LIVE_ANNOUNCER_ELEMENT_TOKEN,
Expand Down Expand Up @@ -280,7 +280,7 @@ describe('CdkAriaLive', () => {
invokeMutationCallbacks();
flush();

expect(announcer.announce).toHaveBeenCalledWith('New content', 'polite');
expect(announcer.announce).toHaveBeenCalledWith('New content', 'polite', undefined);
}));

it('should dynamically update the politeness', fakeAsync(() => {
Expand All @@ -289,7 +289,7 @@ describe('CdkAriaLive', () => {
invokeMutationCallbacks();
flush();

expect(announcer.announce).toHaveBeenCalledWith('New content', 'polite');
expect(announcer.announce).toHaveBeenCalledWith('New content', 'polite', undefined);

announcerSpy.calls.reset();
fixture.componentInstance.politeness = 'off';
Expand All @@ -307,7 +307,7 @@ describe('CdkAriaLive', () => {
invokeMutationCallbacks();
flush();

expect(announcer.announce).toHaveBeenCalledWith('Newest content', 'assertive');
expect(announcer.announce).toHaveBeenCalledWith('Newest content', 'assertive', undefined);
}));

it('should not announce the same text multiple times', fakeAsync(() => {
Expand All @@ -324,6 +324,16 @@ describe('CdkAriaLive', () => {

expect(announcer.announce).toHaveBeenCalledTimes(1);
}));

it('should be able to pass in a duration', fakeAsync(() => {
fixture.componentInstance.content = 'New content';
fixture.componentInstance.duration = 1337;
fixture.detectChanges();
invokeMutationCallbacks();
flush();

expect(announcer.announce).toHaveBeenCalledWith('New content', 'polite', 1337);
}));
});

function getLiveElement(): Element {
Expand All @@ -339,8 +349,14 @@ class TestApp {
}
}

@Component({template: `<div [cdkAriaLive]="politeness ? politeness : null">{{content}}</div>`})
@Component({
template: `
<div
[cdkAriaLive]="politeness ? politeness : null"
[cdkAriaLiveDuration]="duration">{{content}}</div>`,
})
class DivWithCdkAriaLive {
@Input() politeness: AriaLivePoliteness;
@Input() content = 'Initial content';
politeness = 'polite';
content = 'Initial content';
duration: number;
}
5 changes: 4 additions & 1 deletion src/cdk/a11y/live-announcer/live-announcer.ts
Expand Up @@ -198,7 +198,7 @@ export class CdkAriaLive implements OnDestroy {
// The `MutationObserver` fires also for attribute
// changes which we don't want to announce.
if (elementText !== this._previousAnnouncedText) {
this._liveAnnouncer.announce(elementText, this._politeness);
this._liveAnnouncer.announce(elementText, this._politeness, this.duration);
this._previousAnnouncedText = elementText;
}
});
Expand All @@ -207,6 +207,9 @@ export class CdkAriaLive implements OnDestroy {
}
private _politeness: AriaLivePoliteness = 'polite';

/** Time in milliseconds after which to clear out the announcer element. */
@Input('cdkAriaLiveDuration') duration: number;

private _previousAnnouncedText?: string;
private _subscription: Subscription | null;

Expand Down
3 changes: 2 additions & 1 deletion tools/public_api_guard/cdk/a11y.md
Expand Up @@ -67,12 +67,13 @@ export const CDK_DESCRIBEDBY_ID_PREFIX = "cdk-describedby-message";
// @public
export class CdkAriaLive implements OnDestroy {
constructor(_elementRef: ElementRef, _liveAnnouncer: LiveAnnouncer, _contentObserver: ContentObserver, _ngZone: NgZone);
duration: number;
// (undocumented)
ngOnDestroy(): void;
get politeness(): AriaLivePoliteness;
set politeness(value: AriaLivePoliteness);
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<CdkAriaLive, "[cdkAriaLive]", ["cdkAriaLive"], { "politeness": "cdkAriaLive"; }, {}, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<CdkAriaLive, "[cdkAriaLive]", ["cdkAriaLive"], { "politeness": "cdkAriaLive"; "duration": "cdkAriaLiveDuration"; }, {}, never>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<CdkAriaLive, never>;
}
Expand Down

0 comments on commit 7d0bca7

Please sign in to comment.