From e01cd40b1f28edc367a64aea6a746f4bf9bb8e30 Mon Sep 17 00:00:00 2001
From: Anton Ramanovich <90370279+rmnvch@users.noreply.github.com>
Date: Wed, 14 Jan 2026 12:43:29 +0100
Subject: [PATCH] [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
---
.../data/share-datatable-adapter.spec.ts | 12 +++
.../data/share-datatable-adapter.ts | 7 +-
.../datatable/datatable.component.html | 6 +-
.../datatable/datatable.component.spec.ts | 80 ++++++++++++++-----
.../lib/datatable/data/datatable-adapter.ts | 2 +
5 files changed, 83 insertions(+), 24 deletions(-)
diff --git a/lib/content-services/src/lib/document-list/data/share-datatable-adapter.spec.ts b/lib/content-services/src/lib/document-list/data/share-datatable-adapter.spec.ts
index 86919182a7..22944eca39 100644
--- a/lib/content-services/src/lib/document-list/data/share-datatable-adapter.spec.ts
+++ b/lib/content-services/src/lib/document-list/data/share-datatable-adapter.spec.ts
@@ -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);
+ });
});
diff --git a/lib/content-services/src/lib/document-list/data/share-datatable-adapter.ts b/lib/content-services/src/lib/document-list/data/share-datatable-adapter.ts
index 5549849488..78911da92a 100644
--- a/lib/content-services/src/lib/document-list/data/share-datatable-adapter.ts
+++ b/lib/content-services/src/lib/document-list/data/share-datatable-adapter.ts
@@ -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');
diff --git a/lib/core/src/lib/datatable/components/datatable/datatable.component.html b/lib/core/src/lib/datatable/components/datatable/datatable.component.html
index 186ee5a7e9..cce0da9956 100644
--- a/lib/core/src/lib/datatable/components/datatable/datatable.component.html
+++ b/lib/core/src/lib/datatable/components/datatable/datatable.component.html
@@ -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)"
+ >
{
});
});
- 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', () => {
diff --git a/lib/core/src/lib/datatable/data/datatable-adapter.ts b/lib/core/src/lib/datatable/data/datatable-adapter.ts
index 1284e16e79..a8aaa6305f 100644
--- a/lib/core/src/lib/datatable/data/datatable-adapter.ts
+++ b/lib/core/src/lib/datatable/data/datatable-adapter.ts
@@ -22,6 +22,7 @@ import { Subject } from 'rxjs';
export interface DataTableAdapter {
rowsChanged?: Subject
>;
+ allowFocusOnRows?: boolean;
selectedRow: DataRow;
getRows(): Array;
@@ -33,4 +34,5 @@ export interface DataTableAdapter {
getSorting(): DataSorting;
setSorting(sorting: DataSorting): void;
sort(key?: string, direction?: string): void;
+ setAllowFocusOnTableRows?(allow: boolean): void;
}