mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2026-09-09 18:02:54 +00:00
[ACS-10309] screen reader personal files new process dialog does not announce any semantics (#5008)
* [ACS-10309] Fixed reading for screen reader for save search dialog * [ACS-10309] Unit tests
This commit is contained in:
+5
-2
@@ -1,5 +1,9 @@
|
|||||||
<div class="aca-save-search-dialog__header">
|
<div class="aca-save-search-dialog__header">
|
||||||
<h2 class="aca-save-search-dialog__title">{{"APP.BROWSE.SEARCH.SAVE_SEARCH.MODAL_HEADER" | translate}}</h2>
|
<h2
|
||||||
|
id="aca-save-search-dialog-title"
|
||||||
|
class="aca-save-search-dialog__title">
|
||||||
|
{{"APP.BROWSE.SEARCH.SAVE_SEARCH.MODAL_HEADER" | translate}}
|
||||||
|
</h2>
|
||||||
<button
|
<button
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
mat-dialog-close
|
mat-dialog-close
|
||||||
@@ -20,7 +24,6 @@
|
|||||||
matInput
|
matInput
|
||||||
required
|
required
|
||||||
[formControlName]="'name'"
|
[formControlName]="'name'"
|
||||||
adf-auto-focus
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<mat-error *ngIf="form.controls['name'].touched">
|
<mat-error *ngIf="form.controls['name'].touched">
|
||||||
|
|||||||
+1
-2
@@ -34,7 +34,7 @@ import { A11yModule } from '@angular/cdk/a11y';
|
|||||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { NotificationService } from '@alfresco/adf-core';
|
import { NotificationService } from '@alfresco/adf-core';
|
||||||
import { AutoFocusDirective, forbidOnlySpaces } from '@alfresco/adf-content-services';
|
import { forbidOnlySpaces } from '@alfresco/adf-content-services';
|
||||||
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { take } from 'rxjs/operators';
|
import { take } from 'rxjs/operators';
|
||||||
import { UniqueSearchNameValidator } from './unique-search-name-validator';
|
import { UniqueSearchNameValidator } from './unique-search-name-validator';
|
||||||
@@ -53,7 +53,6 @@ import { SavedSearchesContextService } from '../../../../services/saved-searches
|
|||||||
A11yModule,
|
A11yModule,
|
||||||
MatCheckboxModule,
|
MatCheckboxModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
AutoFocusDirective,
|
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
MatDialogModule
|
MatDialogModule
|
||||||
],
|
],
|
||||||
|
|||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface SaveSearchDirectiveDialogData {
|
||||||
|
searchUrl: string;
|
||||||
|
}
|
||||||
+37
-11
@@ -24,11 +24,13 @@
|
|||||||
|
|
||||||
import { Component, DebugElement } from '@angular/core';
|
import { Component, DebugElement } from '@angular/core';
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
|
import { MatDialog, MatDialogConfig, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { of, Subject } from 'rxjs';
|
import { of, Subject } from 'rxjs';
|
||||||
import { SaveSearchDirective } from './save-search.directive';
|
import { SaveSearchDirective } from './save-search.directive';
|
||||||
import { SaveSearchDialogComponent } from '../dialog/save-search-dialog.component';
|
import { SaveSearchDialogComponent } from '../dialog/save-search-dialog.component';
|
||||||
|
import { SaveSearchDirectiveDialogData } from '../dialog/save-search-directive-dialog-data';
|
||||||
|
import { OverlayContainer } from '@angular/cdk/overlay';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-test-component',
|
selector: 'app-test-component',
|
||||||
@@ -49,11 +51,6 @@ describe('SaveSearchDirective', () => {
|
|||||||
let element: DebugElement;
|
let element: DebugElement;
|
||||||
let dialog: MatDialog;
|
let dialog: MatDialog;
|
||||||
|
|
||||||
const event = {
|
|
||||||
type: 'click',
|
|
||||||
preventDefault: jasmine.createSpy('preventDefault')
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
void TestBed.configureTestingModule({
|
void TestBed.configureTestingModule({
|
||||||
imports: [SaveSearchDirective, TestComponent],
|
imports: [SaveSearchDirective, TestComponent],
|
||||||
@@ -75,18 +72,46 @@ describe('SaveSearchDirective', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Click on save search directive', () => {
|
||||||
it('should prevent the default click action', () => {
|
it('should prevent the default click action', () => {
|
||||||
element.triggerEventHandler('click', event);
|
const event = new MouseEvent('click');
|
||||||
|
spyOn(event, 'preventDefault');
|
||||||
|
|
||||||
|
element.nativeElement.dispatchEvent(event);
|
||||||
expect(event.preventDefault).toHaveBeenCalled();
|
expect(event.preventDefault).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should open the dialog with the correct configuration', () => {
|
it('should open the dialog with the correct configuration', () => {
|
||||||
spyOn(dialog, 'open');
|
spyOn(dialog, 'open');
|
||||||
element.triggerEventHandler('click', event);
|
element.nativeElement.click();
|
||||||
|
|
||||||
const expectedConfig = {
|
const expectedConfig: MatDialogConfig<SaveSearchDirectiveDialogData> = {
|
||||||
data: { searchUrl: 'encodedQuery' },
|
data: { searchUrl: 'encodedQuery' },
|
||||||
restoreFocus: true
|
restoreFocus: true,
|
||||||
|
ariaLabelledBy: 'aca-save-search-dialog-title'
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(dialog.open).toHaveBeenCalledWith(SaveSearchDialogComponent, expectedConfig);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call setAttribute on container element', () => {
|
||||||
|
const overlayContainer = TestBed.inject(OverlayContainer);
|
||||||
|
const containerElement = document.createElement('div');
|
||||||
|
spyOn(containerElement, 'setAttribute');
|
||||||
|
spyOn(overlayContainer, 'getContainerElement').and.returnValue(containerElement);
|
||||||
|
|
||||||
|
element.nativeElement.click();
|
||||||
|
expect(containerElement.setAttribute).toHaveBeenCalledWith('role', 'dialog');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open the dialog with the correct configuration', () => {
|
||||||
|
spyOn(dialog, 'open');
|
||||||
|
element.nativeElement.click();
|
||||||
|
|
||||||
|
const expectedConfig: MatDialogConfig<SaveSearchDirectiveDialogData> = {
|
||||||
|
data: { searchUrl: 'encodedQuery' },
|
||||||
|
restoreFocus: true,
|
||||||
|
ariaLabelledBy: 'aca-save-search-dialog-title'
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(dialog.open).toHaveBeenCalledWith(SaveSearchDialogComponent, expectedConfig);
|
expect(dialog.open).toHaveBeenCalledWith(SaveSearchDialogComponent, expectedConfig);
|
||||||
@@ -99,7 +124,7 @@ describe('SaveSearchDirective', () => {
|
|||||||
afterClosed: () => afterClosed$.asObservable()
|
afterClosed: () => afterClosed$.asObservable()
|
||||||
} as MatDialogRef<SaveSearchDialogComponent>);
|
} as MatDialogRef<SaveSearchDialogComponent>);
|
||||||
|
|
||||||
element.triggerEventHandler('click', event);
|
element.nativeElement.click();
|
||||||
|
|
||||||
afterClosed$.next(true);
|
afterClosed$.next(true);
|
||||||
afterClosed$.complete();
|
afterClosed$.complete();
|
||||||
@@ -107,4 +132,5 @@ describe('SaveSearchDirective', () => {
|
|||||||
|
|
||||||
expect(fixture.componentInstance.isSavedWithSuccess).toBe(true);
|
expect(fixture.componentInstance.isSavedWithSuccess).toBe(true);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+10
-5
@@ -26,10 +26,8 @@ import { DestroyRef, Directive, ElementRef, EventEmitter, HostListener, inject,
|
|||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { SaveSearchDialogComponent } from '../dialog/save-search-dialog.component';
|
import { SaveSearchDialogComponent } from '../dialog/save-search-dialog.component';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
|
import { OverlayContainer } from '@angular/cdk/overlay';
|
||||||
interface SaveSearchDirectiveDialogData {
|
import { SaveSearchDirectiveDialogData } from '../dialog/save-search-directive-dialog-data';
|
||||||
searchUrl: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[acaSaveSearch]',
|
selector: '[acaSaveSearch]',
|
||||||
@@ -48,6 +46,8 @@ export class SaveSearchDirective {
|
|||||||
private readonly dialogRef = inject(MatDialog);
|
private readonly dialogRef = inject(MatDialog);
|
||||||
private readonly elementRef = inject(ElementRef<HTMLElement>);
|
private readonly elementRef = inject(ElementRef<HTMLElement>);
|
||||||
|
|
||||||
|
constructor(private readonly overlayContainer: OverlayContainer) {}
|
||||||
|
|
||||||
@HostListener('click', ['$event'])
|
@HostListener('click', ['$event'])
|
||||||
onClick(event: MouseEvent) {
|
onClick(event: MouseEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -56,7 +56,12 @@ export class SaveSearchDirective {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private openDialog(): void {
|
private openDialog(): void {
|
||||||
const dialog = this.dialogRef.open(SaveSearchDialogComponent, { ...this.getDialogConfig(), restoreFocus: true });
|
this.overlayContainer.getContainerElement().setAttribute('role', 'dialog');
|
||||||
|
const dialog = this.dialogRef.open(SaveSearchDialogComponent, {
|
||||||
|
...this.getDialogConfig(),
|
||||||
|
restoreFocus: true,
|
||||||
|
ariaLabelledBy: 'aca-save-search-dialog-title'
|
||||||
|
});
|
||||||
dialog
|
dialog
|
||||||
.afterClosed()
|
.afterClosed()
|
||||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||||
|
|||||||
Reference in New Issue
Block a user