Skip to content

Commit d4f5859

Browse files
committed
feat(grid): correct aria attributes related to total rows/cols count and indexes
2 parents 64f9aeb + 8911970 commit d4f5859

13 files changed

+142
-24
lines changed

projects/igniteui-angular/src/lib/carousel/carousel-base.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,14 @@ export abstract class IgxCarouselComponentBase implements OnDestroy {
5252
}
5353

5454
public ngOnDestroy(): void {
55-
this.enterAnimationPlayer?.destroy();
56-
this.leaveAnimationPlayer?.destroy();
55+
if (this.enterAnimationPlayer) {
56+
this.enterAnimationPlayer.destroy();
57+
this.enterAnimationPlayer = null;
58+
}
59+
if (this.leaveAnimationPlayer) {
60+
this.leaveAnimationPlayer.destroy();
61+
this.leaveAnimationPlayer = null;
62+
}
5763
}
5864

5965
/** @hidden */

projects/igniteui-angular/src/lib/carousel/carousel.component.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,17 @@ describe('Carousel', () => {
745745
expect(carousel.get(1).nativeElement.classList.contains(HelperTestFunctions.ACTIVE_SLIDE_CLASS)).toBeTruthy();
746746
expect(carousel.get(0).nativeElement.classList.contains(HelperTestFunctions.PREVIOUS_SLIDE_CLASS)).toBeFalsy();
747747
});
748+
749+
it('should not throw an error when playing an animation and destroying the component - #15976', () => {
750+
expect(() => {
751+
carousel.next();
752+
carousel.ngOnDestroy();
753+
fixture.detectChanges();
754+
}).not.toThrow();
755+
756+
expect(carousel['enterAnimationPlayer']).toBe(null);
757+
expect(carousel['leaveAnimationPlayer']).toBe(null);
758+
});
748759
});
749760

