mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-09-10 14:11:42 +00:00
[ACS-9267] a11y testing: Datatable - columns settings menu (#10668)
* [ACS-9267] a11y testing: Datatable - columns settings menu * delete comment * [ACS-9267] add empty line * [ACS-9267] adress sonarcloud issues * [ACS-9267] fix unit tests
This commit is contained in:
committed by
GitHub
parent
ba322b70b2
commit
1e83be9194
@@ -1137,7 +1137,7 @@ describe('DocumentList', () => {
|
|||||||
it('should display [empty folder] template ', () => {
|
it('should display [empty folder] template ', () => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
runInInjectionContext(injector, () => {
|
runInInjectionContext(injector, () => {
|
||||||
documentList.dataTable = new DataTableComponent(null, null, matIconRegistryMock, domSanitizerMock);
|
documentList.dataTable = new DataTableComponent(null, null, matIconRegistryMock, domSanitizerMock, null);
|
||||||
});
|
});
|
||||||
expect(documentList.dataTable).toBeDefined();
|
expect(documentList.dataTable).toBeDefined();
|
||||||
expect(fixture.debugElement.query(By.css('adf-empty-list'))).not.toBeNull();
|
expect(fixture.debugElement.query(By.css('adf-empty-list'))).not.toBeNull();
|
||||||
@@ -1158,7 +1158,7 @@ describe('DocumentList', () => {
|
|||||||
|
|
||||||
it('should empty folder NOT show the pagination', () => {
|
it('should empty folder NOT show the pagination', () => {
|
||||||
runInInjectionContext(injector, () => {
|
runInInjectionContext(injector, () => {
|
||||||
documentList.dataTable = new DataTableComponent(null, null, matIconRegistryMock, domSanitizerMock);
|
documentList.dataTable = new DataTableComponent(null, null, matIconRegistryMock, domSanitizerMock, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(documentList.isEmpty()).toBeTruthy();
|
expect(documentList.isEmpty()).toBeTruthy();
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
<div
|
<div
|
||||||
class="adf-columns-selector"
|
class="adf-columns-selector"
|
||||||
|
role="presentation"
|
||||||
data-automation-id="adf-columns-selector"
|
data-automation-id="adf-columns-selector"
|
||||||
tabindex="0"
|
|
||||||
role="button"
|
|
||||||
(keyup.enter)="$event.stopPropagation()"
|
(keyup.enter)="$event.stopPropagation()"
|
||||||
(click)="$event.stopPropagation();"
|
(click)="$event.stopPropagation();"
|
||||||
>
|
>
|
||||||
@@ -14,6 +13,8 @@
|
|||||||
<button
|
<button
|
||||||
data-automation-id="adf-columns-selector-close-button"
|
data-automation-id="adf-columns-selector-close-button"
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
|
role="menuitem"
|
||||||
|
[attr.aria-label]="'CLOSE' | translate"
|
||||||
(click)="closeMenu()"
|
(click)="closeMenu()"
|
||||||
>
|
>
|
||||||
<mat-icon>close</mat-icon>
|
<mat-icon>close</mat-icon>
|
||||||
@@ -33,6 +34,7 @@
|
|||||||
class="adf-columns-selector-search-input"
|
class="adf-columns-selector-search-input"
|
||||||
data-automation-id="adf-columns-selector-search-input"
|
data-automation-id="adf-columns-selector-search-input"
|
||||||
type="text"
|
type="text"
|
||||||
|
role="menuitem"
|
||||||
[placeholder]='"ADF-DATATABLE.COLUMNS_SELECTOR.SEARCH" | translate'>
|
[placeholder]='"ADF-DATATABLE.COLUMNS_SELECTOR.SEARCH" | translate'>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -43,7 +45,9 @@
|
|||||||
class="adf-columns-selector-column-checkbox"
|
class="adf-columns-selector-column-checkbox"
|
||||||
[attr.data-automation-id]="'adf-columns-selector-column-checkbox-' + column.title"
|
[attr.data-automation-id]="'adf-columns-selector-column-checkbox-' + column.title"
|
||||||
[checked]="!column.isHidden"
|
[checked]="!column.isHidden"
|
||||||
|
role="menuitem"
|
||||||
[disabled]="isCheckboxDisabled(column)"
|
[disabled]="isCheckboxDisabled(column)"
|
||||||
|
(keydown.enter)="changeColumnVisibility(column)"
|
||||||
(change)="changeColumnVisibility(column)">
|
(change)="changeColumnVisibility(column)">
|
||||||
<div class="adf-columns-selector-list-content">
|
<div class="adf-columns-selector-list-content">
|
||||||
{{column.title | translate}}
|
{{column.title | translate}}
|
||||||
@@ -68,6 +72,7 @@
|
|||||||
<div class="adf-columns-selector-footer">
|
<div class="adf-columns-selector-footer">
|
||||||
<button
|
<button
|
||||||
mat-flat-button
|
mat-flat-button
|
||||||
|
role="menuitem"
|
||||||
data-automation-id="adf-columns-selector-apply-button"
|
data-automation-id="adf-columns-selector-apply-button"
|
||||||
color="primary"
|
color="primary"
|
||||||
(click)="apply()">
|
(click)="apply()">
|
||||||
|
@@ -161,16 +161,18 @@
|
|||||||
title="{{ 'ADF-DATATABLE.CONTENT-ACTIONS.SELECT_COLUMNS' | translate }}"
|
title="{{ 'ADF-DATATABLE.CONTENT-ACTIONS.SELECT_COLUMNS' | translate }}"
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
#mainMenuTrigger="matMenuTrigger"
|
#mainMenuTrigger="matMenuTrigger"
|
||||||
(keydown.enter)="mainMenuTrigger.openMenu()"
|
(click)="onMainMenuOpen()"
|
||||||
[matMenuTriggerFor]="mainMenu">
|
[matMenuTriggerFor]="mainMenu">
|
||||||
<mat-icon>view_week_outline</mat-icon>
|
<mat-icon>view_week_outline</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<mat-menu #mainMenu>
|
<mat-menu #mainMenu (closed)="onMainMenuClosed()">
|
||||||
<ng-container
|
<div #mainMenuTemplate role="presentation" (keydown.tab)="$event.stopPropagation()">
|
||||||
[ngTemplateOutlet]="mainActionTemplate"
|
<ng-container
|
||||||
[ngTemplateOutletContext]="{
|
[ngTemplateOutlet]="mainActionTemplate"
|
||||||
$implicit: mainMenuTrigger
|
[ngTemplateOutletContext]="{
|
||||||
}" />
|
$implicit: mainMenuTrigger
|
||||||
|
}" />
|
||||||
|
</div>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
<span class="adf-sr-only">{{ 'ADF-DATATABLE.ACCESSIBILITY.ACTIONS' | translate }}</span>
|
<span class="adf-sr-only">{{ 'ADF-DATATABLE.ACCESSIBILITY.ACTIONS' | translate }}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@@ -34,6 +34,7 @@ import { MatCheckboxHarness } from '@angular/material/checkbox/testing';
|
|||||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||||
import { UnitTestingUtils } from '../../../testing/unit-testing-utils';
|
import { UnitTestingUtils } from '../../../testing/unit-testing-utils';
|
||||||
import { HarnessLoader } from '@angular/cdk/testing';
|
import { HarnessLoader } from '@angular/cdk/testing';
|
||||||
|
import { ConfigurableFocusTrapFactory } from '@angular/cdk/a11y';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-custom-column-template-component',
|
selector: 'adf-custom-column-template-component',
|
||||||
@@ -1508,15 +1509,19 @@ describe('DataTable', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Accesibility', () => {
|
describe('Accessibility', () => {
|
||||||
let fixture: ComponentFixture<DataTableComponent>;
|
let fixture: ComponentFixture<DataTableComponent>;
|
||||||
let dataTable: DataTableComponent;
|
let dataTable: DataTableComponent;
|
||||||
let columnCustomTemplate: TemplateRef<any>;
|
let columnCustomTemplate: TemplateRef<any>;
|
||||||
let testingUtils: UnitTestingUtils;
|
let testingUtils: UnitTestingUtils;
|
||||||
|
|
||||||
|
const focusTrapFactory = jasmine.createSpyObj('ConfigurableFocusTrapFactory', ['create']);
|
||||||
|
const focusTrap = jasmine.createSpyObj('ConfigurableFocusTrap', ['focusInitialElement', 'destroy']);
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CoreTestingModule, CustomColumnTemplateComponent],
|
imports: [CoreTestingModule, CustomColumnTemplateComponent],
|
||||||
|
providers: [{ provide: ConfigurableFocusTrapFactory, useValue: focusTrapFactory }],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
});
|
});
|
||||||
columnCustomTemplate = TestBed.createComponent(CustomColumnTemplateComponent).componentInstance.templateRef;
|
columnCustomTemplate = TestBed.createComponent(CustomColumnTemplateComponent).componentInstance.templateRef;
|
||||||
@@ -1708,6 +1713,36 @@ describe('Accesibility', () => {
|
|||||||
const cell = testingUtils.getByCSS('.adf-datatable-row[data-automation-id="datatable-row-0"] .adf-cell-value');
|
const cell = testingUtils.getByCSS('.adf-datatable-row[data-automation-id="datatable-row-0"] .adf-cell-value');
|
||||||
expect(cell?.nativeElement.getAttribute('tabindex')).toBe('0');
|
expect(cell?.nativeElement.getAttribute('tabindex')).toBe('0');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should create focus trap on main menu open', () => {
|
||||||
|
dataTable.showHeader = ShowHeaderMode.Always;
|
||||||
|
dataTable.showMainDatatableActions = true;
|
||||||
|
dataTable.mainActionTemplate = columnCustomTemplate;
|
||||||
|
focusTrapFactory.create.and.returnValue(focusTrap);
|
||||||
|
spyOn(dataTable, 'onMainMenuOpen').and.callThrough();
|
||||||
|
|
||||||
|
dataTable.ngOnChanges({
|
||||||
|
rows: new SimpleChange(null, [{ name: 'test1' }, { name: 'test2' }], false)
|
||||||
|
});
|
||||||
|
fixture.detectChanges();
|
||||||
|
dataTable.ngAfterViewInit();
|
||||||
|
|
||||||
|
testingUtils.clickByDataAutomationId('adf-datatable-main-menu-button');
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(dataTable.onMainMenuOpen).toHaveBeenCalled();
|
||||||
|
expect(focusTrapFactory.create).toHaveBeenCalledWith(dataTable.mainMenuTemplate.nativeElement);
|
||||||
|
expect(focusTrap.focusInitialElement).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should destroy focus trap on main menu closed', () => {
|
||||||
|
dataTable.focusTrap = focusTrap;
|
||||||
|
|
||||||
|
dataTable.onMainMenuClosed();
|
||||||
|
|
||||||
|
expect(focusTrap.destroy).toHaveBeenCalled();
|
||||||
|
expect(dataTable.focusTrap).toBeNull();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Drag&Drop column header', () => {
|
describe('Drag&Drop column header', () => {
|
||||||
|
@@ -38,10 +38,11 @@ import {
|
|||||||
SimpleChange,
|
SimpleChange,
|
||||||
SimpleChanges,
|
SimpleChanges,
|
||||||
TemplateRef,
|
TemplateRef,
|
||||||
|
ViewChild,
|
||||||
ViewChildren,
|
ViewChildren,
|
||||||
ViewEncapsulation
|
ViewEncapsulation
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { FocusKeyManager } from '@angular/cdk/a11y';
|
import { ConfigurableFocusTrap, ConfigurableFocusTrapFactory, FocusKeyManager } from '@angular/cdk/a11y';
|
||||||
import { MatCheckboxChange, MatCheckboxModule } from '@angular/material/checkbox';
|
import { MatCheckboxChange, MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
|
import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
|
||||||
import { Observable, Observer, Subscription } from 'rxjs';
|
import { Observable, Observer, Subscription } from 'rxjs';
|
||||||
@@ -133,6 +134,9 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges,
|
|||||||
@ViewChildren(DataTableRowComponent)
|
@ViewChildren(DataTableRowComponent)
|
||||||
rowsList: QueryList<DataTableRowComponent>;
|
rowsList: QueryList<DataTableRowComponent>;
|
||||||
|
|
||||||
|
@ViewChild('mainMenuTemplate')
|
||||||
|
mainMenuTemplate: ElementRef;
|
||||||
|
|
||||||
@ContentChild(DataColumnListComponent)
|
@ContentChild(DataColumnListComponent)
|
||||||
columnList: DataColumnListComponent;
|
columnList: DataColumnListComponent;
|
||||||
|
|
||||||
@@ -324,6 +328,7 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges,
|
|||||||
hoveredHeaderColumnIndex = -1;
|
hoveredHeaderColumnIndex = -1;
|
||||||
resizingColumnIndex = -1;
|
resizingColumnIndex = -1;
|
||||||
isDraggingRow = false;
|
isDraggingRow = false;
|
||||||
|
focusTrap: ConfigurableFocusTrap;
|
||||||
|
|
||||||
private keyManager: FocusKeyManager<DataTableRowComponent>;
|
private keyManager: FocusKeyManager<DataTableRowComponent>;
|
||||||
private clickObserver: Observer<DataRowEvent>;
|
private clickObserver: Observer<DataRowEvent>;
|
||||||
@@ -342,7 +347,13 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges,
|
|||||||
this.keyManager.onKeydown(event);
|
this.keyManager.onKeydown(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private elementRef: ElementRef, differs: IterableDiffers, private matIconRegistry: MatIconRegistry, private sanitizer: DomSanitizer) {
|
constructor(
|
||||||
|
private readonly elementRef: ElementRef,
|
||||||
|
differs: IterableDiffers,
|
||||||
|
private readonly matIconRegistry: MatIconRegistry,
|
||||||
|
private readonly sanitizer: DomSanitizer,
|
||||||
|
private readonly focusTrapFactory: ConfigurableFocusTrapFactory
|
||||||
|
) {
|
||||||
if (differs) {
|
if (differs) {
|
||||||
this.differ = differs.find([]).create(null);
|
this.differ = differs.find([]).create(null);
|
||||||
}
|
}
|
||||||
@@ -465,6 +476,20 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges,
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMainMenuOpen() {
|
||||||
|
if (this.mainMenuTemplate && !this.focusTrap) {
|
||||||
|
this.focusTrap = this.focusTrapFactory.create(this.mainMenuTemplate.nativeElement);
|
||||||
|
this.focusTrap.focusInitialElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMainMenuClosed() {
|
||||||
|
if (this.focusTrap) {
|
||||||
|
this.focusTrap.destroy();
|
||||||
|
this.focusTrap = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private initAndSubscribeClickStream() {
|
private initAndSubscribeClickStream() {
|
||||||
this.unsubscribeClickStream();
|
this.unsubscribeClickStream();
|
||||||
const singleClickStream = this.click$.pipe(
|
const singleClickStream = this.click$.pipe(
|
||||||
|
Reference in New Issue
Block a user