diff --git a/projects/igniteui-angular-performance/src/app/app.routes.ts b/projects/igniteui-angular-performance/src/app/app.routes.ts
index 190c44ecdb4..5804152b12b 100644
--- a/projects/igniteui-angular-performance/src/app/app.routes.ts
+++ b/projects/igniteui-angular-performance/src/app/app.routes.ts
@@ -2,6 +2,7 @@ import { Routes } from '@angular/router';
import { GridComponent } from './grid/grid.component';
import { TreeGridComponent } from './tree-grid/tree-grid.component';
import { PivotGridComponent } from './pivot-grid/pivot-grid.component';
+import { HierarchicalGridComponent } from './hierarchical-grid/hierarchical-grid.component';
export const routes: Routes = [
{
@@ -45,6 +46,12 @@ export const routes: Routes = [
pathMatch: 'full',
component: GridComponent,
data: { rows: 1000 }
- }
+ },
+ {
+ path: "hierarchical-grid-100k",
+ title: "Hierarchical Grid 100k records",
+ component: HierarchicalGridComponent,
+ data: { rows: 100_000 }
+ },
];
diff --git a/projects/igniteui-angular-performance/src/app/hierarchical-grid/hierarchical-grid.component.html b/projects/igniteui-angular-performance/src/app/hierarchical-grid/hierarchical-grid.component.html
new file mode 100644
index 00000000000..7c186c0982c
--- /dev/null
+++ b/projects/igniteui-angular-performance/src/app/hierarchical-grid/hierarchical-grid.component.html
@@ -0,0 +1,35 @@
+
+
+ @for (col of columns; track col) {
+
+
+ }
+
+ @for (col of columns; track col) {
+
+
+ }
+
+
+
diff --git a/projects/igniteui-angular-performance/src/app/hierarchical-grid/hierarchical-grid.component.scss b/projects/igniteui-angular-performance/src/app/hierarchical-grid/hierarchical-grid.component.scss
new file mode 100644
index 00000000000..bc86bb4c9a3
--- /dev/null
+++ b/projects/igniteui-angular-performance/src/app/hierarchical-grid/hierarchical-grid.component.scss
@@ -0,0 +1,10 @@
+:host {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+}
+
+.grid-wrapper {
+ height: 100%;
+ width: 100%;
+}
diff --git a/projects/igniteui-angular-performance/src/app/hierarchical-grid/hierarchical-grid.component.ts b/projects/igniteui-angular-performance/src/app/hierarchical-grid/hierarchical-grid.component.ts
new file mode 100644
index 00000000000..a23f8a5bfa0
--- /dev/null
+++ b/projects/igniteui-angular-performance/src/app/hierarchical-grid/hierarchical-grid.component.ts
@@ -0,0 +1,39 @@
+import { Component, inject, ViewChild } from '@angular/core';
+import { GridColumnDataType, IgxColumnComponent, IgxHierarchicalGridComponent, IgxRowIslandComponent } from "igniteui-angular"
+import { DataService } from '../services/data.service';
+import { ActivatedRoute } from '@angular/router';
+
+@Component({
+ selector: 'app-hierarchical-grid',
+ imports: [IgxHierarchicalGridComponent, IgxColumnComponent, IgxRowIslandComponent],
+ templateUrl: './hierarchical-grid.component.html',
+ styleUrl: './hierarchical-grid.component.scss'
+})
+export class HierarchicalGridComponent {
+ protected columns: any[] = []
+ protected data: any[] = [];
+ protected performanceDataList: PerformanceEntryList = [];
+ private dataService = inject(DataService);
+ private activatedRoute = inject(ActivatedRoute);
+
+ @ViewChild(IgxHierarchicalGridComponent, { static: true })
+ public grid: IgxHierarchicalGridComponent;
+
+ constructor() {
+ this.data = this.dataService.generateHierarchicalData(this.activatedRoute.snapshot.data.rows)
+ this.columns = [
+ { field: "Id", dataType: GridColumnDataType.Number, sortable: true, width: 'auto', groupable: true },
+ { field: "Name", dataType: GridColumnDataType.String, sortable: true, width: 'auto', groupable: true },
+ { field: "AthleteNumber", dataType: GridColumnDataType.Number, sortable: true, width: 'auto', groupable: true },
+ { field: "Registered", dataType: GridColumnDataType.DateTime, sortable: true, width: 'auto', groupable: true },
+ { field: "CountryName", dataType: GridColumnDataType.String, sortable: true, width: 'auto', groupable: true },
+ { field: "FirstAppearance", dataType: GridColumnDataType.Time, sortable: true, width: 'auto', groupable: true },
+ { field: "CareerStart", dataType: GridColumnDataType.Date, sortable: true, width: 'auto', groupable: true },
+ { field: "Active", dataType: GridColumnDataType.Boolean, sortable: true, width: 'auto', groupable: true },
+ { field: "NetWorth", dataType: GridColumnDataType.Currency, sortable: true, width: 'auto', groupable: true },
+ { field: "CountryFlag", dataType: GridColumnDataType.Image, sortable: true, width: 'auto', groupable: true },
+ { field: "SuccessRate", dataType: GridColumnDataType.Percent, sortable: true, width: 'auto', groupable: true },
+ { field: "Position", dataType: GridColumnDataType.String, sortable: true, width: 'auto', groupable: true },
+ ];
+ }
+}
diff --git a/projects/igniteui-angular-performance/src/app/services/data.service.ts b/projects/igniteui-angular-performance/src/app/services/data.service.ts
index 2cda3cbf715..b598d85a9cb 100644
--- a/projects/igniteui-angular-performance/src/app/services/data.service.ts
+++ b/projects/igniteui-angular-performance/src/app/services/data.service.ts
@@ -16,6 +16,12 @@ export class DataService {
return data;
}
+ public generateHierarchicalData(rows: number): any[] {
+ const rnd = new Mulberry32(1234);
+ const data = this.generateAthletesData(rnd, rows, true);
+ return data;
+ }
+
public generateTreeData(rows: number): any[] {
const rnd = new Mulberry32(1234);
const data = this.generateEmployeesData(rnd, rows);
@@ -114,7 +120,7 @@ export class DataService {
return currData;
}
- private generateAthletesData(rnd: Mulberry32, rows: number): any[] {
+ private generateAthletesData(rnd: Mulberry32, rows: number, children = false): any[] {
const currData = [];
for (let i = 0; i < rows; i++) {
const rand = Math.floor(rnd.random() * Math.floor(athletesData.length));
@@ -125,6 +131,10 @@ export class DataService {
dataObj["Active"] = this.randomizeBoolean(rnd);
dataObj["SuccessRate"] = this.randomizePercentage(rnd);
dataObj["AthleteNumber"] = this.randomizeAthleteNumber(dataObj["AthleteNumber"], rnd);
+ if (children) {
+ const rnd = new Mulberry32(i);
+ dataObj["childData"] = this.generateAthletesData(rnd, 5);
+ }
currData.push(dataObj);
}
return currData;
diff --git a/projects/igniteui-angular/directives/src/directives/for-of/for_of.directive.ts b/projects/igniteui-angular/directives/src/directives/for-of/for_of.directive.ts
index b45dce222ba..65c05e41d19 100644
--- a/projects/igniteui-angular/directives/src/directives/for-of/for_of.directive.ts
+++ b/projects/igniteui-angular/directives/src/directives/for-of/for_of.directive.ts
@@ -1,5 +1,5 @@
import { NgForOfContext } from '@angular/common';
-import { ChangeDetectorRef, ComponentRef, Directive, EmbeddedViewRef, EventEmitter, Input, IterableChanges, IterableDiffer, IterableDiffers, NgZone, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, TemplateRef, TrackByFunction, ViewContainerRef, AfterViewInit, booleanAttribute, DOCUMENT, inject, afterNextRender, runInInjectionContext, EnvironmentInjector } from '@angular/core';
+import { ChangeDetectorRef, ComponentRef, Directive, EmbeddedViewRef, EventEmitter, Input, IterableChanges, IterableDiffer, IterableDiffers, NgZone, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, TemplateRef, TrackByFunction, ViewContainerRef, booleanAttribute, DOCUMENT, inject, afterNextRender, runInInjectionContext, EnvironmentInjector, AfterViewInit } from '@angular/core';
import { DisplayContainerComponent } from './display.container';
import { HVirtualHelperComponent } from './horizontal.virtual.helper.component';
@@ -85,7 +85,7 @@ export abstract class IgxForOfToken {
],
standalone: true
})
-export class IgxForOfDirective extends IgxForOfToken implements OnInit, OnChanges, OnDestroy, AfterViewInit {
+export class IgxForOfDirective extends IgxForOfToken implements OnInit, AfterViewInit, OnChanges, OnDestroy {
private _viewContainer = inject(ViewContainerRef);
protected _template = inject>>(TemplateRef);
protected _differs = inject(IterableDiffers);
@@ -96,6 +96,7 @@ export class IgxForOfDirective extends IgxForOfToken, number>();
/**
* Sets the data to be rendered.
@@ -282,6 +283,8 @@ export class IgxForOfDirective extends IgxForOfToken> = [];
protected contentResizeNotify = new Subject();
protected contentObserver: ResizeObserver;
+ protected viewObserver: ResizeObserver;
+ protected viewResizeNotify = new Subject();
/** Size that is being virtualized. */
protected _virtSize = 0;
/**
@@ -397,10 +400,10 @@ export class IgxForOfDirective extends IgxForOfToken= because `scrollTop + container size` can't be bigger than `scrollHeight`, unless something isn't updated.
// Also use Math.round because Chrome has some inconsistencies and `scrollTop + container` can be float when zooming the page.
- return Math.round(this.getScroll().scrollTop + this.igxForContainerSize) === scrollHeight;
+ return Math.round(this.scrollComponent.scrollAmount + this.igxForContainerSize) === scrollHeight;
}
private get _isAtBottomIndex() {
@@ -415,7 +418,7 @@ export class IgxForOfDirective extends IgxForOfToken parseInt(this.igxForContainerSize, 10);
}
- private get embeddedViewNodes() {
+ protected get embeddedViewNodes() {
const result = new Array(this._embeddedViews.length);
for (let i = 0; i < this._embeddedViews.length; i++) {
const view = this._embeddedViews[i];
@@ -517,6 +520,16 @@ export class IgxForOfDirective extends IgxForOfToken {
+ if (this.platformUtil.isBrowser) {
+ this.viewObserver.observe(target);
+ }
+ });
+ }
+ }
+
/**
* @hidden
*/
@@ -527,6 +540,10 @@ export class IgxForOfDirective extends IgxForOfToken extends IgxForOfToken 5;
}
+ protected getNodeSize(rNode: Element, _index: number): number {
+ const dimension = this.igxForScrollOrientation === 'horizontal' ?
+ this.igxForSizePropName : 'height';
+ const nodeSize = dimension === 'height' ?
+ rNode.clientHeight + this.getMargin(rNode, dimension):
+ rNode.clientWidth + this.getMargin(rNode, dimension);
+ return nodeSize;
+ }
+
+
/**
* @hidden
* Function that recalculates and updates cache sizes.
*/
- public recalcUpdateSizes() {
- const dimension = this.igxForScrollOrientation === 'horizontal' ?
- this.igxForSizePropName : 'height';
+ public recalcUpdateSizes(prevState?: IForOfState) {
+ if (prevState && prevState.startIndex === this.state.startIndex && prevState.chunkSize === this.state.chunkSize) {
+ // nothing changed
+ return;
+ }
+
const diffs = [];
let totalDiff = 0;
- const l = this._embeddedViews.length;
- const rNodes = this.embeddedViewNodes;
- for (let i = 0; i < l; i++) {
- const rNode = rNodes[i];
- if (rNode) {
- const height = window.getComputedStyle(rNode).getPropertyValue('height');
- const h = parseFloat(height) || parseInt(this.igxForItemSize, 10);
- const index = this.state.startIndex + i;
- if (!this.isRemote && !this.igxForOf[index]) {
- continue;
- }
- const margin = this.getMargin(rNode, dimension);
- const oldVal = this.individualSizeCache[index];
- const newVal = (dimension === 'height' ? h : rNode.clientWidth) + margin;
- this.individualSizeCache[index] = newVal;
- const currDiff = newVal - oldVal;
- diffs.push(currDiff);
- totalDiff += currDiff;
- this.sizesCache[index + 1] = (this.sizesCache[index] || 0) + newVal;
- }
+ const nodes = this.embeddedViewNodes;
+ for (let index = 0; index < this._embeddedViews.length; index++) {
+ const targetIndex = this.state.startIndex + index;
+ const nodeSize = this.getNodeSize(nodes[index], index);
+ const oldVal = this.individualSizeCache[targetIndex];
+ const currDiff = nodeSize - oldVal;
+ diffs.push(currDiff);
+ totalDiff += currDiff;
+ this.individualSizeCache[targetIndex] = nodeSize;
+ this.sizesCache[targetIndex + 1] = (this.sizesCache[targetIndex] || 0) + nodeSize;
}
// update cache
if (Math.abs(totalDiff) > 0) {
@@ -866,12 +886,12 @@ export class IgxForOfDirective extends IgxForOfToken acc + val;
- const hSum = this.individualSizeCache.reduce(reducer);
- if (hSum > this._maxSize) {
- this._virtRatio = hSum / this._maxSize;
+ this._virtSize += totalDiff;
+ if (this._virtSize > this._maxSize) {
+ this._virtRatio = this._virtSize / this._maxSize;
}
this.scrollComponent.size = Math.min(this.scrollComponent.size + totalDiff, this._maxSize);
- this._virtSize = hSum;
+
if (!this.scrollComponent.destroyed) {
this.scrollComponent.cdr.detectChanges();
}
@@ -980,6 +1000,18 @@ export class IgxForOfDirective extends IgxForOfToken extends IgxForOfToken node.nodeType === Node.ELEMENT_NODE) || oldElem.rootNodes[0].nextElementSibling);
// also detach from ViewContainerRef to make absolutely sure this is removed from the view container.
this.dc.instance._vcr.detach(this.dc.instance._vcr.length - 1);
oldElem.destroy();
+ this._embeddedViewSizesCache.delete(oldElem);
this.state.chunkSize--;
}
@@ -1510,7 +1544,6 @@ export class IgxForOfDirective extends IgxForOfToken 0 && this.scrollPosition > 0) {
- this.recalcUpdateSizes();
const offset = this.igxForScrollOrientation === 'horizontal' ?
parseInt(this.dc.instance._viewContainer.element.nativeElement.style.left, 10) :
Number(this.dc.instance._viewContainer.element.nativeElement.style.transform?.match(/translateY\((-?\d+\.?\d*)px\)/)?.[1]);
@@ -1522,7 +1555,7 @@ export class IgxForOfDirective extends IgxForOfToken extends IgxForOfDirec
return this.igxForSizePropName || 'height';
}
- public override recalcUpdateSizes() {
+ public override recalcUpdateSizes(prevState?: IForOfState) {
if (this.igxGridForOfVariableSizes && this.igxForScrollOrientation === 'vertical') {
- super.recalcUpdateSizes();
+ super.recalcUpdateSizes(prevState);
}
}
@@ -1638,6 +1671,12 @@ export class IgxGridForOfDirective extends IgxForOfDirec
this.syncService.setMaster(this);
super.ngOnInit();
this.removeScrollEventListeners();
+ const destructor = takeUntil(this.destroy$);
+ this.viewObserver = new (getResizeObserver())((entries: ResizeObserverEntry[]) => this.viewResizeNotify.next(entries));
+ this.viewResizeNotify.pipe(
+ filter(() => this.igxForContainerSize && this.igxForOf && this.igxForOf.length > 0),
+ destructor
+ ).subscribe((entries: ResizeObserverEntry[]) => this._zone.runTask(() => this.updateViewSizes(entries)));
}
public override ngOnChanges(changes: SimpleChanges) {
@@ -1725,12 +1764,13 @@ export class IgxGridForOfDirective extends IgxForOfDirec
} else {
this._bScrollInternal = false;
}
+ const prevState = Object.assign({}, this.state);
const scrollOffset = this.fixedUpdateAllElements(this._virtScrollPosition);
runInInjectionContext(this._injector, () => {
afterNextRender({
write: () => {
this.dc.instance._viewContainer.element.nativeElement.style.transform = `translateY(${-scrollOffset}px)`;
- this._zone.onStable.pipe(first()).subscribe(this.recalcUpdateSizes.bind(this));
+ this._zone.onStable.pipe(first()).subscribe(this.recalcUpdateSizes.bind(this, prevState));
}
});
});
@@ -1793,6 +1833,15 @@ export class IgxGridForOfDirective extends IgxForOfDirec
return totalSize;
}
+ protected override getNodeSize(rNode: Element, index?: number): number {
+ if (this.igxForScrollOrientation === 'vertical') {
+ const view = this._embeddedViews[index];
+ return this._embeddedViewSizesCache.get(view) || parseInt(this.igxForItemSize, 10);
+ } else {
+ return super.getNodeSize(rNode, index);
+ }
+ }
+
protected override _updateSizeCache(changes: IterableChanges = null) {
const oldSize = this.individualSizeCache.length > 0 ? this.individualSizeCache.reduce((acc, val) => acc + val) : 0;
let newSize = oldSize;
@@ -1862,6 +1911,7 @@ export class IgxGridForOfDirective extends IgxForOfDirec
);
this._embeddedViews.push(embeddedView);
+ this.subscribeToViewObserver(embeddedView.rootNodes.find(node => node.nodeType === Node.ELEMENT_NODE) || embeddedView.rootNodes[0].nextElementSibling);
this.state.chunkSize++;
}
diff --git a/projects/igniteui-angular/grids/grid/src/cell-merge.spec.ts b/projects/igniteui-angular/grids/grid/src/cell-merge.spec.ts
index 918975267a2..d4bea0e8436 100644
--- a/projects/igniteui-angular/grids/grid/src/cell-merge.spec.ts
+++ b/projects/igniteui-angular/grids/grid/src/cell-merge.spec.ts
@@ -186,11 +186,12 @@ describe('IgxGrid - Cell merging #grid', () => {
hasClass(mergedCell, 'igx-grid__td--merged-hovered', true);
});
- it('should set correct size to merged cell that spans multiple rows that have different sizes.', () => {
+ it('should set correct size to merged cell that spans multiple rows that have different sizes.', async() => {
const col = grid.getColumnByName('ID');
col.bodyTemplate = fix.componentInstance.customTemplate;
fix.detectChanges();
- grid.verticalScrollContainer.recalcUpdateSizes();
+ await wait(100);
+ fix.detectChanges();
grid.dataRowList.toArray().forEach(x => x.cdr.detectChanges());
const mergedCell = fix.debugElement.queryAll(By.css(MERGE_CELL_CSS_CLASS))[0].nativeNode;
// one row is 100px, other is 200, 2px border
diff --git a/projects/igniteui-angular/grids/grid/src/cell.spec.ts b/projects/igniteui-angular/grids/grid/src/cell.spec.ts
index 95d97bc6142..63143376997 100644
--- a/projects/igniteui-angular/grids/grid/src/cell.spec.ts
+++ b/projects/igniteui-angular/grids/grid/src/cell.spec.ts
@@ -156,7 +156,6 @@ describe('IgxGrid - Cell component #grid', () => {
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [NoopAnimationsModule, VirtualGridComponent],
- providers: [{ provide: NgZone, useFactory: () => new TestNgZone() }]
}).compileComponents();
}));
@@ -185,14 +184,18 @@ describe('IgxGrid - Cell component #grid', () => {
it('should fit last cell in the available display container when there is vertical and horizontal scroll.', (async () => {
fix.componentInstance.columns = fix.componentInstance.generateCols(100);
fix.componentInstance.data = fix.componentInstance.generateData(1000);
- await wait();
+ fix.detectChanges();
+ await wait(16);
fix.detectChanges();
const firsCell = GridFunctions.getRowCells(fix, 1)[0];
expect(GridFunctions.getValueFromCellElement(firsCell)).toEqual('0');
- fix.componentInstance.scrollLeft(999999);
- await wait();
+ const scrollbar = grid.headerContainer.getScroll();
+ scrollbar.scrollLeft = 999999;
+
+ await wait(16);
+
// This won't work always in debugging mode due to the angular native events behavior, so errors are expected
fix.detectChanges();
const cells = GridFunctions.getRowCells(fix, 1);
@@ -238,7 +241,7 @@ describe('IgxGrid - Cell component #grid', () => {
const scrollbar = grid.headerContainer.getScroll();
scrollbar.scrollLeft = 10000;
fix.detectChanges();
- await wait();
+ await wait(16);
const lastColumnCells = grid.columnList.get(grid.columnList.length - 1).cells;
fix.detectChanges();
lastColumnCells.forEach((item) => {
diff --git a/projects/igniteui-angular/grids/grid/src/grid-summary.spec.ts b/projects/igniteui-angular/grids/grid/src/grid-summary.spec.ts
index 923ab8e26fd..6fb1b4c5d1f 100644
--- a/projects/igniteui-angular/grids/grid/src/grid-summary.spec.ts
+++ b/projects/igniteui-angular/grids/grid/src/grid-summary.spec.ts
@@ -18,6 +18,7 @@ import { DropPosition, IgxColumnComponent, IgxDateSummaryOperand, IgxGridRow, Ig
import { DatePipe } from '@angular/common';
import { IgxGridGroupByRowComponent } from './groupby-row.component';
import { GridSummaryCalculationMode, IColumnPipeArgs, IgxNumberFilteringOperand, IgxStringFilteringOperand, IgxSummaryResult, SortingDirection } from 'igniteui-angular/core';
+import { SCROLL_THROTTLE_TIME_MULTIPLIER } from './../../grid/src/grid-base.directive';
describe('IgxGrid - Summaries #grid', () => {
@@ -1204,6 +1205,9 @@ describe('IgxGrid - Summaries #grid', () => {
let fix;
let grid;
beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [{ provide: SCROLL_THROTTLE_TIME_MULTIPLIER, useValue: 0 }]
+ });
fix = TestBed.createComponent(SummariesGroupByTransactionsComponent);
fix.detectChanges();
grid = fix.componentInstance.grid;
@@ -1497,6 +1501,8 @@ describe('IgxGrid - Summaries #grid', () => {
fieldName: 'ParentID', dir: SortingDirection.Asc, ignoreCase: false
});
fix.detectChanges();
+ await wait(60);
+
const newRow = {
ID: 777,
@@ -1508,6 +1514,7 @@ describe('IgxGrid - Summaries #grid', () => {
};
grid.addRow(newRow);
fix.detectChanges();
+ await wait(60);
const summaryRow = GridSummaryFunctions.getRootSummaryRow(fix);
GridSummaryFunctions.verifyColumnSummaries(summaryRow, 2, ['Count'], ['9']);
@@ -1516,7 +1523,7 @@ describe('IgxGrid - Summaries #grid', () => {
GridSummaryFunctions.verifyColumnSummaries(summaryRow, 4, ['Min', 'Max'], ['19', '50']);
grid.verticalScrollContainer.scrollTo(grid.dataView.length - 1);
- await wait(50);
+ await wait(60);
fix.detectChanges();
let row = grid.gridAPI.get_row_by_index(16);
@@ -1528,7 +1535,7 @@ describe('IgxGrid - Summaries #grid', () => {
// Undo transactions
grid.transactions.undo();
- await wait(50);
+ await wait(60);
fix.detectChanges();
row = grid.gridAPI.get_row_by_index(16);
expect(row).toBeUndefined();
@@ -1537,7 +1544,7 @@ describe('IgxGrid - Summaries #grid', () => {
// redo transactions
grid.transactions.redo();
- await wait(50);
+ await wait(60);
fix.detectChanges();
row = grid.gridAPI.get_row_by_index(16);
@@ -1546,13 +1553,13 @@ describe('IgxGrid - Summaries #grid', () => {
GridSummaryFunctions.verifyColumnSummariesBySummaryRowIndex(fix, 0, 5, ['Count'], ['9']);
grid.verticalScrollContainer.scrollTo(grid.dataView.length - 1);
- await wait(50);
+ await wait(60);
fix.detectChanges();
GridSummaryFunctions.verifyColumnSummariesBySummaryRowIndex(fix, 18, 5, ['Count'], ['1']);
// Discard
grid.transactions.clear();
- await wait(50);
+ await wait(60);
fix.detectChanges();
row = grid.gridAPI.get_row_by_index(16);
diff --git a/projects/igniteui-angular/grids/grid/src/grid.component.html b/projects/igniteui-angular/grids/grid/src/grid.component.html
index 9e96d118d99..9efef0a7b42 100644
--- a/projects/igniteui-angular/grids/grid/src/grid.component.html
+++ b/projects/igniteui-angular/grids/grid/src/grid.component.html
@@ -104,6 +104,7 @@
[igxForItemSize]="hasColumnLayouts ? rowHeight * multiRowLayoutRowSize + 1 : renderedRowHeight"
[igxForTrackBy]="trackChanges"
#verticalScrollContainer (chunkPreload)="dataLoading($event)" (dataChanging)="dataRebinding($event)" (dataChanged)="dataRebound($event)">
+
+
diff --git a/projects/igniteui-angular/grids/grid/src/grid.component.spec.ts b/projects/igniteui-angular/grids/grid/src/grid.component.spec.ts
index 266a2cc9325..c0fbeb117c1 100644
--- a/projects/igniteui-angular/grids/grid/src/grid.component.spec.ts
+++ b/projects/igniteui-angular/grids/grid/src/grid.component.spec.ts
@@ -2029,7 +2029,7 @@ describe('IgxGrid Component Tests #grid', () => {
grid.navigateTo(50, 16);
fix.detectChanges();
- await wait(60);
+ await wait(100);
fix.detectChanges();
expect(headerRowElement.getAttribute('aria-rowindex')).toBe('1');
diff --git a/projects/igniteui-angular/grids/grid/src/grid.groupby.spec.ts b/projects/igniteui-angular/grids/grid/src/grid.groupby.spec.ts
index 0087c32f4db..04c4918e841 100644
--- a/projects/igniteui-angular/grids/grid/src/grid.groupby.spec.ts
+++ b/projects/igniteui-angular/grids/grid/src/grid.groupby.spec.ts
@@ -167,11 +167,11 @@ describe('IgxGrid - GroupBy #grid', () => {
expect(groupRows.length).toEqual(4);
expect(dataRows.length).toEqual(8);
- const expectedValue1 = groupRows[1].nativeElement.nextElementSibling.querySelectorAll('igx-grid-cell')[3].textContent;
+ const expectedValue1 = groupRows[1].nativeElement.parentElement.nextElementSibling.querySelectorAll('igx-grid-cell')[3].textContent;
const actualValue1 = groupRows[1].element.nativeElement.querySelector('.igx-group-label__text').textContent;
- const expectedValue2 = groupRows[2].nativeElement.nextElementSibling.querySelectorAll('igx-grid-cell')[3].textContent;
+ const expectedValue2 = groupRows[2].nativeElement.parentElement.nextElementSibling.querySelectorAll('igx-grid-cell')[3].textContent;
const actualValue2 = groupRows[2].element.nativeElement.querySelector('.igx-group-label__text').textContent;
- const expectedValue3 = groupRows[3].nativeElement.nextElementSibling.querySelectorAll('igx-grid-cell')[3].textContent;
+ const expectedValue3 = groupRows[3].nativeElement.parentElement.nextElementSibling.querySelectorAll('igx-grid-cell')[3].textContent;
const actualValue3 = groupRows[3].element.nativeElement.querySelector('.igx-group-label__text').textContent;
expect(actualValue1).toEqual(expectedValue1);
@@ -300,7 +300,7 @@ describe('IgxGrid - GroupBy #grid', () => {
fix.detectChanges();
const groupRows = grid.groupsRowList.toArray();
- const expectedValue1 = groupRows[0].nativeElement.nextElementSibling.querySelectorAll('igx-grid-cell')[3].textContent;
+ const expectedValue1 = groupRows[0].nativeElement.parentElement.nextElementSibling.querySelectorAll('igx-grid-cell')[3].textContent;
const actualValue1 = groupRows[0].element.nativeElement.querySelector('.igx-group-label__text').textContent;
expect(expectedValue1).toEqual(actualValue1);
}));
@@ -317,7 +317,7 @@ describe('IgxGrid - GroupBy #grid', () => {
fix.detectChanges();
const groupRows = grid.groupsRowList.toArray();
- const expectedValue1 = groupRows[0].nativeElement.nextElementSibling.querySelectorAll('igx-grid-cell')[4].textContent;
+ const expectedValue1 = groupRows[0].nativeElement.parentElement.nextElementSibling.querySelectorAll('igx-grid-cell')[4].textContent;
const actualValue1 = groupRows[0].element.nativeElement.querySelector('.igx-group-label__text').textContent;
expect(expectedValue1).toEqual(actualValue1);
}));
diff --git a/projects/igniteui-angular/grids/grid/src/grid.master-detail.spec.ts b/projects/igniteui-angular/grids/grid/src/grid.master-detail.spec.ts
index 5a6d88b3f87..6bc40676064 100644
--- a/projects/igniteui-angular/grids/grid/src/grid.master-detail.spec.ts
+++ b/projects/igniteui-angular/grids/grid/src/grid.master-detail.spec.ts
@@ -479,7 +479,7 @@ describe('IgxGrid Master Detail #grid', () => {
await wait(DEBOUNCE_TIME);
fix.detectChanges();
- const detailRow = row.nativeElement.previousElementSibling as HTMLElement;
+ const detailRow = row.nativeElement.parentElement.previousElementSibling.children[0] as HTMLElement;
GridFunctions.verifyMasterDetailRowFocused(detailRow);
expect(GridFunctions.elementInGridView(grid, detailRow)).toBeTruthy();
});
@@ -499,7 +499,7 @@ describe('IgxGrid Master Detail #grid', () => {
fix.detectChanges();
row = grid.gridAPI.get_row_by_index(2);
- const detailRow = row.nativeElement.previousElementSibling as HTMLElement;
+ const detailRow = row.nativeElement.parentElement.previousElementSibling.children[0] as HTMLElement;
GridFunctions.verifyMasterDetailRowFocused(detailRow);
expect(GridFunctions.elementInGridView(grid, detailRow)).toBeTruthy();
});
@@ -1204,7 +1204,7 @@ describe('IgxGrid Master Detail #grid', () => {
await wait();
fix.detectChanges();
- const allRows = grid.tbody.nativeElement.firstElementChild.children;
+ const allRows = [...grid.tbody.nativeElement.firstElementChild.children].map(x=> x.children[0]);
expect(allRows.length).toBe(8);
expect(allRows[0].tagName.toLowerCase()).toBe(GROUP_ROW_TAG);
expect(allRows[1].tagName.toLowerCase()).toBe(ROW_TAG);
@@ -1227,7 +1227,7 @@ describe('IgxGrid Master Detail #grid', () => {
grid.summaryPosition = GridSummaryPosition.top;
fix.detectChanges();
- const allRows = grid.tbody.nativeElement.firstElementChild.children;
+ const allRows = [...grid.tbody.nativeElement.firstElementChild.children].map(x=> x.children[0]);
expect(allRows.length).toBe(8);
expect(allRows[0].tagName.toLowerCase()).toBe(GROUP_ROW_TAG);
expect(allRows[1].tagName.toLowerCase()).toBe(SUMMARY_ROW_TAG);
@@ -1245,7 +1245,7 @@ describe('IgxGrid Master Detail #grid', () => {
after grouping by and detail views for the group rows are collapsed.`, () => {
grid.summaryPosition = GridSummaryPosition.top;
fix.detectChanges();
- const allRows = grid.tbody.nativeElement.firstElementChild.children;
+ const allRows = [...grid.tbody.nativeElement.firstElementChild.children].map(x=> x.children[0]);
expect(allRows.length).toBe(9);
expect(allRows[0].tagName.toLowerCase()).toBe(GROUP_ROW_TAG);
expect(allRows[1].tagName.toLowerCase()).toBe(SUMMARY_ROW_TAG);
@@ -1260,7 +1260,7 @@ describe('IgxGrid Master Detail #grid', () => {
it(`Should correctly position summary rows when summary
row position is bottom after grouping by and detail views for the group rows are collapsed.`, () => {
- const allRows = grid.tbody.nativeElement.firstElementChild.children;
+ const allRows = [...grid.tbody.nativeElement.firstElementChild.children].map(x=> x.children[0]);
expect(allRows.length).toBe(9);
expect(allRows[0].tagName.toLowerCase()).toBe(GROUP_ROW_TAG);
expect(allRows[1].tagName.toLowerCase()).toBe(ROW_TAG);
diff --git a/projects/igniteui-angular/grids/grid/src/grid.multi-row-layout.integration.spec.ts b/projects/igniteui-angular/grids/grid/src/grid.multi-row-layout.integration.spec.ts
index 12692d5eda7..018f54e75fa 100644
--- a/projects/igniteui-angular/grids/grid/src/grid.multi-row-layout.integration.spec.ts
+++ b/projects/igniteui-angular/grids/grid/src/grid.multi-row-layout.integration.spec.ts
@@ -850,6 +850,7 @@ describe('IgxGrid - multi-row-layout Integration #grid - ', () => {
strategy: DefaultSortingStrategy.instance()
});
fixture.detectChanges();
+ await wait(16);
expect(grid.rowList.length).toEqual(8);
expect((grid.verticalScrollContainer.getScroll().children[0] as HTMLElement).offsetHeight -
diff --git a/projects/igniteui-angular/grids/hierarchical-grid/src/hierarchical-grid.component.html b/projects/igniteui-angular/grids/hierarchical-grid/src/hierarchical-grid.component.html
index e7683ef7c9b..5761a4e041a 100644
--- a/projects/igniteui-angular/grids/hierarchical-grid/src/hierarchical-grid.component.html
+++ b/projects/igniteui-angular/grids/hierarchical-grid/src/hierarchical-grid.component.html
@@ -82,11 +82,13 @@
[igxForScrollOrientation]="'vertical'" [igxForScrollContainer]="verticalScroll"
[igxForContainerSize]="calcHeight" [igxForItemSize]="renderedRowHeight" [igxForTrackBy]="trackChanges"
#verticalScrollContainer (chunkPreload)="dataLoading($event)" (dataChanging)="dataRebinding($event)" (dataChanged)="dataRebound($event)">
+
+
diff --git a/projects/igniteui-angular/grids/hierarchical-grid/src/hierarchical-grid.navigation.spec.ts b/projects/igniteui-angular/grids/hierarchical-grid/src/hierarchical-grid.navigation.spec.ts
index bdf059de178..c96232939e5 100644
--- a/projects/igniteui-angular/grids/hierarchical-grid/src/hierarchical-grid.navigation.spec.ts
+++ b/projects/igniteui-angular/grids/hierarchical-grid/src/hierarchical-grid.navigation.spec.ts
@@ -172,13 +172,13 @@ describe('IgxHierarchicalGrid Navigation', () => {
it('should allow navigating to start in child grid when child grid target row moves outside the parent view port.', async () => {
hierarchicalGrid.verticalScrollContainer.scrollTo(2);
fixture.detectChanges();
- await wait();
+ await wait(DEBOUNCE_TIME);
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const horizontalScrDir = childGrid.dataRowList.toArray()[0].virtDirRow;
horizontalScrDir.scrollTo(6);
fixture.detectChanges();
- await wait();
+ await wait(DEBOUNCE_TIME);
const childLastCell = childGrid.dataRowList.toArray()[9].cells.toArray()[3];
GridFunctions.focusCell(fixture, childLastCell);
@@ -979,7 +979,7 @@ describe('IgxHierarchicalGrid Navigation', () => {
clearGridSubs();
});
- it('should navigate to exact child grid with navigateToChildGrid.', (done) => {
+ it('should navigate to exact child grid with navigateToChildGrid.', async() => {
hierarchicalGrid.primaryKey = 'ID';
hierarchicalGrid.expandChildren = false;
fixture.detectChanges();
@@ -988,23 +988,25 @@ describe('IgxHierarchicalGrid Navigation', () => {
rowIslandKey: 'childData2',
rowID: 10
};
- hierarchicalGrid.navigation.navigateToChildGrid([path], () => {
- fixture.detectChanges();
- const childGrid = hierarchicalGrid.gridAPI.getChildGrid([path]).nativeElement;
- expect(childGrid).not.toBe(undefined);
-
- const parentBottom = hierarchicalGrid.tbody.nativeElement.getBoundingClientRect().bottom;
- const parentTop = hierarchicalGrid.tbody.nativeElement.getBoundingClientRect().top;
- // check it's in view within its parent
- expect(childGrid.getBoundingClientRect().bottom <= parentBottom && childGrid.getBoundingClientRect().top >= parentTop);
- done();
- });
+ await wait(16);
+ hierarchicalGrid.navigation.navigateToChildGrid([path]);
+ await wait(DEBOUNCE_TIME);
+ fixture.detectChanges();
+ const childGrid = hierarchicalGrid.gridAPI.getChildGrid([path]).nativeElement;
+ expect(childGrid).not.toBe(undefined);
+
+ const parentBottom = hierarchicalGrid.tbody.nativeElement.getBoundingClientRect().bottom;
+ const parentTop = hierarchicalGrid.tbody.nativeElement.getBoundingClientRect().top;
+ // check it's in view within its parent
+ expect(childGrid.getBoundingClientRect().bottom <= parentBottom && childGrid.getBoundingClientRect().top >= parentTop);
});
- it('should navigate to exact nested child grid with navigateToChildGrid.', (done) => {
+ it('should navigate to exact nested child grid with navigateToChildGrid.', async() => {
hierarchicalGrid.expandChildren = false;
+ await wait(DEBOUNCE_TIME);
hierarchicalGrid.primaryKey = 'ID';
hierarchicalGrid.childLayoutList.toArray()[0].primaryKey = 'ID';
fixture.detectChanges();
+ await wait(DEBOUNCE_TIME);
const targetRoot: IPathSegment = {
rowKey: 10,
rowIslandKey: 'childData',
@@ -1016,19 +1018,18 @@ describe('IgxHierarchicalGrid Navigation', () => {
rowID: 5
};
- hierarchicalGrid.navigation.navigateToChildGrid([targetRoot, targetNested], () => {
+ hierarchicalGrid.navigation.navigateToChildGrid([targetRoot, targetNested]);
+ await wait(DEBOUNCE_TIME * 2);
fixture.detectChanges();
- const childGrid = hierarchicalGrid.gridAPI.getChildGrid([targetRoot]).nativeElement;
+ const childGrid = hierarchicalGrid.gridAPI.getChildGrid([targetRoot]).nativeElement;
expect(childGrid).not.toBe(undefined);
- const childGridNested = hierarchicalGrid.gridAPI.getChildGrid([targetRoot, targetNested]).nativeElement;
+ const childGridNested = hierarchicalGrid.gridAPI.getChildGrid([targetRoot, targetNested]).nativeElement;
expect(childGridNested).not.toBe(undefined);
const parentBottom = childGrid.getBoundingClientRect().bottom;
const parentTop = childGrid.getBoundingClientRect().top;
// check it's in view within its parent
expect(childGridNested.getBoundingClientRect().bottom <= parentBottom && childGridNested.getBoundingClientRect().top >= parentTop);
- done();
- });
});
});
});
diff --git a/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.component.html b/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.component.html
index 558cf1c4721..6833e93d665 100644
--- a/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.component.html
+++ b/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.component.html
@@ -44,11 +44,13 @@
[igxForItemSize]="hasColumnLayouts ? rowHeight * multiRowLayoutRowSize + 1 : renderedRowHeight"
[igxGridForOfVariableSizes]="false"
#verticalScrollContainer (dataChanging)="dataRebinding($event)" (dataChanged)="dataRebound($event)">
+
+
{
it('Should not change active summary cell when press Arrow Down and it is last summary row', async () => {
treeGrid.expandAll();
fix.detectChanges();
+ await wait(16);
treeGrid.verticalScrollContainer.scrollTo(treeGrid.dataView.length - 1);
await wait(100);
diff --git a/projects/igniteui-angular/grids/tree-grid/src/tree-grid.component.html b/projects/igniteui-angular/grids/tree-grid/src/tree-grid.component.html
index bc331c85ae6..1c5fa054b0c 100644
--- a/projects/igniteui-angular/grids/tree-grid/src/tree-grid.component.html
+++ b/projects/igniteui-angular/grids/tree-grid/src/tree-grid.component.html
@@ -88,11 +88,13 @@
let-rowIndex="index" [igxForScrollOrientation]="'vertical'" [igxForScrollContainer]='verticalScroll'
[igxForContainerSize]='calcHeight' [igxForItemSize]="renderedRowHeight" #verticalScrollContainer
(dataChanging)="dataRebinding($event)" (dataChanged)="dataRebound($event)">
+
+
diff --git a/projects/igniteui-angular/test-utils/grid-functions.spec.ts b/projects/igniteui-angular/test-utils/grid-functions.spec.ts
index a46159be685..bf0b8125a4d 100644
--- a/projects/igniteui-angular/test-utils/grid-functions.spec.ts
+++ b/projects/igniteui-angular/test-utils/grid-functions.spec.ts
@@ -208,7 +208,7 @@ export class GridFunctions {
}
public static getMasterRowDetail(row) {
- const nextSibling = row.element.nativeElement.nextElementSibling;
+ const nextSibling = row.element.nativeElement.parentElement.nextElementSibling.children[0];
if (nextSibling &&
nextSibling.tagName.toLowerCase() === 'div' &&
nextSibling.getAttribute('detail') === 'true') {
@@ -252,8 +252,8 @@ export class GridFunctions {
public static getMasterRowDetailDebug(fix: ComponentFixture, row: IgxRowDirective) {
const rowDE = fix.debugElement.queryAll(By.directive(IgxRowDirective)).find(el => el.componentInstance === row);
- const detailDE = rowDE.parent.children
- .find(el => el.attributes['detail'] === 'true' && el.attributes['data-rowindex'] === row.index + 1 + '');
+ const detailDE = rowDE.parent.parent.children
+ .find(el => el.children[0].attributes['detail'] === 'true' && el.children[0].attributes['data-rowindex'] === row.index + 1 + '');
return detailDE;
}