diff --git a/src/cdk/a11y/live-announcer/live-announcer.spec.ts b/src/cdk/a11y/live-announcer/live-announcer.spec.ts index 84b7c1ceab58..516318cef786 100644 --- a/src/cdk/a11y/live-announcer/live-announcer.spec.ts +++ b/src/cdk/a11y/live-announcer/live-announcer.spec.ts @@ -112,6 +112,16 @@ describe('LiveAnnouncer', () => { expect(spy).toHaveBeenCalled(); })); + it('should resolve the returned promise if another announcement is made before the timeout has expired', fakeAsync(() => { + const spy = jasmine.createSpy('announce spy'); + announcer.announce('something').then(spy); + tick(10); + announcer.announce('something').then(spy); + tick(100); + + expect(spy).toHaveBeenCalledTimes(2); + })); + it('should ensure that there is only one live element at a time', fakeAsync(() => { fixture.destroy(); diff --git a/src/cdk/a11y/live-announcer/live-announcer.ts b/src/cdk/a11y/live-announcer/live-announcer.ts index cc78425ade11..3f6f5048bd80 100644 --- a/src/cdk/a11y/live-announcer/live-announcer.ts +++ b/src/cdk/a11y/live-announcer/live-announcer.ts @@ -31,6 +31,8 @@ export class LiveAnnouncer implements OnDestroy { private _liveElement: HTMLElement; private _document: Document; private _previousTimeout: number; + private _currentPromise: Promise | undefined; + private _currentResolve: (() => void) | undefined; constructor( @Optional() @Inject(LIVE_ANNOUNCER_ELEMENT_TOKEN) elementToken: any, @@ -115,17 +117,23 @@ export class LiveAnnouncer implements OnDestroy { // second time without clearing and then using a non-zero delay. // (using JAWS 17 at time of this writing). return this._ngZone.runOutsideAngular(() => { - return new Promise(resolve => { - clearTimeout(this._previousTimeout); - this._previousTimeout = setTimeout(() => { - this._liveElement.textContent = message; - resolve(); - - if (typeof duration === 'number') { - this._previousTimeout = setTimeout(() => this.clear(), duration); - } - }, 100); - }); + if (!this._currentPromise) { + this._currentPromise = new Promise(resolve => (this._currentResolve = resolve)); + } + + clearTimeout(this._previousTimeout); + this._previousTimeout = setTimeout(() => { + this._liveElement.textContent = message; + + if (typeof duration === 'number') { + this._previousTimeout = setTimeout(() => this.clear(), duration); + } + + this._currentResolve!(); + this._currentPromise = this._currentResolve = undefined; + }, 100); + + return this._currentPromise; }); } @@ -144,6 +152,8 @@ export class LiveAnnouncer implements OnDestroy { clearTimeout(this._previousTimeout); this._liveElement?.remove(); this._liveElement = null!; + this._currentResolve?.(); + this._currentPromise = this._currentResolve = undefined; } private _createLiveElement(): HTMLElement {