mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-09 18:03:21 +00:00
[ACS-10205]: Extends DataTableAdapter interface to handle focus for accessibility (#11506)
* [ACS-10205]: updates base DataTableAdapter class with optional props * [ACS-10205]: updates ShareDataTableAdapter with new props for focus handling * [ACS-10205]: bind new focus props to template * [ACS-10205]: uts update * [ACS-10205]: uts refactor * [ACS-10205]: fixes circullar deps
This commit is contained in:
@@ -493,4 +493,16 @@ describe('ShareDataTableAdapter', () => {
|
|||||||
expect(adapter.getRowByNodeId('fake-node-id-2')).toEqual(fakeShareDataRows[1]);
|
expect(adapter.getRowByNodeId('fake-node-id-2')).toEqual(fakeShareDataRows[1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should initialize with allowFocusOnRows as true by default', () => {
|
||||||
|
const adapter = new ShareDataTableAdapter(thumbnailService, contentService, null);
|
||||||
|
expect(adapter.allowFocusOnRows).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set allowFocusOnRows value', () => {
|
||||||
|
const adapter = new ShareDataTableAdapter(thumbnailService, contentService, null);
|
||||||
|
expect(adapter.allowFocusOnRows).toBe(true);
|
||||||
|
adapter.setAllowFocusOnTableRows(false);
|
||||||
|
expect(adapter.allowFocusOnRows).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -34,10 +34,11 @@ export class ShareDataTableAdapter implements DataTableAdapter {
|
|||||||
private filter: RowFilter;
|
private filter: RowFilter;
|
||||||
private imageResolver: any;
|
private imageResolver: any;
|
||||||
|
|
||||||
thumbnails: boolean = false;
|
thumbnails = false;
|
||||||
permissionsStyle: PermissionStyleModel[];
|
permissionsStyle: PermissionStyleModel[];
|
||||||
selectedRow: DataRow;
|
selectedRow: DataRow;
|
||||||
allowDropFiles: boolean;
|
allowDropFiles: boolean;
|
||||||
|
allowFocusOnRows = true;
|
||||||
|
|
||||||
set sortingMode(value: string) {
|
set sortingMode(value: string) {
|
||||||
let newValue = (value || 'client').toLowerCase();
|
let newValue = (value || 'client').toLowerCase();
|
||||||
@@ -196,6 +197,10 @@ export class ShareDataTableAdapter implements DataTableAdapter {
|
|||||||
this.imageResolver = resolver;
|
this.imageResolver = resolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setAllowFocusOnTableRows(allow: boolean) {
|
||||||
|
this.allowFocusOnRows = allow;
|
||||||
|
}
|
||||||
|
|
||||||
private getFolderIcon(node: any) {
|
private getFolderIcon(node: any) {
|
||||||
if (this.isSmartFolder(node)) {
|
if (this.isSmartFolder(node)) {
|
||||||
return this.thumbnailService.getMimeTypeIcon('smartFolder');
|
return this.thumbnailService.getMimeTypeIcon('smartFolder');
|
||||||
|
|||||||
@@ -206,7 +206,9 @@
|
|||||||
[ngClass]="getRowStyle(row)"
|
[ngClass]="getRowStyle(row)"
|
||||||
[class.adf-datatable-row__dragging]="isDraggingRow"
|
[class.adf-datatable-row__dragging]="isDraggingRow"
|
||||||
[attr.data-automation-id]="'datatable-row-' + idx"
|
[attr.data-automation-id]="'datatable-row-' + idx"
|
||||||
(contextmenu)="markRowAsContextMenuSource(row)">
|
(contextmenu)="markRowAsContextMenuSource(row)"
|
||||||
|
[disabled]="!(data.allowFocusOnRows ?? true)"
|
||||||
|
>
|
||||||
<!-- Drag button -->
|
<!-- Drag button -->
|
||||||
<div *ngIf="enableDragRows"
|
<div *ngIf="enableDragRows"
|
||||||
role="gridcell"
|
role="gridcell"
|
||||||
@@ -269,7 +271,7 @@
|
|||||||
[attr.aria-selected]="row.isSelected"
|
[attr.aria-selected]="row.isSelected"
|
||||||
[attr.aria-label]="col.title ? (col.title | translate) : null"
|
[attr.aria-label]="col.title ? (col.title | translate) : null"
|
||||||
(click)="onRowClick(row, $event)"
|
(click)="onRowClick(row, $event)"
|
||||||
tabindex="0"
|
[attr.tabindex]="col.focus ? 0 : null"
|
||||||
(keydown.enter)="onEnterKeyPressed(row, $any($event))"
|
(keydown.enter)="onEnterKeyPressed(row, $any($event))"
|
||||||
[adf-context-menu]="getContextMenuActions(row, col)"
|
[adf-context-menu]="getContextMenuActions(row, col)"
|
||||||
[adf-context-menu-enabled]="contextMenu"
|
[adf-context-menu-enabled]="contextMenu"
|
||||||
|
|||||||
@@ -1752,38 +1752,76 @@ describe('Accessibility', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove cell focus when [focus] is set to false', () => {
|
describe('DataTable row focus management', () => {
|
||||||
dataTable.showHeader = ShowHeaderMode.Never;
|
const testFocus = (focus: boolean, selector: string, expectedTabindex: string | null) => {
|
||||||
const dataRows = [{ name: 'name1' }];
|
dataTable.showHeader = ShowHeaderMode.Never;
|
||||||
|
const dataRows = [{ name: 'name1' }];
|
||||||
|
|
||||||
dataTable.data = new ObjectDataTableAdapter([], [new ObjectDataColumn({ key: 'name', template: columnCustomTemplate, focus: false })]);
|
dataTable.data = new ObjectDataTableAdapter([], [new ObjectDataColumn({ key: 'name', template: columnCustomTemplate, focus })]);
|
||||||
|
|
||||||
dataTable.ngOnChanges({
|
dataTable.ngOnChanges({
|
||||||
rows: new SimpleChange(null, dataRows, false)
|
rows: new SimpleChange(null, dataRows, false)
|
||||||
|
});
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
dataTable.ngAfterViewInit();
|
||||||
|
|
||||||
|
const element = testingUtils.getByCSS(selector);
|
||||||
|
expect(element?.nativeElement.getAttribute('tabindex')).toEqual(expectedTabindex);
|
||||||
|
};
|
||||||
|
|
||||||
|
const cellaValSelector = '.adf-datatable-row[data-automation-id="datatable-row-0"] .adf-cell-value';
|
||||||
|
const cellWrapperSelector = '.adf-datatable-cell';
|
||||||
|
|
||||||
|
it('should remove cell focus when [focus] is set to false', () => {
|
||||||
|
testFocus(false, cellaValSelector, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
fixture.detectChanges();
|
it('should allow element focus when [focus] is set to true', () => {
|
||||||
dataTable.ngAfterViewInit();
|
testFocus(true, cellaValSelector, '0');
|
||||||
|
});
|
||||||
|
|
||||||
const cell = testingUtils.getByCSS('.adf-datatable-row[data-automation-id="datatable-row-0"] .adf-cell-value');
|
it('should remove col focus when [focus] is set to false', () => {
|
||||||
expect(cell?.nativeElement.getAttribute('tabindex')).toBe(null);
|
testFocus(false, cellWrapperSelector, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow col focus when [focus] is set to true', () => {
|
||||||
|
testFocus(true, cellWrapperSelector, '0');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow element focus when [focus] is set to true', () => {
|
describe('ShareDatatable adapter allowFocusOnRows', () => {
|
||||||
dataTable.showHeader = ShowHeaderMode.Never;
|
class ShareAdapterMock extends ObjectDataTableAdapter {
|
||||||
const dataRows = [{ name: 'name1' }];
|
public allowFocusOnRows = true;
|
||||||
|
|
||||||
dataTable.data = new ObjectDataTableAdapter([], [new ObjectDataColumn({ key: 'name', template: columnCustomTemplate, focus: true })]);
|
constructor(data: any[], schema: DataColumn[]) {
|
||||||
|
super(data, schema);
|
||||||
|
}
|
||||||
|
|
||||||
dataTable.ngOnChanges({
|
setAllowFocusOnTableRows(allow: boolean) {
|
||||||
rows: new SimpleChange(null, dataRows, false)
|
this.allowFocusOnRows = allow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const testAllowFocusOnRows = (allowFocus: boolean, expectedTabindex: string | null) => {
|
||||||
|
const fakeDataRows = [new FakeDataRow(), new FakeDataRow()];
|
||||||
|
|
||||||
|
const adapter = new ShareAdapterMock([], []);
|
||||||
|
adapter.setRows(fakeDataRows);
|
||||||
|
adapter.setAllowFocusOnTableRows(allowFocus);
|
||||||
|
dataTable.data = adapter;
|
||||||
|
fixture.detectChanges();
|
||||||
|
const rowElements = testingUtils.getAllByCSS('.adf-datatable-body adf-datatable-row');
|
||||||
|
expect(rowElements.length).toBeGreaterThan(0);
|
||||||
|
expect(rowElements.every((row) => row.nativeElement.getAttribute('tabindex') === expectedTabindex)).toBeTrue();
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should set tabindex to null (disabled === true) on datatable-body rows when allowFocusOnRows is set to false in ShareDatatable adapter', () => {
|
||||||
|
testAllowFocusOnRows(false, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
fixture.detectChanges();
|
it('should set tabindex to 0 (disabled === false) on datatable-body rows when allowFocusOnRows is set to true in ShareDatatable adapter (default case)', () => {
|
||||||
dataTable.ngAfterViewInit();
|
testAllowFocusOnRows(true, '0');
|
||||||
|
});
|
||||||
const cell = testingUtils.getByCSS('.adf-datatable-row[data-automation-id="datatable-row-0"] .adf-cell-value');
|
|
||||||
expect(cell?.nativeElement.getAttribute('tabindex')).toBe('0');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create focus trap on main menu open', () => {
|
it('should create focus trap on main menu open', () => {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { Subject } from 'rxjs';
|
|||||||
|
|
||||||
export interface DataTableAdapter {
|
export interface DataTableAdapter {
|
||||||
rowsChanged?: Subject<Array<DataRow>>;
|
rowsChanged?: Subject<Array<DataRow>>;
|
||||||
|
allowFocusOnRows?: boolean;
|
||||||
|
|
||||||
selectedRow: DataRow;
|
selectedRow: DataRow;
|
||||||
getRows(): Array<DataRow>;
|
getRows(): Array<DataRow>;
|
||||||
@@ -33,4 +34,5 @@ export interface DataTableAdapter {
|
|||||||
getSorting(): DataSorting;
|
getSorting(): DataSorting;
|
||||||
setSorting(sorting: DataSorting): void;
|
setSorting(sorting: DataSorting): void;
|
||||||
sort(key?: string, direction?: string): void;
|
sort(key?: string, direction?: string): void;
|
||||||
|
setAllowFocusOnTableRows?(allow: boolean): void;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user