diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index eaacd3064e5..4e4bc00dca9 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -517,11 +517,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT /** @hidden @internal */ @HostBinding('attr.aria-describedby') public get ariaDescribeBy() { - let describeBy = (this.gridID + '_' + this.column.field).replace('.', '_'); - if (this.isInvalid) { - describeBy += ' ' + this.ariaErrorMessage; - } - return describeBy; + return this.isInvalid ? this.ariaErrorMessage : null; } /** @hidden @internal */ diff --git a/projects/igniteui-angular/src/lib/grids/grid/column-group.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/column-group.spec.ts index e78c32fe29c..01f954a99e0 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/column-group.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/column-group.spec.ts @@ -145,6 +145,65 @@ describe('IgxGrid - multi-column headers #grid', () => { })); + it('Should render a hidden row of the leaf column headers for accessibility purposes.', fakeAsync(() => { + fixture = TestBed.createComponent(BlueWhaleGridComponent) as ComponentFixture; + (fixture as ComponentFixture).componentInstance.firstGroupRepeats = 0; + (fixture as ComponentFixture).componentInstance.secondGroupRepeats = 0; + tick(); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + grid = fixture.componentInstance.grid; + const gridHeader = GridFunctions.getGridHeader(grid); + + const groupHeaderEls = Array.from(gridHeader.nativeElement.querySelectorAll('.' + GRID_COL_GROUP_THEAD_TITLE_CLASS)); + for (const header of groupHeaderEls) { + expect(header.getAttribute('aria-hidden')).toBe('true'); + } + + const columnHeaders = GridFunctions.getColumnHeaders(fixture); + for (const header of columnHeaders) { + expect(header.nativeNode.getAttribute('aria-hidden')).toBe('true'); + } + + const hiddenRow = gridHeader.nativeElement.querySelectorAll('[role="row"]')[1]; + const horizontalVirtualization = grid.rowList.first.virtDirRow; + const chunkSize = horizontalVirtualization.state.chunkSize; + + expect(hiddenRow.children.length).toBeLessThanOrEqual(chunkSize); + expect(grid.columns.length).toBeGreaterThan(chunkSize); + + expect(hiddenRow.children[0].textContent).toBe('ID'); + expect(hiddenRow.children[1].textContent).toBe('Company Name'); + expect(hiddenRow.children[2].textContent).toBe('ContactName'); + expect(hiddenRow.children[3].textContent).toBe('ContactTitle'); + expect(hiddenRow.children[4].textContent).toBe('Country'); + expect(hiddenRow.children[5].textContent).toBe('Region'); + expect(hiddenRow.children[6].textContent).toBe('City'); + })); + + it('The hidden row of the leaf columns contains only headers of the rendered cells', fakeAsync(() => { + fixture = TestBed.createComponent(BlueWhaleGridComponent) as ComponentFixture; + tick(); + fixture.detectChanges(); + + grid = fixture.componentInstance.grid; + const horizontalVirtualization = grid.rowList.first.virtDirRow; + const chunkSize = horizontalVirtualization.state.chunkSize; + + tick(); + fixture.detectChanges(); + + const gridHeader = GridFunctions.getGridHeader(grid); + const hiddenRow = gridHeader.nativeElement.querySelectorAll('[role="row"]')[1]; + + expect(hiddenRow.children.length).toBeLessThanOrEqual(chunkSize); + for (const ariaHeader of Array.from(hiddenRow.children)) { + expect(ariaHeader.textContent).toBe('ID'); + } + })); + it('Should render dynamic column group header correctly (#12165).', () => { fixture = TestBed.createComponent(BlueWhaleGridComponent) as ComponentFixture; (fixture as ComponentFixture).componentInstance.firstGroupRepeats = 1; diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.pinning.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.pinning.spec.ts index 7fb0029c904..3b10f661ba8 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.pinning.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.pinning.spec.ts @@ -577,8 +577,6 @@ describe('IgxGrid - Column Pinning #grid', () => { GridSelectionFunctions.verifyRowHasCheckbox(row); expect(GridFunctions.getRowDisplayContainer(fix, 0)).toBeDefined(); - expect(row.children[2].getAttribute('aria-describedby')).toBe(grid.id + '_CompanyName'); - expect(row.children[3].getAttribute('aria-describedby')).toBe(grid.id + '_ContactName'); // check scrollbar DOM const scrBarStartSection = fix.debugElement.query(By.css(`${GRID_SCROLL_CLASS}-start`)); @@ -695,7 +693,6 @@ describe('IgxGrid - Column Pinning #grid', () => { for (let i = 0; i <= pinnedCols.length - 1; i++) { const elem = row.children[i + 1]; expect(parseInt((elem as any).style.left, 10)).toBe(-330); - expect(elem.getAttribute('aria-describedby')).toBe(grid.id + '_' + pinnedCols[i].field); } // check correct headers have left border diff --git a/projects/igniteui-angular/src/lib/grids/headers/grid-header-group.component.html b/projects/igniteui-angular/src/lib/grids/headers/grid-header-group.component.html index 35ad981e793..0532a6916af 100644 --- a/projects/igniteui-angular/src/lib/grids/headers/grid-header-group.component.html +++ b/projects/igniteui-angular/src/lib/grids/headers/grid-header-group.component.html @@ -42,6 +42,7 @@ }
+ + @if (grid.hasColumnGroups) { +
+ @for (column of visibleLeafColumns; track column.index) { +
{{ column.header || column.field }}
+ } +
+ } + @if (grid.filteringService.isFilterRowVisible) { group.filter); } + /** + * Gets a list of all visible leaf columns in the grid. + * + * @hidden @internal + */ + public get visibleLeafColumns(): ColumnType[] { + const row = this.grid.gridAPI.get_row_by_index(this.grid.rowList.first?.index || 0); + if (row && row.cells) { + return row.cells.map(cell => cell.column); + } + } + + /** + * @hidden + * @internal + */ + public get isLeafHeaderAriaHidden(): boolean { + return this.grid.navigation.activeNode.row === -1; + } + /** The virtualized part of the header row containing the unpinned header groups. */ @ViewChild('headerVirtualContainer', { read: IgxGridForOfDirective, static: true }) public headerContainer: IgxGridForOfDirective; diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-header-row.component.html b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-header-row.component.html index b238803b861..289bdae4020 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-header-row.component.html +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-header-row.component.html @@ -310,6 +310,13 @@ } }
+ + +
+ @for (column of visibleLeafColumns; track column.index) { +
{{ column.header || column.field }}
+ } +
diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-header-row.component.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-header-row.component.ts index d2510db1ac2..2622622ae5e 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-header-row.component.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-header-row.component.ts @@ -203,6 +203,14 @@ export class IgxPivotHeaderRowComponent extends IgxGridHeaderRowComponent implem return this.totalDepth * this.grid.renderedRowHeight; } + /** + * @hidden + * @internal + */ + public override get isLeafHeaderAriaHidden(): boolean { + return super.isLeafHeaderAriaHidden || this.grid.navigation.isRowHeaderActive || this.grid.navigation.isRowDimensionHeaderActive; + } + /** * @hidden * @internal