Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions goldens/cdk/a11y/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { OnChanges } from '@angular/core';
import { OnDestroy } from '@angular/core';
import { Provider } from '@angular/core';
import { QueryList } from '@angular/core';
import { SafeHtml } from '@angular/platform-browser';
import { Signal } from '@angular/core';
import { SimpleChanges } from '@angular/core';
import { Subject } from 'rxjs';
Expand Down Expand Up @@ -405,10 +404,10 @@ export const LIVE_ANNOUNCER_ELEMENT_TOKEN: InjectionToken<HTMLElement | null>;
// @public (undocumented)
export class LiveAnnouncer implements OnDestroy {
constructor(...args: unknown[]);
announce(message: LiveAnnouncerMessage): Promise<void>;
announce(message: LiveAnnouncerMessage, politeness?: AriaLivePoliteness): Promise<void>;
announce(message: LiveAnnouncerMessage, duration?: number): Promise<void>;
announce(message: LiveAnnouncerMessage, politeness?: AriaLivePoliteness, duration?: number): Promise<void>;
announce(message: string): Promise<void>;
announce(message: string, politeness?: AriaLivePoliteness): Promise<void>;
announce(message: string, duration?: number): Promise<void>;
announce(message: string, politeness?: AriaLivePoliteness, duration?: number): Promise<void>;
clear(): void;
// (undocumented)
ngOnDestroy(): void;
Expand All @@ -424,9 +423,6 @@ export interface LiveAnnouncerDefaultOptions {
politeness?: AriaLivePoliteness;
}

// @public
export type LiveAnnouncerMessage = string | SafeHtml;

// @public @deprecated
export const MESSAGES_CONTAINER_ID = "cdk-describedby-message-container";

Expand Down
1 change: 0 additions & 1 deletion src/cdk/a11y/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ ng_project(
deps = [
"//:node_modules/@angular/common",
"//:node_modules/@angular/core",
"//:node_modules/@angular/platform-browser",
"//:node_modules/rxjs",
"//src:dev_mode_types",
"//src/cdk/coercion",
Expand Down
21 changes: 4 additions & 17 deletions src/cdk/a11y/live-announcer/live-announcer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import {MutationObserverFactory} from '../../observers';
import {ComponentPortal} from '../../portal';
import {Component, inject, Injector} from '@angular/core';
import {ComponentFixture, TestBed, fakeAsync, flush, tick} from '@angular/core/testing';
import {By, DomSanitizer} from '@angular/platform-browser';
import {By} from '@angular/platform-browser';
import {A11yModule} from '../index';
import {LiveAnnouncer, LiveAnnouncerMessage} from './live-announcer';
import {LiveAnnouncer} from './live-announcer';
import {
AriaLivePoliteness,
LIVE_ANNOUNCER_DEFAULT_OPTIONS,
Expand Down Expand Up @@ -202,19 +202,6 @@ describe('LiveAnnouncer', () => {
tick(100);
expect(modal.getAttribute('aria-owns')).toBe(`foo bar ${ariaLiveElement.id}`);
}));

it('should be able to announce safe HTML', fakeAsync(() => {
const sanitizer = TestBed.inject(DomSanitizer);
const message = sanitizer.bypassSecurityTrustHtml(
'<span class="message" lang="fr">Bonjour</span>',
);
fixture.componentInstance.announce(message);

// This flushes our 100ms timeout for the screenreaders.
tick(100);

expect(ariaLiveElement.querySelector('.message')?.textContent).toBe('Bonjour');
}));
});

describe('with a custom element', () => {
Expand Down Expand Up @@ -391,13 +378,13 @@ function getLiveElement(): Element {
}

@Component({
template: `<button (click)="announce('Test')">Announce</button>`,
template: `<button (click)="announceText('Test')">Announce</button>`,
imports: [A11yModule],
})
class TestApp {
live = inject(LiveAnnouncer);

announce(message: LiveAnnouncerMessage) {
announceText(message: string) {
this.live.announce(message);
}
}
Expand Down
39 changes: 7 additions & 32 deletions src/cdk/a11y/live-announcer/live-announcer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,18 @@ import {
OnDestroy,
inject,
DOCUMENT,
SecurityContext,
} from '@angular/core';
import {DomSanitizer, SafeHtml} from '@angular/platform-browser';
import {Subscription} from 'rxjs';
import {
AriaLivePoliteness,
LiveAnnouncerDefaultOptions,
LIVE_ANNOUNCER_ELEMENT_TOKEN,
LIVE_ANNOUNCER_DEFAULT_OPTIONS,
} from './live-announcer-tokens';
import {_CdkPrivateStyleLoader, _VisuallyHiddenLoader, trustedHTMLFromString} from '../../private';
import {_CdkPrivateStyleLoader, _VisuallyHiddenLoader} from '../../private';

let uniqueIds = 0;

/** Possible types for a message that can be announced by the `LiveAnnouncer`. */
export type LiveAnnouncerMessage = string | SafeHtml;

@Injectable({providedIn: 'root'})
export class LiveAnnouncer implements OnDestroy {
private _ngZone = inject(NgZone);
Expand All @@ -43,7 +38,6 @@ export class LiveAnnouncer implements OnDestroy {

private _liveElement: HTMLElement;
private _document = inject(DOCUMENT);
private _sanitizer = inject(DomSanitizer);
private _previousTimeout: ReturnType<typeof setTimeout>;
private _currentPromise: Promise<void> | undefined;
private _currentResolve: (() => void) | undefined;
Expand All @@ -60,15 +54,15 @@ export class LiveAnnouncer implements OnDestroy {
* @param message Message to be announced to the screen reader.
* @returns Promise that will be resolved when the message is added to the DOM.
*/
announce(message: LiveAnnouncerMessage): Promise<void>;
announce(message: string): Promise<void>;

/**
* Announces a message to screen readers.
* @param message Message to be announced to the screen reader.
* @param politeness The politeness of the announcer element.
* @returns Promise that will be resolved when the message is added to the DOM.
*/
announce(message: LiveAnnouncerMessage, politeness?: AriaLivePoliteness): Promise<void>;
announce(message: string, politeness?: AriaLivePoliteness): Promise<void>;

/**
* Announces a message to screen readers.
Expand All @@ -78,7 +72,7 @@ export class LiveAnnouncer implements OnDestroy {
* 100ms after `announce` has been called.
* @returns Promise that will be resolved when the message is added to the DOM.
*/
announce(message: LiveAnnouncerMessage, duration?: number): Promise<void>;
announce(message: string, duration?: number): Promise<void>;

/**
* Announces a message to screen readers.
Expand All @@ -89,13 +83,9 @@ export class LiveAnnouncer implements OnDestroy {
* 100ms after `announce` has been called.
* @returns Promise that will be resolved when the message is added to the DOM.
*/
announce(
message: LiveAnnouncerMessage,
politeness?: AriaLivePoliteness,
duration?: number,
): Promise<void>;
announce(message: string, politeness?: AriaLivePoliteness, duration?: number): Promise<void>;

announce(message: LiveAnnouncerMessage, ...args: any[]): Promise<void> {
announce(message: string, ...args: any[]): Promise<void> {
const defaultOptions = this._defaultOptions;
let politeness: AriaLivePoliteness | undefined;
let duration: number | undefined;
Expand Down Expand Up @@ -137,22 +127,7 @@ export class LiveAnnouncer implements OnDestroy {

clearTimeout(this._previousTimeout);
this._previousTimeout = setTimeout(() => {
if (!message || typeof message === 'string') {
this._liveElement.textContent = message;
} else {
const cleanMessage = this._sanitizer.sanitize(SecurityContext.HTML, message);

if (cleanMessage === null && (typeof ngDevMode === 'undefined' || ngDevMode)) {
throw new Error(
`The message provided to LiveAnnouncer was not trusted as safe HTML by ` +
`Angular's DomSanitizer. Attempted message was "${message}".`,
);
}

this._liveElement.innerHTML = trustedHTMLFromString(
cleanMessage || '',
) as unknown as string;
}
this._liveElement.textContent = message;

if (typeof duration === 'number') {
this._previousTimeout = setTimeout(() => this.clear(), duration);
Expand Down
Loading