Merge branch 'live-region-8.0' into live-region-main

This commit is contained in:
Andreas Awouters
2024-09-27 10:15:51 +02:00
3 changed files with 67 additions and 17 deletions

View File

@@ -11,6 +11,12 @@ import { Observable } from 'rxjs';
import { LiveRegionService } from './live-region.service'; import { LiveRegionService } from './live-region.service';
/**
* The Live Region Component is an accessibility tool for screenreaders. When a change occurs on a page when the changed
* section is not in focus, a message should be displayed by this component so it can be announced by a screen reader.
*
* This component should not be used directly. Use the {@link LiveRegionService} to add messages.
*/
@Component({ @Component({
selector: `ds-live-region`, selector: `ds-live-region`,
templateUrl: './live-region.component.html', templateUrl: './live-region.component.html',

View File

@@ -4,14 +4,16 @@ import {
tick, tick,
} from '@angular/core/testing'; } from '@angular/core/testing';
import { UUIDService } from '../../core/shared/uuid.service';
import { LiveRegionService } from './live-region.service'; import { LiveRegionService } from './live-region.service';
describe('liveRegionService', () => { describe('liveRegionService', () => {
let service: LiveRegionService; let service: LiveRegionService;
beforeEach(() => { beforeEach(() => {
service = new LiveRegionService(); service = new LiveRegionService(
new UUIDService(),
);
}); });
describe('addMessage', () => { describe('addMessage', () => {
@@ -90,6 +92,34 @@ describe('liveRegionService', () => {
expect(results[3]).toEqual([]); expect(results[3]).toEqual([]);
})); }));
it('should not pop messages added after clearing within timeOut period', fakeAsync(() => {
const results: string[][] = [];
service.getMessages$().subscribe((messages) => {
results.push(messages);
});
expect(results.length).toEqual(1);
expect(results[0]).toEqual([]);
service.addMessage('Message One');
tick(10000);
service.clear();
tick(15000);
service.addMessage('Message Two');
// Message Two should not be cleared after 5 more seconds
tick(5000);
expect(results.length).toEqual(4);
expect(results[3]).toEqual(['Message Two']);
// But should be cleared 30 seconds after it was added
tick(25000);
expect(results.length).toEqual(5);
expect(results[4]).toEqual([]);
}));
it('should respect configured timeOut', fakeAsync(() => { it('should respect configured timeOut', fakeAsync(() => {
const results: string[][] = []; const results: string[][] = [];

View File

@@ -2,12 +2,22 @@ import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { environment } from '../../../environments/environment'; import { environment } from '../../../environments/environment';
import { UUIDService } from '../../core/shared/uuid.service';
/**
* The LiveRegionService is responsible for handling the messages that are shown by the {@link LiveRegionComponent}.
* Use this service to add or remove messages to the Live Region.
*/
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class LiveRegionService { export class LiveRegionService {
constructor(
protected uuidService: UUIDService,
) {
}
/** /**
* The duration after which the messages disappear in milliseconds * The duration after which the messages disappear in milliseconds
* @protected * @protected
@@ -15,10 +25,11 @@ export class LiveRegionService {
protected messageTimeOutDurationMs: number = environment.liveRegion.messageTimeOutDurationMs; protected messageTimeOutDurationMs: number = environment.liveRegion.messageTimeOutDurationMs;
/** /**
* Array containing the messages that should be shown in the live region * Array containing the messages that should be shown in the live region,
* together with a uuid, so they can be uniquely identified
* @protected * @protected
*/ */
protected messages: string[] = []; protected messages: { message: string, uuid: string }[] = [];
/** /**
* BehaviorSubject emitting the array with messages every time the array updates * BehaviorSubject emitting the array with messages every time the array updates
@@ -35,27 +46,28 @@ export class LiveRegionService {
/** /**
* Returns a copy of the array with the current live region messages * Returns a copy of the array with the current live region messages
*/ */
getMessages() { getMessages(): string[] {
return [...this.messages]; return this.messages.map(messageObj => messageObj.message);
} }
/** /**
* Returns the BehaviorSubject emitting the array with messages every time the array updates * Returns the BehaviorSubject emitting the array with messages every time the array updates
*/ */
getMessages$() { getMessages$(): BehaviorSubject<string[]> {
return this.messages$; return this.messages$;
} }
/** /**
* Adds a message to the live-region messages array * Adds a message to the live-region messages array
* @param message * @param message
* @return The uuid of the message
*/ */
addMessage(message: string) { addMessage(message: string): string {
this.messages.push(message); const uuid = this.uuidService.generate();
this.messages.push({ message, uuid });
setTimeout(() => this.clearMessageByUUID(uuid), this.messageTimeOutDurationMs);
this.emitCurrentMessages(); this.emitCurrentMessages();
return uuid;
// Clear the message once the timeOut has passed
setTimeout(() => this.pop(), this.messageTimeOutDurationMs);
} }
/** /**
@@ -67,12 +79,14 @@ export class LiveRegionService {
} }
/** /**
* Removes the longest living message from the array. * Removes the message with the given UUID from the messages array
* @protected * @param uuid The uuid of the message to clear
*/ */
protected pop() { clearMessageByUUID(uuid: string) {
if (this.messages.length > 0) { const index = this.messages.findIndex(messageObj => messageObj.uuid === uuid);
this.messages.shift();
if (index !== -1) {
this.messages.splice(index, 1);
this.emitCurrentMessages(); this.emitCurrentMessages();
} }
} }