mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
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:
committed by
Jeremy Elbourn
parent
e6443508a1
commit
a10bfa4995
@@ -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', () => {
|
||||
|
||||
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user