[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:
Anton Ramanovich
2026-01-14 12:43:29 +01:00
committed by GitHub
parent e44e00304a
commit e01cd40b1f
5 changed files with 83 additions and 24 deletions
@@ -493,4 +493,16 @@ describe('ShareDataTableAdapter', () => {
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 imageResolver: any;
thumbnails: boolean = false;
thumbnails = false;
permissionsStyle: PermissionStyleModel[];
selectedRow: DataRow;
allowDropFiles: boolean;
allowFocusOnRows = true;
set sortingMode(value: string) {
let newValue = (value || 'client').toLowerCase();
@@ -196,6 +197,10 @@ export class ShareDataTableAdapter implements DataTableAdapter {
this.imageResolver = resolver;
}
setAllowFocusOnTableRows(allow: boolean) {
this.allowFocusOnRows = allow;
}
private getFolderIcon(node: any) {
if (this.isSmartFolder(node)) {
return this.thumbnailService.getMimeTypeIcon('smartFolder');
@@ -206,7 +206,9 @@
[ngClass]="getRowStyle(row)"
[class.adf-datatable-row__dragging]="isDraggingRow"
[attr.data-automation-id]="'datatable-row-' + idx"
(contextmenu)="markRowAsContextMenuSource(row)">
(contextmenu)="markRowAsContextMenuSource(row)"
[disabled]="!(data.allowFocusOnRows ?? true)"
>
<!-- Drag button -->
<div *ngIf="enableDragRows"
role="gridcell"
@@ -269,7 +271,7 @@
[attr.aria-selected]="row.isSelected"
[attr.aria-label]="col.title ? (col.title | translate) : null"
(click)="onRowClick(row, $event)"
tabindex="0"
[attr.tabindex]="col.focus ? 0 : null"
(keydown.enter)="onEnterKeyPressed(row, $any($event))"
[adf-context-menu]="getContextMenuActions(row, col)"
[adf-context-menu-enabled]="contextMenu"
@@ -1752,38 +1752,76 @@ describe('Accessibility', () => {
});
});
it('should remove cell focus when [focus] is set to false', () => {
dataTable.showHeader = ShowHeaderMode.Never;
const dataRows = [{ name: 'name1' }];
describe('DataTable row focus management', () => {
const testFocus = (focus: boolean, selector: string, expectedTabindex: string | null) => {
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({
rows: new SimpleChange(null, dataRows, false)
dataTable.ngOnChanges({
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();
dataTable.ngAfterViewInit();
it('should allow element focus when [focus] is set to true', () => {
testFocus(true, cellaValSelector, '0');
});
const cell = testingUtils.getByCSS('.adf-datatable-row[data-automation-id="datatable-row-0"] .adf-cell-value');
expect(cell?.nativeElement.getAttribute('tabindex')).toBe(null);
it('should remove col focus when [focus] is set to false', () => {
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', () => {
dataTable.showHeader = ShowHeaderMode.Never;
const dataRows = [{ name: 'name1' }];
describe('ShareDatatable adapter allowFocusOnRows', () => {
class ShareAdapterMock extends ObjectDataTableAdapter {
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({
rows: new SimpleChange(null, dataRows, false)
setAllowFocusOnTableRows(allow: boolean) {
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();
dataTable.ngAfterViewInit();
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 set tabindex to 0 (disabled === false) on datatable-body rows when allowFocusOnRows is set to true in ShareDatatable adapter (default case)', () => {
testAllowFocusOnRows(true, '0');
});
});
it('should create focus trap on main menu open', () => {
@@ -22,6 +22,7 @@ import { Subject } from 'rxjs';
export interface DataTableAdapter {
rowsChanged?: Subject<Array<DataRow>>;
allowFocusOnRows?: boolean;
selectedRow: DataRow;
getRows(): Array<DataRow>;
@@ -33,4 +34,5 @@ export interface DataTableAdapter {
getSorting(): DataSorting;
setSorting(sorting: DataSorting): void;
sort(key?: string, direction?: string): void;
setAllowFocusOnTableRows?(allow: boolean): void;
}