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

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 Dec 19, 2020
1 parent 71b7b15 commit cd7e18e
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 9 deletions.
30 changes: 23 additions & 7 deletions src/cdk/a11y/live-announcer/live-announcer.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
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';
Expand Down Expand Up @@ -288,7 +288,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 @@ -297,7 +297,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 @@ -315,7 +315,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 @@ -333,6 +333,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);
}));

});


Expand All @@ -349,8 +359,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
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,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 @@ -212,6 +212,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.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@ export declare const CDK_DESCRIBEDBY_HOST_ATTRIBUTE = "cdk-describedby-host";
export declare const CDK_DESCRIBEDBY_ID_PREFIX = "cdk-describedby-message";

export declare class CdkAriaLive implements OnDestroy {
duration: number;
get politeness(): AriaLivePoliteness;
set politeness(value: AriaLivePoliteness);
constructor(_elementRef: ElementRef, _liveAnnouncer: LiveAnnouncer, _contentObserver: ContentObserver, _ngZone: NgZone);
ngOnDestroy(): void;
static ɵdir: i0.ɵɵDirectiveDefWithMeta<CdkAriaLive, "[cdkAriaLive]", ["cdkAriaLive"], { "politeness": "cdkAriaLive"; }, {}, never>;
static ɵdir: i0.ɵɵDirectiveDefWithMeta<CdkAriaLive, "[cdkAriaLive]", ["cdkAriaLive"], { "politeness": "cdkAriaLive"; "duration": "cdkAriaLiveDuration"; }, {}, never>;
static ɵfac: i0.ɵɵFactoryDef<CdkAriaLive, never>;
}

Expand Down

0 comments on commit cd7e18e

Please sign in to comment.