750761
describe('Dynamic Slides: ', () => {

projects/igniteui-angular/src/lib/core/styles/components/drop-down/_drop-down-theme.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@
259259
-webkit-overflow-scrolling: touch;
260260
position: relative;
261261

262-
igx-display-container {
262+
.igx-display-container--scrollbar {
263263
padding-inline-end: var(--vhelper-scrollbar-size);
264264
}
265265
}

projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,14 @@
131131
%advanced-filter {
132132
@include sizable();
133133

134+
--_tree-scrollbar-gutter: #{rem(16px)};
135+
136+
@if $variant == 'bootstrap' {
137+
--query-builder-outer-padding: #{rem(16px)};
138+
} @else {
139+
--query-builder-outer-padding: #{rem(24px)};
140+
}
141+
134142
width: auto;
135143
min-width: rem(660px);
136144
background-color: var-get($theme, 'background');
@@ -139,11 +147,19 @@
139147
overflow: hidden;
140148

141149
&:has(:not(igx-query-builder-header)) {
142-
padding-block-start: if($variant != 'bootstrap', rem(24px), rem(16px))
150+
padding-block-start: var(--query-builder-outer-padding);
151+
152+
%query-level-0 {
153+
padding-block: 0 var(--query-builder-outer-padding);
154+
}
143155
}
144156

145157
&:has(igx-query-builder-header) {
146158
padding-block-start: 0;
159+
160+
%query-level-0 {
161+
padding-block: if($variant != 'bootstrap', 0, rem(16px)) var(--query-builder-outer-padding);
162+
}
147163
}
148164

149165
.igx-chip__ghost {
@@ -162,10 +178,8 @@
162178
%query-level-0 {
163179
display: block;
164180
width: 100%;
165-
padding-inline: if($variant != 'bootstrap', rem(24px), rem(16px));
166-
padding-block:
167-
if($variant != 'bootstrap', 0, rem(16px))
168-
if($variant != 'bootstrap', rem(24px), rem(16px));
181+
182+
padding-inline: var(--query-builder-outer-padding);
169183

170184
> %advanced-filter__main {
171185
gap: rem(16px);
@@ -176,7 +190,7 @@
176190
max-height: rem(570px);
177191
overflow-y: auto;
178192
overflow-x: hidden;
179-
padding-inline-end: rem(16px);
193+
padding-inline-end: var(--_tree-scrollbar-gutter);
180194
}
181195
}
182196
}
@@ -247,8 +261,8 @@
247261
margin-bottom: 0;
248262
border-block-end: rem(1px) solid var-get($theme, 'header-border');
249263

250-
padding-inline: if($variant != 'bootstrap', rem(24px), rem(16px));
251-
padding-block: if($variant != 'bootstrap', rem(24px), rem(16px)) rem(16px);
264+
padding-inline: var(--query-builder-outer-padding);
265+
padding-block: var(--query-builder-outer-padding) rem(16px);
252266
}
253267

254268
%advanced-filter__title {

projects/igniteui-angular/src/lib/directives/for-of/base.helper.component.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ import {
77
AfterViewInit,
88
Inject,
99
NgZone,
10-
DOCUMENT
10+
Renderer2,
11+
PLATFORM_ID,
12+
inject
1113
} from '@angular/core';
1214
import { Subject } from 'rxjs';
1315
import { takeUntil, throttleTime } from 'rxjs/operators';
1416
import { resizeObservable, PlatformUtil } from '../../core/utils';
17+
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
1518

1619
@Directive({
1720
selector: '[igxVirtualHelperBase]',
@@ -27,13 +30,16 @@ export class VirtualHelperBaseDirective implements OnDestroy, AfterViewInit {
2730
private _afterViewInit = false;
2831
private _scrollNativeSize: number;
2932
private _detached = false;
33+
protected renderer = inject(Renderer2);
34+
protected platformId = inject(PLATFORM_ID);
35+
protected ngZone = inject(NgZone);
3036

3137
constructor(
3238
public elementRef: ElementRef<HTMLElement>,
3339
public cdr: ChangeDetectorRef,
3440
protected _zone: NgZone,
3541
@Inject(DOCUMENT) public document: any,
36-
protected platformUtil: PlatformUtil,
42+
protected platformUtil: PlatformUtil
3743
) {
3844
this._scrollNativeSize = this.calculateScrollNativeSize();
3945
}
@@ -104,6 +110,34 @@ export class VirtualHelperBaseDirective implements OnDestroy, AfterViewInit {
104110
return this.document.body.contains(this.nativeElement);
105111
}
106112

113+
private toggleClass(element: HTMLElement, className: string, shouldHaveClass: boolean): void {
114+
if (shouldHaveClass) {
115+
this.renderer.addClass(element, className);
116+
} else {
117+
this.renderer.removeClass(element, className);
118+
}
119+
}
120+
121+
private updateScrollbarClass() {
122+
if (!isPlatformBrowser(this.platformId)) {
123+
return;
124+
}
125+
126+
this.ngZone.runOutsideAngular(() => {
127+
requestAnimationFrame(() => {
128+
const el = this.nativeElement;
129+
const hasScrollbar = el.scrollHeight > el.clientHeight;
130+
const prevSibling = el.previousElementSibling as HTMLElement | null;
131+
const scrollbarClass = 'igx-display-container--scrollbar';
132+
133+
if (prevSibling?.tagName.toLowerCase() === 'igx-display-container') {
134+
this.toggleClass(prevSibling, scrollbarClass, hasScrollbar);
135+
}
136+
});
137+
});
138+
}
139+
140+
107141
protected handleMutations(event) {
108142
const hasSize = !(event[0].contentRect.height === 0 && event[0].contentRect.width === 0);
109143
if (!hasSize && !this.isAttachedToDom) {
@@ -113,6 +147,8 @@ export class VirtualHelperBaseDirective implements OnDestroy, AfterViewInit {
113147
// attached back now.
114148
this.restoreScroll();
115149
}
150+
151+
this.updateScrollbarClass();
116152
}
117153

118154
protected restoreScroll() {}

projects/igniteui-angular/src/lib/directives/for-of/horizontal.virtual.helper.component.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { Component, ElementRef, HostBinding, Input, ViewChild, ViewContainerRef, ChangeDetectorRef, Inject, NgZone, DOCUMENT } from '@angular/core';
1+
import { Component, ElementRef, HostBinding, Input, ViewChild, ViewContainerRef, ChangeDetectorRef, Inject, NgZone } from '@angular/core';
22
import { VirtualHelperBaseDirective } from './base.helper.component';
3+
import { DOCUMENT } from '@angular/common';
34
import { PlatformUtil } from '../../core/utils';
45

56
/**
@@ -18,7 +19,13 @@ export class HVirtualHelperComponent extends VirtualHelperBaseDirective {
1819
@HostBinding('class')
1920
public cssClasses = 'igx-vhelper--horizontal';
2021

21-
constructor(elementRef: ElementRef, cdr: ChangeDetectorRef, zone: NgZone, @Inject(DOCUMENT) document, platformUtil: PlatformUtil) {
22+
constructor(
23+
elementRef: ElementRef,
24+
cdr: ChangeDetectorRef,
25+
zone: NgZone,
26+
@Inject(DOCUMENT) document: any,
27+
platformUtil: PlatformUtil
28+
) {
2229
super(elementRef, cdr, zone, document, platformUtil);
2330
}
2431

projects/igniteui-angular/src/lib/directives/for-of/virtual.helper.component.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Component, ElementRef, HostBinding, Input, ViewChild, ViewContainerRef,
2-
ChangeDetectorRef, OnDestroy, OnInit, Inject, NgZone, DOCUMENT } from '@angular/core';
2+
ChangeDetectorRef, OnDestroy, OnInit, Inject, NgZone} from '@angular/core';
33
import { VirtualHelperBaseDirective } from './base.helper.component';
4+
import { DOCUMENT } from '@angular/common';
45
import { PlatformUtil } from '../../core/utils';
56

67
@Component({
@@ -20,7 +21,13 @@ export class VirtualHelperComponent extends VirtualHelperBaseDirective implement
2021
@HostBinding('class')
2122
public cssClasses = 'igx-vhelper--vertical';
2223

23-
constructor(elementRef: ElementRef, cdr: ChangeDetectorRef, zone: NgZone, @Inject(DOCUMENT) document, platformUtil: PlatformUtil) {
24+
constructor(
25+
elementRef: ElementRef,
26+
cdr: ChangeDetectorRef,
27+
zone: NgZone,
28+
@Inject(DOCUMENT) document: any,
29+
platformUtil: PlatformUtil,
30+
) {
2431
super(elementRef, cdr, zone, document, platformUtil);
2532
}
2633

projects/igniteui-angular/src/lib/grids/cell.component.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -702,12 +702,13 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT
702702

703703
@HostBinding('attr.aria-rowindex')
704704
protected get ariaRowIndex(): number {
705-
return this.rowIndex + 1;
705+
// +2 because aria-rowindex is 1-based and the first row is the header
706+
return this.rowIndex + 2;
706707
}
707708

708709
@HostBinding('attr.aria-colindex')
709710
protected get ariaColIndex(): number {
710-
return this.visibleColumnIndex + 1;
711+
return this.column.index + 1;
711712
}
712713

713714
@ViewChild('defaultCell', { read: TemplateRef, static: true })

projects/igniteui-angular/src/lib/grids/grid-base.directive.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1839,6 +1839,18 @@ export abstract class IgxGridBaseDirective implements GridType,
18391839
@HostBinding('class.igx-grid')
18401840
protected baseClass = 'igx-grid';
18411841

1842+
@HostBinding('attr.aria-colcount')
1843+
protected get ariaColCount(): number {
1844+
return this.visibleColumns.length;
1845+
}
1846+
1847+
@HostBinding('attr.aria-rowcount')
1848+
protected get ariaRowCount(): number {
1849+
if (this.paginator) {
1850+
return this._totalRecords;
1851+
}
1852+
return ((this as any).totalItemCount || this.data?.length || 0) + 1;
1853+
}
18421854

18431855
/**
18441856
* Gets/Sets the resource strings.

projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { FilteringLogic } from '../../data-operations/filtering-expression.inter
2121
import { IgxTabContentComponent, IgxTabHeaderComponent, IgxTabItemComponent, IgxTabsComponent } from '../../tabs/tabs/public_api';
2222
import { IgxGridRowComponent } from './grid-row.component';
2323
import { ISortingExpression, SortingDirection } from '../../data-operations/sorting-strategy';
24-
import { GRID_SCROLL_CLASS } from '../../test-utils/grid-functions.spec';
24+
import { GRID_SCROLL_CLASS, GridFunctions } from '../../test-utils/grid-functions.spec';
2525
import { AsyncPipe } from '@angular/common';
2626
import { IgxPaginatorComponent, IgxPaginatorContentDirective } from '../../paginator/paginator.component';
2727
import { IGridRowEventArgs, IgxColumnGroupComponent, IgxGridEmptyTemplateDirective, IgxGridFooterComponent, IgxGridLoadingTemplateDirective, IgxGridRow, IgxGroupByRow, IgxSummaryRow } from '../public_api';
@@ -2017,26 +2017,31 @@ describe('IgxGrid Component Tests #grid', () => {
20172017
expect(grid.columns[2].field).toBe('lastName');
20182018
}));
20192019

2020-
it('should specify the correct aria-rowindex and aria-colindex attributes for cells', async () => {
2020+
it('should set correct aria attributes related to total rows/cols count and indexes', async () => {
20212021
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
20222022
fix.componentInstance.initColumnsRows(80, 20);
20232023
fix.detectChanges();
20242024
fix.detectChanges();
20252025

20262026
const grid = fix.componentInstance.grid;
2027+
const gridHeader = GridFunctions.getGridHeader(grid);
2028+
const headerRowElement = gridHeader.nativeElement.querySelector('[role="row"]');
20272029

20282030
grid.navigateTo(50, 16);
20292031
fix.detectChanges();
20302032
await wait();
20312033
fix.detectChanges();
20322034

2035+
expect(headerRowElement.getAttribute('aria-rowindex')).toBe('1');
2036+
expect(grid.nativeElement.getAttribute('aria-rowcount')).toBe('81');
2037+
expect(grid.nativeElement.getAttribute('aria-colcount')).toBe('20');
2038+
20332039
const cell = grid.gridAPI.get_cell_by_index(50, 'col16');
20342040
// The following attributes indicate to assistive technologies which portions
20352041
// of the content are displayed in case not all are rendered,
20362042
// such as with the built-in virtualization of the grid. 1-based index.
2037-
expect(cell.nativeElement.getAttribute('aria-rowindex')).toBe('51');
2043+
expect(cell.nativeElement.getAttribute('aria-rowindex')).toBe('52');
20382044
expect(cell.nativeElement.getAttribute('aria-colindex')).toBe('17');
2039-
20402045
});
20412046
});
20422047

0 commit comments

Comments
 (0)