Skip to content

Commit

Permalink
perf(cdk/table): Optimize a11y role logic in CdkCell.
Browse files Browse the repository at this point in the history
  • Loading branch information
kseamon committed Jan 4, 2024
1 parent a8b8e62 commit d4fb042
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 9 deletions.
10 changes: 4 additions & 6 deletions src/cdk/table/cell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,8 @@ export class CdkHeaderCell extends BaseCdkCell {
export class CdkFooterCell extends BaseCdkCell {
constructor(columnDef: CdkColumnDef, elementRef: ElementRef) {
super(columnDef, elementRef);
if (columnDef._table?._elementRef.nativeElement.nodeType === 1) {
const tableRole = columnDef._table._elementRef.nativeElement.getAttribute('role');
const role = tableRole === 'grid' || tableRole === 'treegrid' ? 'gridcell' : 'cell';
const role = columnDef._table?._cellRole;
if (role) {
elementRef.nativeElement.setAttribute('role', role);
}
}
Expand All @@ -210,9 +209,8 @@ export class CdkFooterCell extends BaseCdkCell {
export class CdkCell extends BaseCdkCell {
constructor(columnDef: CdkColumnDef, elementRef: ElementRef) {
super(columnDef, elementRef);
if (columnDef._table?._elementRef.nativeElement.nodeType === 1) {
const tableRole = columnDef._table._elementRef.nativeElement.getAttribute('role');
const role = tableRole === 'grid' || tableRole === 'treegrid' ? 'gridcell' : 'cell';
const role = columnDef._table?._cellRole;
if (role) {
elementRef.nativeElement.setAttribute('role', role);
}
}
Expand Down
21 changes: 21 additions & 0 deletions src/cdk/table/table.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,27 @@ describe('CdkTable', () => {
]);
});

it('defaults to table role in native HTML table', () => {
const fixture = createComponent(NativeHtmlTableApp);
const tableElement = fixture.nativeElement.querySelector('table');
fixture.detectChanges();
expect(tableElement.getAttribute('role')).toBe('table');

expect(getHeaderRows(tableElement)[0].getAttribute('role')).toBe('row');
const header = getHeaderRows(tableElement)[0];
getHeaderCells(header).forEach(cell => {
expect(cell.getAttribute('role')).toBe('columnheader');
});

getRows(tableElement).forEach(row => {
expect(row.getAttribute('role')).toBe('row');
getCells(row).forEach(cell => {
// Native role of TD elements is row.
expect(cell.getAttribute('role')).toBe(null);
});
});
});

it('should be able to nest tables', () => {
const thisFixture = createComponent(NestedHtmlTableApp);
thisFixture.detectChanges();
Expand Down
15 changes: 13 additions & 2 deletions src/cdk/table/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,17 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes
/** Whether the no data row is currently showing anything. */
private _isShowingNoDataRow = false;

/** Aria role to apply to the table's cells based on the table's own role. */
get _cellRole(): string | null {
if (this._cellRoleInternal === undefined) {
const cellRole = this._role === 'grid' || this._role === 'treegrid' ? 'gridcell' : 'cell';
this._cellRoleInternal = this._isNativeHtmlTable && cellRole === 'cell' ? null : cellRole;
}

return this._cellRoleInternal;
}
private _cellRoleInternal: string | null | undefined = undefined;

/**
* Tracking function that will be used to check the differences in data changes. Used similarly
* to `ngFor` `trackBy` function. Optimize row operations by identifying a row based on its data
Expand Down Expand Up @@ -534,7 +545,7 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes
protected readonly _differs: IterableDiffers,
protected readonly _changeDetectorRef: ChangeDetectorRef,
protected readonly _elementRef: ElementRef,
@Attribute('role') role: string,
@Attribute('role') protected readonly _role: string,
@Optional() protected readonly _dir: Directionality,
@Inject(DOCUMENT) _document: any,
private _platform: Platform,
Expand All @@ -558,7 +569,7 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes
@Optional()
protected readonly _ngZone?: NgZone,
) {
if (!role) {
if (!_role) {
this._elementRef.nativeElement.setAttribute('role', 'table');
}

Expand Down
5 changes: 4 additions & 1 deletion tools/public_api_guard/cdk/table.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,13 +289,14 @@ export class CdkRowDef<T> extends BaseRowDef {

// @public
export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDestroy, OnInit {
constructor(_differs: IterableDiffers, _changeDetectorRef: ChangeDetectorRef, _elementRef: ElementRef, role: string, _dir: Directionality, _document: any, _platform: Platform, _viewRepeater: _ViewRepeater<T, RenderRow<T>, RowContext<T>>, _coalescedStyleScheduler: _CoalescedStyleScheduler, _viewportRuler: ViewportRuler,
constructor(_differs: IterableDiffers, _changeDetectorRef: ChangeDetectorRef, _elementRef: ElementRef, _role: string, _dir: Directionality, _document: any, _platform: Platform, _viewRepeater: _ViewRepeater<T, RenderRow<T>, RowContext<T>>, _coalescedStyleScheduler: _CoalescedStyleScheduler, _viewportRuler: ViewportRuler,
_stickyPositioningListener: StickyPositioningListener,
_ngZone?: NgZone | undefined);
addColumnDef(columnDef: CdkColumnDef): void;
addFooterRowDef(footerRowDef: CdkFooterRowDef): void;
addHeaderRowDef(headerRowDef: CdkHeaderRowDef): void;
addRowDef(rowDef: CdkRowDef<T>): void;
get _cellRole(): string | null;
// (undocumented)
protected readonly _changeDetectorRef: ChangeDetectorRef;
// (undocumented)
Expand Down Expand Up @@ -349,6 +350,8 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes
removeRowDef(rowDef: CdkRowDef<T>): void;
renderRows(): void;
// (undocumented)
protected readonly _role: string;
// (undocumented)
_rowOutlet: DataRowOutlet;
setNoDataRow(noDataRow: CdkNoDataRow | null): void;
protected stickyCssClass: string;
Expand Down

0 comments on commit d4fb042

Please sign in to comment.