fix(live-announcer): duplicate live element when coming in from the server (#12378)

Fixes the case where the user might get multiple live announcer elements, if they're coming in from a server-side-rendered page. Along the same lines as #11940.
This commit is contained in:
Kristiyan Kostadinov
2018-08-01 16:18:15 +02:00
committed by Jeremy Elbourn
parent e6443508a1
commit a10bfa4995
2 changed files with 39 additions and 3 deletions
@@ -81,6 +81,33 @@ describe('LiveAnnouncer', () => {
tick(100);
expect(spy).toHaveBeenCalled();
}));
it('should ensure that there is only one live element at a time', fakeAsync(() => {
announcer.ngOnDestroy();
fixture.destroy();
TestBed.resetTestingModule().configureTestingModule({
imports: [A11yModule],
declarations: [TestApp],
});
const extraElement = document.createElement('div');
extraElement.classList.add('cdk-live-announcer-element');
document.body.appendChild(extraElement);
inject([LiveAnnouncer], (la: LiveAnnouncer) => {
announcer = la;
ariaLiveElement = getLiveElement();
fixture = TestBed.createComponent(TestApp);
})();
announcer.announce('Hey Google');
tick(100);
expect(document.body.querySelectorAll('.cdk-live-announcer-element').length)
.toBe(1, 'Expected only one live announcer element in the DOM.');
}));
});
describe('with a custom element', () => {
+12 -3
View File
@@ -30,14 +30,16 @@ export type AriaLivePoliteness = 'off' | 'polite' | 'assertive';
@Injectable({providedIn: 'root'})
export class LiveAnnouncer implements OnDestroy {
private readonly _liveElement: HTMLElement;
private _document: Document;
constructor(
@Optional() @Inject(LIVE_ANNOUNCER_ELEMENT_TOKEN) elementToken: any,
@Inject(DOCUMENT) private _document: any) {
@Inject(DOCUMENT) _document: any) {
// We inject the live element and document as `any` because the constructor signature cannot
// reference browser globals (HTMLElement, Document) on non-browser environments, since having
// a class decorator causes TypeScript to preserve the constructor signature types.
this._document = _document;
this._liveElement = elementToken || this._createLiveElement();
}
@@ -73,9 +75,16 @@ export class LiveAnnouncer implements OnDestroy {
}
private _createLiveElement(): HTMLElement {
let liveEl = this._document.createElement('div');
const elementClass = 'cdk-live-announcer-element';
const previousElements = this._document.getElementsByClassName(elementClass);
liveEl.classList.add('cdk-live-announcer-element');
// Remove any old containers. This can happen when coming in from a server-side-rendered page.
for (let i = 0; i < previousElements.length; i++) {
previousElements[i].parentNode!.removeChild(previousElements[i]);
}
const liveEl = this._document.createElement('div');
liveEl.classList.add(elementClass);
liveEl.classList.add('cdk-visually-hidden');
liveEl.setAttribute('aria-atomic', 'true');