Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ export class IgxGridFilteringCellComponent implements AfterViewInit, OnInit, DoC
return !this.column.grid.hasColumnLayouts ? this.column.isFirstPinned : false;;
}

@HostBinding('attr.role')
public role = 'gridcell';

private baseClass = 'igx-grid__filtering-cell-indicator';

constructor() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ export abstract class IgxGroupByAreaDirective {
@HostBinding('class.igx-grid-grouparea')
public defaultClass = true;

/**
* @hidden @internal
*/
@HostBinding('attr.role')
public role = 'presentation';

/** The parent grid containing the component. */
@Input()
public grid: FlatGridType | GridType;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div role="rowgroup" class="igx-grid-thead__wrapper" (scroll)="scroll($event)" [style.width.px]="width"
<div class="igx-grid-thead__wrapper" (scroll)="scroll($event)" [style.width.px]="width"
[class.igx-grid__tr--mrl]="hasMRL">

<!-- Column headers area -->
Expand All @@ -16,7 +16,7 @@

<!-- Row dragging area -->
@if (grid.rowDraggable) {
<div #headerDragContainer class="igx-grid__drag-indicator igx-grid__tr-action" (pointerdown)="$event.preventDefault()" [class.igx-grid__drag-indicator--header]="!grid.isRowSelectable">
<div #headerDragContainer class="igx-grid__drag-indicator igx-grid__tr-action" role="columnheader" (pointerdown)="$event.preventDefault()" [class.igx-grid__drag-indicator--header]="!grid.isRowSelectable">
<div style="visibility: hidden;">
<ng-container *ngTemplateOutlet="grid.dragIndicatorIconTemplate || grid.dragIndicatorIconBase"></ng-container>
</div>
Expand All @@ -25,7 +25,7 @@

<!-- Row selectors area -->
@if (grid.showRowSelectors) {
<div #headerSelectorContainer class="igx-grid__cbx-selection igx-grid__tr-action"
<div #headerSelectorContainer class="igx-grid__cbx-selection igx-grid__tr-action" role="columnheader"
[class.igx-grid__cbx-selection--push]="grid.filteringService.isFilterRowVisible"
(click)="headerRowSelection($event)"
(pointerdown)="$event.preventDefault()">
Expand All @@ -41,6 +41,7 @@
(click)="grid.toggleAll()"
(pointerdown)="$event.preventDefault()"
[hidden]="!grid.hasExpandableChildren || !grid.hasVisibleColumns"
role="columnheader"
[ngClass]="{
'igx-grid__hierarchical-expander igx-grid__hierarchical-expander--header igx-grid__tr-action': grid.hasExpandableChildren,
'igx-grid__hierarchical-expander--push': grid.filteringService.isFilterRowVisible,
Expand All @@ -55,7 +56,8 @@
@if (grid?.groupingExpressions?.length) {
<div #headerGroupContainer class="{{ indentationCSSClasses }}"
(click)="grid.toggleAllGroupRows()"
(pointerdown)="$event.preventDefault()">
(pointerdown)="$event.preventDefault()"
role="columnheader">
<ng-container *ngTemplateOutlet="grid.iconTemplate; context: { $implicit: grid }"></ng-container>
</div>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ export class IgxGridHeaderRowComponent implements DoCheck {
@Input()
public unpinnedColumnCollection: ColumnType[] = [];

/**
* @hidden @internal
*/
@HostBinding('attr.role')
public role = 'rowgroup';

@HostBinding('attr.aria-activedescendant')
public get activeDescendant() {
const activeElem = this.navigation.activeNode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ export class IgxGridToolbarComponent implements OnDestroy {
@HostBinding('class.igx-grid-toolbar')
public defaultStyle = true;

/**
* @hidden
* @internal
*/
@HostBinding('attr.role')
public role = 'presentation';

protected _grid: GridType;
protected sub: Subscription;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2452,6 +2452,11 @@ describe('IgxGrid - Row Selection #grid', () => {
expect(rowSelector.textContent).toBe('CUSTOM SELECTOR: 0');
expect(groupRowSelector.textContent).toBe('CUSTOM GROUP SELECTOR');
expect(headerSelector.textContent).toBe('CUSTOM HEADER SELECTOR');

// ARIA: row-selector wrappers must have the correct cell roles
expect(rowSelector.getAttribute('role')).toBe('gridcell');
expect(groupRowSelector.getAttribute('role')).toBe('gridcell');
expect(headerSelector.getAttribute('role')).toBe('columnheader');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
</ng-template>
<ng-template #defaultTemp>
@if (rowDraggable) {
<div [class]="resolveDragIndicatorClasses" [igxRowDrag]="this" (click)="$event.stopPropagation()" [ghostTemplate]="this.grid.getDragGhostCustomTemplate()">
<div [class]="resolveDragIndicatorClasses" role="gridcell" [igxRowDrag]="this" (click)="$event.stopPropagation()" [ghostTemplate]="this.grid.getDragGhostCustomTemplate()">
<ng-container *ngTemplateOutlet="this.grid.dragIndicatorIconTemplate ? this.grid.dragIndicatorIconTemplate : this.grid.dragIndicatorIconBase"></ng-container>
</div>
}
@if (this.showRowSelectors) {
<div class="igx-grid__cbx-selection igx-grid__tr-action" (pointerdown)="$event.preventDefault()" (click)="onRowSelectorClick($event)">
<div class="igx-grid__cbx-selection igx-grid__tr-action" role="gridcell" (pointerdown)="$event.preventDefault()" (click)="onRowSelectorClick($event)">
<ng-template *ngTemplateOutlet="
this.grid.rowSelectorTemplate ? this.grid.rowSelectorTemplate : rowSelectorBaseTemplate;
context: { $implicit: { index: viewIndex, rowID: key, key, selected: selected }}">
Expand Down
12 changes: 6 additions & 6 deletions projects/igniteui-angular/grids/grid/src/grid.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
>
</igx-grid-header-row>

<div igxGridBody (keydown.control.c)="copyHandler($event)" (copy)="copyHandler($event)" class="igx-grid__tbody" role="rowgroup">
<div class="igx-grid__tbody-content" tabindex="0" [attr.role]="dataView.length ? null : 'row'" (keydown)="navigation.handleNavigation($event)" (focus)="navigation.focusTbody($event)"
<div igxGridBody (keydown.control.c)="copyHandler($event)" (copy)="copyHandler($event)" class="igx-grid__tbody" role="presentation">
<div class="igx-grid__tbody-content" tabindex="0" [attr.role]="dataView.length ? 'rowgroup' : 'row'" (keydown)="navigation.handleNavigation($event)" (focus)="navigation.focusTbody($event)"
(dragStop)="selectionService.dragMode = $event" (scroll)="preventContainerScroll($event)"
(dragScroll)="dragScroll($event)" [igxGridDragSelect]="selectionService.dragMode"
[style.height.px]="totalHeight" [style.width.px]="totalCalcWidth" [style.width]="!platform.isBrowser ? '100%' : undefined" #tbody [attr.aria-activedescendant]="activeDescendant">
Expand Down Expand Up @@ -186,8 +186,8 @@
</div>


<div class="igx-grid__tfoot" role="rowgroup" [style.height.px]="summaryRowHeight" #tfoot>
<div tabindex="0" (focus)="navigation.focusFirstCell(false)" (keydown)="navigation.summaryNav($event)" [attr.aria-activedescendant]="activeDescendant">
<div class="igx-grid__tfoot" role="presentation" [style.height.px]="summaryRowHeight" #tfoot>
<div tabindex="0" role="rowgroup" (focus)="navigation.focusFirstCell(false)" (keydown)="navigation.summaryNav($event)" [attr.aria-activedescendant]="activeDescendant">
@if (hasSummarizedColumns && rootSummariesEnabled) {
<igx-grid-summary-row [style.width.px]="calcWidth" [style.height.px]="summaryRowHeight"
[gridID]="id" role="row"
Expand All @@ -200,7 +200,7 @@
</div>
</div>

<div class="igx-grid__scroll" [style.height.px]="scrollSize" #scr [hidden]="isHorizontalScrollHidden" (pointerdown)="$event.preventDefault()">
<div class="igx-grid__scroll" role="presentation" [style.height.px]="scrollSize" #scr [hidden]="isHorizontalScrollHidden" (pointerdown)="$event.preventDefault()">
<div class="igx-grid__scroll-start" [style.width.px]="pinnedStartWidth" [style.min-width.px]="pinnedStartWidth"></div>
<div class="igx-grid__scroll-main" [style.width.px]="unpinnedWidth">
<ng-template igxGridFor [igxGridForOf]="EMPTY_DATA" #scrollContainer>
Expand All @@ -209,7 +209,7 @@
<div class="igx-grid__scroll-end" [style.float]="'right'" [style.width.px]="pinnedEndWidth" [style.min-width.px]="pinnedEndWidth" [hidden]="pinnedEndWidth === 0"></div>
</div>

<div class="igx-grid__footer" #footer>
<div class="igx-grid__footer" role="presentation" #footer>
<ng-content select="igx-grid-footer,igc-grid-footer"></ng-content>
<ng-content select="igx-paginator,igc-paginator"></ng-content>
</div>
Expand Down
30 changes: 28 additions & 2 deletions projects/igniteui-angular/grids/grid/src/grid.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,9 +319,9 @@ describe('IgxGrid Component Tests #grid', () => {
fixture.componentInstance.generateData(30);
fixture.detectChanges();
tick(100);
// Checks if igx-grid__tbody-content attribute is null when there is data in the grid
// With data, igx-grid__tbody-content is the rowgroup focus host
const container = fixture.nativeElement.querySelectorAll('.igx-grid__tbody-content')[0];
expect(container.getAttribute('role')).toBe(null);
expect(container.getAttribute('role')).toBe('rowgroup');

//Filter grid so no results are available and grid is empty
grid.filter('index','111',IgxStringFilteringOperand.instance().condition('contains'),true);
Expand All @@ -339,6 +339,32 @@ describe('IgxGrid Component Tests #grid', () => {

}));

it('should have correct ARIA role structure on tbody and tfoot', fakeAsync(() => {
const fixture = TestBed.createComponent(IgxGridTestComponent);
fixture.componentInstance.columns[0].hasSummary = true;

fixture.componentInstance.generateData(30);
fixture.detectChanges();
tick(100);

// Outer tbody wrapper is layout-only
const tbodyWrapper = fixture.nativeElement.querySelector('.igx-grid__tbody');
expect(tbodyWrapper.getAttribute('role')).toBe('presentation');

// Inner focus host is the rowgroup
const tbodyContent = fixture.nativeElement.querySelector('.igx-grid__tbody-content');
expect(tbodyContent.getAttribute('role')).toBe('rowgroup');
expect(tbodyContent.getAttribute('tabindex')).toBe('0');

// Outer tfoot wrapper is layout-only
const tfootWrapper = fixture.nativeElement.querySelector('.igx-grid__tfoot');
expect(tfootWrapper.getAttribute('role')).toBe('presentation');

// Inner tfoot div is the rowgroup focus host
const tfootContent = fixture.nativeElement.querySelector('.igx-grid__tfoot > div');
expect(tfootContent.getAttribute('role')).toBe('rowgroup');
expect(tfootContent.getAttribute('tabindex')).toBe('0');
}));
it('should render empty message', fakeAsync(() => {
const fixture = TestBed.createComponent(IgxGridTestComponent);
fixture.componentInstance.data = [];
Expand Down
50 changes: 49 additions & 1 deletion projects/igniteui-angular/grids/grid/src/grid.groupby.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -790,11 +790,59 @@ describe('IgxGrid - GroupBy #grid', () => {
const groupRows = grid.groupsRowList.toArray();
for (const grRow of groupRows) {
const elem = grRow.element.nativeElement;
// host carries role="row", aria-describedby; aria-expanded moved to the toggle cell
expect(elem.getAttribute('role')).toBe('row');
expect(elem.attributes['aria-describedby'].value).toEqual(grid.id + '_Released');
expect(elem.attributes['aria-expanded'].value).toEqual('true');
const toggleCell = elem.querySelector('.igx-grid__grouping-indicator');
expect(toggleCell.getAttribute('role')).toBe('gridcell');
expect(toggleCell.getAttribute('aria-expanded')).toBe('true');
}
}));

it('should update aria-expanded on the toggle cell when a group row is collapsed and expanded', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
grid.primaryKey = 'ID';
fix.detectChanges();

grid.groupBy({ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false });
fix.detectChanges();

const grRow = grid.groupsRowList.toArray()[0];
const toggleCell = grRow.element.nativeElement.querySelector('.igx-grid__grouping-indicator');

expect(toggleCell.getAttribute('aria-expanded')).toBe('true');

grRow.toggle();
fix.detectChanges();

expect(toggleCell.getAttribute('aria-expanded')).toBe('false');

grRow.toggle();
fix.detectChanges();

expect(toggleCell.getAttribute('aria-expanded')).toBe('true');
}));

it('should assign role="gridcell" to all action wrappers inside a group row', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
grid.rowDraggable = true;
grid.rowSelection = GridSelectionMode.multiple;
fix.detectChanges();

grid.groupBy({ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false });
fix.detectChanges();

const grRow = grid.groupsRowList.toArray()[0];
const elem = grRow.element.nativeElement;

expect(elem.querySelector('.igx-grid__drag-indicator').getAttribute('role')).toBe('gridcell');
expect(elem.querySelector('.igx-grid__cbx-selection').getAttribute('role')).toBe('gridcell');
expect(elem.querySelector('.igx-grid__grouping-indicator').getAttribute('role')).toBe('gridcell');
expect(elem.querySelector('.igx-grid__group-content').getAttribute('role')).toBe('gridcell');
}));

it('should not apply grouping if the grouping expressions value is the same reference', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
fix.detectChanges();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<ng-container #defaultGroupRow>

@if (rowDraggable) {
<div class="igx-grid__drag-indicator igx-grid__tr-action">
<div class="igx-grid__drag-indicator igx-grid__tr-action" role="gridcell">
<igx-icon family="default" name="drag_indicator" [style.visibility]="'hidden'"></igx-icon>
</div>
}

@if (showRowSelectors) {
<div class="igx-grid__cbx-selection igx-grid__tr-action" (pointerdown)="$event.preventDefault()"
<div class="igx-grid__cbx-selection igx-grid__tr-action" role="gridcell" (pointerdown)="$event.preventDefault()"
(click)="onGroupSelectorClick($event)">
<ng-template #groupByRowSelector *ngTemplateOutlet="
this.grid.groupByRowSelectorTemplate ? this.grid.groupByRowSelectorTemplate : groupByRowSelectorBaseTemplate;
Expand All @@ -19,12 +19,12 @@
</div>
}

<div (click)="toggle()" class="igx-grid__grouping-indicator">
<div (click)="toggle()" class="igx-grid__grouping-indicator" role="gridcell" [attr.aria-expanded]="expanded">
<ng-container *ngTemplateOutlet="iconTemplate; context: { $implicit: this }">
</ng-container>
</div>

<div class="igx-grid__group-content" #groupContent>
<div class="igx-grid__group-content" role="gridcell" #groupContent>
<ng-container
*ngTemplateOutlet="grid.groupRowTemplate ? grid.groupRowTemplate : defaultGroupByTemplate; context: { $implicit: groupRow }">
</ng-container>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ export class IgxGridGroupByRowComponent implements OnDestroy {
* const groupRowExpanded = this.grid1.rowList.first.expanded;
* ```
*/
@HostBinding('attr.aria-expanded')
public get expanded(): boolean {
return this.grid.isExpandedGroup(this.groupRow);
}
Expand Down Expand Up @@ -229,6 +228,12 @@ export class IgxGridGroupByRowComponent implements OnDestroy {
(this.isActive() ? ` ${this.defaultCssClass}--active` : '');
}

/**
* @hidden @internal
*/
@HostBinding('attr.role')
public role = 'row';

public isActive() {
return this.grid.navigation.activeNode ? this.grid.navigation.activeNode.row === this.index : false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
>
</igx-grid-header-row>

<div igxGridBody (keydown.control.c)="copyHandler($event)" (copy)="copyHandler($event)" class="igx-grid__tbody" role="rowgroup">
<div igxGridBody (keydown.control.c)="copyHandler($event)" (copy)="copyHandler($event)" class="igx-grid__tbody" role="presentation">
<div class="igx-grid__tbody-content" tabindex="0" (focus)="navigation.focusTbody($event)"
(keydown)="navigation.handleNavigation($event)" (dragStop)="selectionService.dragMode = $event"
(dragScroll)="dragScroll($event)" [igxGridDragSelect]="selectionService.dragMode" [attr.aria-activedescendant]="activeDescendant" [attr.role]="dataView.length ? null : 'row'"
(dragScroll)="dragScroll($event)" [igxGridDragSelect]="selectionService.dragMode" [attr.aria-activedescendant]="activeDescendant" [attr.role]="dataView.length ? 'rowgroup' : 'row'"
[style.height.px]="totalHeight" [style.width.px]="totalCalcWidth" [style.width]="!platform.isBrowser ? '100%' : undefined" #tbody (scroll)="preventContainerScroll($event)">
@if (moving && columnInDrag && pinnedColumns.length <= 0) {
<span
Expand Down Expand Up @@ -150,8 +150,8 @@
</div>
</div>

<div class="igx-grid__tfoot" role="rowgroup" [style.height.px]="summaryRowHeight" #tfoot>
<div tabindex="0" (focus)="navigation.focusFirstCell(false)" [attr.aria-activedescendant]="activeDescendant"
<div class="igx-grid__tfoot" role="presentation" [style.height.px]="summaryRowHeight" #tfoot>
<div tabindex="0" role="rowgroup" (focus)="navigation.focusFirstCell(false)" [attr.aria-activedescendant]="activeDescendant"
(keydown)="navigation.summaryNav($event)">
@if (hasSummarizedColumns && rootSummariesEnabled) {
<igx-grid-summary-row [style.width.px]="calcWidth" [style.height.px]="summaryRowHeight"
Expand All @@ -165,7 +165,7 @@
</div>
</div>

<div class="igx-grid__scroll" [style.height.px]="scrollSize" #scr [hidden]="isHorizontalScrollHidden" (pointerdown)="$event.preventDefault()">
<div class="igx-grid__scroll" role="presentation" [style.height.px]="scrollSize" #scr [hidden]="isHorizontalScrollHidden" (pointerdown)="$event.preventDefault()">
<div class="igx-grid__scroll-start" [style.width.px]="pinnedStartWidth" [style.min-width.px]="pinnedStartWidth"></div>
<div class="igx-grid__scroll-main" [style.width.px]="unpinnedWidth">
<ng-template igxGridFor [igxGridForOf]="[]" #scrollContainer>
Expand All @@ -174,7 +174,7 @@
<div class="igx-grid__scroll-end" [style.width.px]="pinnedEndWidth" [style.min-width.px]="pinnedEndWidth" [hidden]="pinnedEndWidth === 0"></div>
</div>

<div class="igx-grid__footer" #footer>
<div class="igx-grid__footer" role="presentation" #footer>
<ng-content select="igx-grid-footer,igc-grid-footer"></ng-content>
<ng-content select="igx-paginator,igc-paginator"></ng-content>
<ng-container #paginatorOutlet></ng-container>
Expand Down
Loading
Loading