igniteui-angular-sovn
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
3,341 lines • 155 kB
text/typescript
import {
AfterViewInit, ChangeDetectorRef, Component, Injectable,
OnInit, ViewChild, TemplateRef
} from '@angular/core';
import { TestBed, fakeAsync, tick, flush, waitForAsync } from '@angular/core/testing';
import { BehaviorSubject, Observable } from 'rxjs';
import { By } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { IgxGridComponent } from './grid.component';
import { IgxColumnComponent } from '../columns/column.component';
import { IForOfState } from '../../directives/for-of/for_of.directive';
import { DisplayDensity } from '../../core/density';
import { GridColumnDataType } from '../../data-operations/data-util';
import { GridTemplateStrings } from '../../test-utils/template-strings.spec';
import { SampleTestData } from '../../test-utils/sample-test-data.spec';
import { BasicGridComponent } from '../../test-utils/grid-base-components.spec';
import { UIInteractions, wait } from '../../test-utils/ui-interactions.spec';
import { IgxStringFilteringOperand, IgxNumberFilteringOperand } from '../../data-operations/filtering-condition';
import { configureTestSuite } from '../../test-utils/configure-suite';
import { GridSelectionMode } from '../common/enums';
import { FilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree';
import { FilteringLogic } from '../../data-operations/filtering-expression.interface';
import { IgxTabContentComponent, IgxTabHeaderComponent, IgxTabItemComponent, IgxTabsComponent } from '../../tabs/tabs/public_api';
import { IgxGridRowComponent } from './grid-row.component';
import { ISortingExpression, SortingDirection } from '../../data-operations/sorting-strategy';
import { GRID_SCROLL_CLASS } from '../../test-utils/grid-functions.spec';
import { AsyncPipe, NgFor, NgIf } from '@angular/common';
import { IgxPaginatorComponent, IgxPaginatorContentDirective } from '../../paginator/paginator.component';
import { IgxGridFooterComponent, IgxGridRow, IgxGroupByRow, IgxSummaryRow } from '../public_api';
describe('IgxGrid Component Tests #grid', () => {
const MIN_COL_WIDTH = '136px';
const COLUMN_HEADER_CLASS = '.igx-grid-th';
const TBODY_CLASS = '.igx-grid__tbody-content';
const THEAD_CLASS = '.igx-grid-thead';
configureTestSuite();
describe('IgxGrid - input properties', () => {
beforeAll(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
IgxGridTestComponent,
IgxGridMarkupDeclarationComponent,
IgxGridRemoteVirtualizationComponent,
IgxGridRemoteOnDemandComponent,
IgxGridEmptyMessage100PercentComponent
]
})
.compileComponents();
}));
it('should initialize a grid with columns from markup', () => {
const fix = TestBed.createComponent(IgxGridMarkupDeclarationComponent);
fix.detectChanges();
const grid = fix.componentInstance.instance;
const domGrid = fix.debugElement.query(By.css('igx-grid')).nativeElement;
expect(grid).toBeDefined('Grid initializing through markup failed');
expect(grid.columnList.length).toEqual(2, 'Invalid number of columns initialized');
expect(grid.rowList.length).toEqual(3, 'Invalid number of rows initialized');
expect(grid.id).toContain('igx-grid-');
expect(domGrid.id).toContain('igx-grid-');
grid.id = 'customGridId';
fix.detectChanges();
expect(grid.id).toBe('customGridId');
expect(domGrid.id).toBe('customGridId');
expect(fix.componentInstance.columnEventCount).toEqual(2);
});
it('should initialize a grid with autogenerated columns', () => {
const fix = TestBed.createComponent(IgxGridTestComponent);
fix.componentInstance.data = [
{ Number: 1, String: '1', Boolean: true, Date: new Date(Date.now()) }
];
fix.componentInstance.columns = [];
fix.componentInstance.autoGenerate = true;
fix.detectChanges();
// fakeAsync is not needed. Need a second change detection cycle for height changes to be applied.
fix.detectChanges();
const grid = fix.componentInstance.grid;
expect(grid).toBeDefined('Grid initializing through autoGenerate failed');
expect(grid.columns.length).toEqual(4, 'Invalid number of columns initialized');
expect(grid.rowList.length).toEqual(1, 'Invalid number of rows initialized');
expect(grid.columns[0].dataType).toEqual(GridColumnDataType.Number, 'Invalid dataType set on column');
expect(grid.columns.find((col) => col.index === 1).dataType)
.toEqual(GridColumnDataType.String, 'Invalid dataType set on column');
expect(grid.columns.find((col) => col.index === 2).dataType)
.toEqual(GridColumnDataType.Boolean, 'Invalid dataType set on column');
expect(grid.columns[grid.columns.length - 1].dataType).toEqual(GridColumnDataType.Date, 'Invalid dataType set on column');
expect(fix.componentInstance.columnEventCount).toEqual(4);
});
it('should initialize a grid and change column properties during initialization', () => {
const fix = TestBed.createComponent(IgxGridTestComponent);
fix.componentInstance.columns = [];
fix.componentInstance.autoGenerate = true;
fix.detectChanges();
// fakeAsync is not needed. Need a second change detection cycle for height changes to be applied.
fix.detectChanges();
const grid = fix.componentInstance.grid;
grid.columnList.forEach((column) => {
expect(column.filterable).toEqual(true);
expect(column.sortable).toEqual(true);
});
});
it('should skip properties from autoGenerateExclude when auto-generating columns', () => {
const fix = TestBed.createComponent(IgxGridTestComponent);
fix.componentInstance.data = [
{ Number: 1, String: '1', Boolean: true, Date: new Date(Date.now()) }
];
fix.componentInstance.autoGenerateExclude = ['Date', 'String'];
fix.componentInstance.columns = [];
fix.componentInstance.autoGenerate = true;
fix.detectChanges();
const grid = fix.componentInstance.grid;
expect(grid.columns.map(col => col.field)).toEqual(['Number', 'Boolean'], 'Invalid columns after exclusion initialized');
});
it('should initialize a grid and allow changing columns runtime with ngFor', () => {
const fix = TestBed.createComponent(IgxGridTestComponent);
fix.detectChanges();
// reverse order of ngFor bound collection
fix.componentInstance.columns.reverse();
fix.detectChanges();
// check order
const grid = fix.componentInstance.grid;
expect(grid.columns[0].field).toBe('value');
expect(grid.columns[1].field).toBe('index');
});
it('should initialize grid with remote virtualization', async () => {
const fix = TestBed.createComponent(IgxGridRemoteVirtualizationComponent);
fix.detectChanges();
await wait(16);
let rows = fix.componentInstance.instance.rowList.toArray();
expect(rows.length).toEqual(10);
const verticalScroll = fix.componentInstance.instance.verticalScrollContainer;
const elem = verticalScroll['scrollComponent'].elementRef.nativeElement;
// scroll down
expect(() => {
elem.scrollTop = 1000;
fix.detectChanges();
fix.componentRef.hostView.detectChanges();
}).not.toThrow();
fix.detectChanges();
fix.componentInstance.cdr.detectChanges();
await wait(16);
rows = fix.componentInstance.instance.rowList.toArray();
const data = fix.componentInstance.data.source.getValue();
for (let i = fix.componentInstance.instance.virtualizationState.startIndex; i < rows.length; i++) {
expect(rows[i].data['Col1'])
.toBe(data[i]['Col1']);
}
});
it('should remove all rows if data becomes null/undefined.', () => {
const fix = TestBed.createComponent(IgxGridRemoteVirtualizationComponent);
fix.detectChanges();
const grid = fix.componentInstance.instance;
expect(grid.rowList.length).toEqual(10);
fix.componentInstance.nullData();
fix.detectChanges();
const noRecordsSpan = fix.debugElement.query(By.css('.igx-grid__tbody-message'));
expect(grid.rowList.length).toEqual(0);
expect(noRecordsSpan).toBeTruthy();
expect(noRecordsSpan.nativeElement.innerText).toBe('Grid has no data.');
});
it('height/width should be calculated depending on number of records', () => {
const fix = TestBed.createComponent(IgxGridTestComponent);
fix.detectChanges();
const grid = fix.componentInstance.grid;
fix.componentInstance.grid.height = null;
fix.detectChanges();
const gridBody = fix.debugElement.query(By.css(TBODY_CLASS));
const gridHeader = fix.debugElement.query(By.css(THEAD_CLASS));
const gridFooter = fix.debugElement.query(By.css('.igx-grid__tfoot'));
const gridScroll = fix.debugElement.query(By.css(GRID_SCROLL_CLASS));
let gridBodyHeight;
expect(grid.rowList.length).toEqual(1);
expect(window.getComputedStyle(gridBody.nativeElement).height).toMatch('51px');
for (let i = 2; i <= 30; i++) {
grid.addRow({ index: i, value: i });
}
fix.detectChanges();
expect(grid.rowList.length).toEqual(30);
expect(window.getComputedStyle(gridBody.nativeElement).height).toMatch('1530px');
expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(false);
expect(fix.componentInstance.isHorizontalScrollbarVisible()).toBe(false);
grid.height = '200px';
fix.detectChanges();
expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(true);
// no horizontal scr, since columns have no width hence they should
// distrubute the available width between them
expect(fix.componentInstance.isHorizontalScrollbarVisible()).toBe(false);
const verticalScrollHeight = fix.componentInstance.getVerticalScrollHeight();
grid.width = '200px';
fix.detectChanges();
expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(true);
expect(fix.componentInstance.isHorizontalScrollbarVisible()).toBe(true);
expect(fix.componentInstance.getVerticalScrollHeight()).toBeLessThan(verticalScrollHeight);
gridBodyHeight = parseInt(window.getComputedStyle(grid.nativeElement).height, 10)
- parseInt(window.getComputedStyle(gridHeader.nativeElement).height, 10)
- parseInt(window.getComputedStyle(gridFooter.nativeElement).height, 10)
- parseInt(window.getComputedStyle(gridScroll.nativeElement).height, 10);
expect(window.getComputedStyle(grid.nativeElement).width).toMatch('200px');
expect(window.getComputedStyle(grid.nativeElement).height).toMatch('200px');
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toEqual(gridBodyHeight);
grid.height = '50%';
fix.detectChanges();
grid.width = '50%';
fix.detectChanges();
expect(window.getComputedStyle(grid.nativeElement).height).toMatch('300px');
expect(window.getComputedStyle(grid.nativeElement).width).toMatch('400px');
gridBodyHeight = parseInt(window.getComputedStyle(grid.nativeElement).height, 10)
- parseInt(window.getComputedStyle(gridHeader.nativeElement).height, 10)
- parseInt(window.getComputedStyle(gridFooter.nativeElement).height, 10);
// The scrollbar is no longer visible
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toEqual(gridBodyHeight);
});
it('should not have column misalignment when no vertical scrollbar is shown', () => {
const fix = TestBed.createComponent(IgxGridTestComponent);
fix.detectChanges();
// fakeAsync is not needed. Need a second change detection cycle for height changes to be applied.
fix.detectChanges();
const grid = fix.componentInstance.grid;
const gridBody = fix.debugElement.query(By.css(TBODY_CLASS));
const gridHeader = fix.debugElement.query(By.css(THEAD_CLASS));
expect(window.getComputedStyle(gridBody.children[0].nativeElement).width).toEqual(
window.getComputedStyle(gridHeader.children[0].nativeElement).width
);
expect(grid.rowList.length).toBeGreaterThan(0);
});
it('should change displayDensity runtime correctly', fakeAsync(() => {
const fixture = TestBed.createComponent(IgxGridTestComponent);
const grid = fixture.componentInstance.grid;
fixture.componentInstance.columns[1].hasSummary = true;
grid.summaryRowHeight = 0;
// density with custom class #6931:
grid.nativeElement.classList.add('custom');
fixture.detectChanges();
const headerHight = fixture.debugElement.query(By.css(THEAD_CLASS)).query(By.css('.igx-grid__tr')).nativeElement;
const rowHeight = fixture.debugElement.query(By.css(TBODY_CLASS)).query(By.css('.igx-grid__tr')).nativeElement;
const summaryItemHeight = fixture.debugElement.query(By.css('.igx-grid__tfoot'))
.query(By.css('.igx-grid-summary__item')).nativeElement;
const summaryRowHeight = fixture.debugElement.query(By.css('.igx-grid__tfoot')).nativeElement;
expect(grid.nativeElement.classList).toEqual(jasmine.arrayWithExactContents(['igx-grid', 'custom']));
expect(grid.defaultRowHeight).toBe(50);
expect(headerHight.offsetHeight).toBe(grid.defaultRowHeight);
expect(rowHeight.offsetHeight).toBe(51);
expect(summaryItemHeight.offsetHeight).toBe(grid.defaultSummaryHeight - 1);
expect(summaryRowHeight.offsetHeight).toBe(grid.defaultSummaryHeight);
grid.displayDensity = 'cosy';
grid.summaryRowHeight = null;
tick(16);
fixture.detectChanges();
expect(grid.nativeElement.classList).toEqual(jasmine.arrayWithExactContents(['igx-grid--cosy', 'custom']));
expect(grid.defaultRowHeight).toBe(40);
expect(headerHight.offsetHeight).toBe(grid.defaultRowHeight);
expect(rowHeight.offsetHeight).toBe(41);
expect(summaryItemHeight.offsetHeight).toBe(grid.defaultSummaryHeight - 1);
expect(summaryRowHeight.offsetHeight).toBe(grid.defaultSummaryHeight);
grid.displayDensity = 'compact';
grid.summaryRowHeight = undefined;
tick(16);
fixture.detectChanges();
expect(grid.nativeElement.classList).toEqual(jasmine.arrayWithExactContents(['igx-grid--compact', 'custom']));
expect(grid.defaultRowHeight).toBe(32);
expect(headerHight.offsetHeight).toBe(grid.defaultRowHeight);
expect(rowHeight.offsetHeight).toBe(33);
expect(summaryItemHeight.offsetHeight).toBe(grid.defaultSummaryHeight - 1);
expect(summaryRowHeight.offsetHeight).toBe(grid.defaultSummaryHeight);
}));
it ('checks if attributes are correctly assigned when grid has or does not have data', fakeAsync( () => {
const fixture = TestBed.createComponent(IgxGridTestComponent);
const grid = fixture.componentInstance.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
const container = fixture.nativeElement.querySelectorAll('.igx-grid__tbody-content')[0];
expect(container.getAttribute('role')).toBe(null);
//Filter grid so no results are available and grid is empty
grid.filter('index','111',IgxStringFilteringOperand.instance().condition('contains'),true);
grid.markForCheck();
fixture.detectChanges();
expect(container.getAttribute('role')).toMatch('row');
// clear grid data and check if attribute is now 'row'
grid.clearFilter();
fixture.componentInstance.clearData();
fixture.detectChanges();
tick(100);
expect(container.getAttribute('role')).toMatch('row');
}));
it('should render empty message', fakeAsync(() => {
const fixture = TestBed.createComponent(IgxGridTestComponent);
fixture.componentInstance.data = [];
fixture.detectChanges();
tick(16);
const grid = fixture.componentInstance.grid;
const gridBody = fixture.debugElement.query(By.css(TBODY_CLASS));
const domGrid = fixture.debugElement.query(By.css('igx-grid')).nativeElement;
// make sure default width/height are applied when there is no data
expect(domGrid.style.height).toBe('100%');
expect(domGrid.style.width).toBe('100%');
// Check for loaded rows in grid's container
fixture.componentInstance.generateData(30);
fixture.detectChanges();
tick(1000);
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(548);
// Check for empty filter grid message and body less than 100px
const columns = fixture.componentInstance.grid.columnList;
grid.filter(columns.get(0).field, 546000, IgxNumberFilteringOperand.instance().condition('equals'));
fixture.detectChanges();
tick(100);
expect(gridBody.nativeElement.textContent).toEqual(grid.emptyFilteredGridMessage);
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(548);
// Clear filter and check if grid's body height is restored based on all loaded rows
grid.clearFilter(columns.get(0).field);
fixture.detectChanges();
tick(100);
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(548);
// Clearing grid's data and check for empty grid message
fixture.componentInstance.clearData();
fixture.detectChanges();
tick(100);
expect(gridBody.nativeElement.innerText).toMatch(grid.emptyGridMessage);
}));
it('should render loading indicator when loading is enabled', fakeAsync(() => {
const fixture = TestBed.createComponent(IgxGridTestComponent);
fixture.componentInstance.data = [];
fixture.componentInstance.grid.isLoading = true;
fixture.detectChanges();
tick(16);
const grid = fixture.componentInstance.grid;
const gridBody = fixture.debugElement.query(By.css(TBODY_CLASS));
let loadingIndicator = gridBody.query(By.css('.igx-grid__loading'));
const domGrid = fixture.debugElement.query(By.css('igx-grid')).nativeElement;
// make sure default width/height are applied when there is no data
expect(domGrid.style.height).toBe('100%');
expect(domGrid.style.width).toBe('100%');
expect(loadingIndicator).not.toBeNull();
expect(gridBody.nativeElement.textContent).not.toEqual(grid.emptyFilteredGridMessage);
// Check for loaded rows in grid's container
fixture.componentInstance.generateData(30);
fixture.detectChanges();
tick(1000);
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(548);
loadingIndicator = gridBody.query(By.css('.igx-grid__loading'));
expect(loadingIndicator).toBeNull();
// Check for empty filter grid message and body less than 100px
const columns = fixture.componentInstance.grid.columnList;
grid.filter(columns.get(0).field, 546000, IgxNumberFilteringOperand.instance().condition('equals'));
fixture.detectChanges();
tick(100);
expect(gridBody.nativeElement.textContent).not.toEqual(grid.emptyFilteredGridMessage);
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(548);
// Clear filter and check if grid's body height is restored based on all loaded rows
grid.clearFilter(columns.get(0).field);
fixture.detectChanges();
tick(100);
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(548);
// Clearing grid's data and check for empty grid message
fixture.componentInstance.clearData();
fixture.detectChanges();
tick(100);
loadingIndicator = gridBody.query(By.css('.igx-grid__loading'));
expect(loadingIndicator).not.toBeNull();
}));
it('should render loading indicator when loading is enabled when there is height', fakeAsync(() => {
const fixture = TestBed.createComponent(IgxGridTestComponent);
fixture.componentInstance.data = [];
fixture.componentInstance.grid.isLoading = true;
fixture.componentInstance.grid.height = '400px';
fixture.detectChanges();
tick(16);
const grid = fixture.componentInstance.grid;
const gridElement = fixture.debugElement.query(By.css('.igx-grid'));
const gridBody = fixture.debugElement.query(By.css(TBODY_CLASS));
let loadingIndicator = gridBody.query(By.css('.igx-grid__loading'));
expect(loadingIndicator).not.toBeNull();
expect(gridBody.nativeElement.textContent).not.toEqual(grid.emptyFilteredGridMessage);
// Check for loaded rows in grid's container
fixture.componentInstance.generateData(30);
fixture.detectChanges();
tick(1000);
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBeGreaterThan(300);
loadingIndicator = gridBody.query(By.css('.igx-grid__loading'));
expect(loadingIndicator).toBeNull();
// the overlay should be shown
loadingIndicator = gridElement.query(By.css('.igx-grid__loading-outlet'));
expect(loadingIndicator.nativeElement.children.length).not.toBe(0);
// Check for empty filter grid message and body less than 100px
const columns = fixture.componentInstance.grid.columnList;
grid.filter(columns.get(0).field, 546000, IgxNumberFilteringOperand.instance().condition('equals'));
fixture.detectChanges();
tick(100);
expect(gridBody.nativeElement.textContent).not.toEqual(grid.emptyFilteredGridMessage);
// Clear filter and check if grid's body height is restored based on all loaded rows
grid.clearFilter(columns.get(0).field);
fixture.detectChanges();
tick(100);
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBeGreaterThan(300);
// Clearing grid's data and check for empty grid message
fixture.componentInstance.clearData();
fixture.detectChanges();
tick(100);
loadingIndicator = gridBody.query(By.css('.igx-grid__loading'));
expect(loadingIndicator).not.toBeNull();
// the overlay should be hidden
loadingIndicator = gridElement.query(By.css('.igx-grid__loading-outlet'));
expect(loadingIndicator.nativeElement.children.length).toBe(0);
}));
it('should render loading indicator when loading is enabled and autoGenerate is enabled', fakeAsync(() => {
const fixture = TestBed.createComponent(IgxGridTestComponent);
fixture.componentInstance.data = [];
fixture.componentInstance.grid.isLoading = true;
fixture.componentInstance.columns = [];
fixture.componentInstance.autoGenerate = true;
fixture.detectChanges();
tick(16);
const grid = fixture.componentInstance.grid;
const gridBody = fixture.debugElement.query(By.css(TBODY_CLASS));
const gridHead = fixture.debugElement.query(By.css(THEAD_CLASS));
let loadingIndicator = gridBody.query(By.css('.igx-grid__loading'));
let colHeaders = gridHead.queryAll(By.css('igx-grid-header'));
expect(loadingIndicator).not.toBeNull();
expect(colHeaders.length).toBe(0);
expect(gridBody.nativeElement.textContent).not.toEqual(grid.emptyFilteredGridMessage);
// Check for loaded rows in grid's container
fixture.componentInstance.grid.shouldGenerate = true;
fixture.componentInstance.data = [
{ Number: 1, String: '1', Boolean: true, Date: new Date(Date.now()) }
];
fixture.detectChanges();
tick(1000);
loadingIndicator = gridBody.query(By.css('.igx-grid__loading'));
colHeaders = gridHead.queryAll(By.css('igx-grid-header'));
expect(colHeaders.length).toBeGreaterThan(0);
expect(loadingIndicator).toBeNull();
// Clearing grid's data and check for empty grid message
fixture.componentInstance.clearData();
fixture.detectChanges();
tick(100);
loadingIndicator = gridBody.query(By.css('.igx-grid__loading'));
expect(loadingIndicator).not.toBeNull();
}));
it('should render loading indicator when loading is enabled and autoGenerate is enabled and async data', fakeAsync(() => {
const fixture = TestBed.createComponent(IgxGridRemoteOnDemandComponent);
fixture.detectChanges();
tick(16);
const grid = fixture.componentInstance.instance;
const gridBody = fixture.debugElement.query(By.css(TBODY_CLASS));
const gridHead = fixture.debugElement.query(By.css(THEAD_CLASS));
let loadingIndicator = gridBody.query(By.css('.igx-grid__loading'));
expect(loadingIndicator).not.toBeNull();
expect(gridBody.nativeElement.textContent).not.toEqual(grid.emptyFilteredGridMessage);
fixture.componentInstance.bind();
const colHeaders = gridHead.queryAll(By.css('igx-grid-header'));
loadingIndicator = gridBody.query(By.css('.igx-grid__loading'));
expect(colHeaders.length).toBeGreaterThan(0);
expect(loadingIndicator).toBeNull();
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBeGreaterThan(500);
}));
it('should render loading indicator when loading is enabled and the grid has empty filtering pre-applied', fakeAsync(() => {
const fixture = TestBed.createComponent(IgxGridTestComponent);
const grid = fixture.componentInstance.grid;
grid.filteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And);
grid.filteringExpressionsTree.filteringOperands = [
{
condition: IgxNumberFilteringOperand.instance().condition('equals'),
fieldName: 'index',
searchVal: 0
}
];
grid.isLoading = true;
fixture.detectChanges();
tick(16);
const gridBody = fixture.debugElement.query(By.css(TBODY_CLASS));
const loadingIndicator = gridBody.query(By.css('.igx-grid__loading'));
const domGrid = fixture.debugElement.query(By.css('igx-grid')).nativeElement;
// make sure default width/height are applied when there is no data
expect(domGrid.style.height).toBe('100%');
expect(domGrid.style.width).toBe('100%');
expect(loadingIndicator).not.toBeNull();
expect(gridBody.nativeElement.textContent).not.toEqual(grid.emptyFilteredGridMessage);
}));
it('should allow applying custom loading indicator', fakeAsync(() => {
const fixture = TestBed.createComponent(IgxGridRemoteOnDemandComponent);
fixture.componentInstance.instance.loadingGridTemplate = fixture.componentInstance.customTemaplate;
fixture.detectChanges();
tick(16);
const grid = fixture.componentInstance.instance;
const gridBody = fixture.debugElement.query(By.css(TBODY_CLASS));
const gridHead = fixture.debugElement.query(By.css(THEAD_CLASS));
expect(gridBody.nativeElement.textContent).toEqual('Loading...');
expect(gridBody.nativeElement.textContent).not.toEqual(grid.emptyFilteredGridMessage);
fixture.componentInstance.bind();
const colHeaders = gridHead.queryAll(By.css('igx-grid-header'));
expect(colHeaders.length).toBeGreaterThan(0);
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBeGreaterThan(500);
}));
it('should remove loading overlay when isLoading is set to false', fakeAsync(() => {
const fixture = TestBed.createComponent(IgxGridTestComponent);
fixture.componentInstance.data = [];
fixture.componentInstance.grid.isLoading = true;
fixture.detectChanges();
tick(16);
const grid = fixture.componentInstance.grid;
const gridElement = fixture.debugElement.query(By.css('.igx-grid'));
const gridBody = fixture.debugElement.query(By.css(TBODY_CLASS));
let loadingIndicator = gridBody.query(By.css('.igx-grid__loading'));
expect(loadingIndicator).not.toBeNull();
expect(gridBody.nativeElement.textContent).not.toEqual(grid.emptyFilteredGridMessage);
// Check for loaded rows in grid's container
fixture.componentInstance.generateData(30);
fixture.detectChanges();
tick(1000);
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(548);
loadingIndicator = gridBody.query(By.css('.igx-grid__loading'));
expect(loadingIndicator).toBeNull();
// the overlay should be shown
loadingIndicator = gridElement.query(By.css('.igx-grid__loading-outlet'));
expect(loadingIndicator.nativeElement.children.length).not.toBe(0);
grid.isLoading = false;
tick(16);
expect(loadingIndicator.nativeElement.children.length).toBe(0);
// Clearing grid's data and check for empty grid message
fixture.componentInstance.clearData();
fixture.detectChanges();
tick(100);
// isLoading is still false so the empty data message should show, not the loading indicator
loadingIndicator = gridBody.query(By.css('.igx-grid__loading'));
expect(loadingIndicator).toBeNull();
expect(gridBody.nativeElement.textContent).toEqual(grid.emptyGridMessage);
}));
it('should render empty message when grid height is 100%', fakeAsync(() => {
const fixture = TestBed.createComponent(IgxGridEmptyMessage100PercentComponent);
fixture.detectChanges();
tick(16);
const grid = fixture.componentInstance.grid;
const gridBody = fixture.debugElement.query(By.css(TBODY_CLASS));
const domGrid = fixture.debugElement.query(By.css('igx-grid')).nativeElement;
// make sure default width/height are applied when there is no data
expect(domGrid.style.height).toBe('100%');
expect(domGrid.style.width).toBe('100%');
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBeGreaterThan(0);
expect(gridBody.nativeElement.innerText).toMatch(grid.emptyGridMessage);
}));
});
describe('IgxGrid - virtualization tests', () => {
beforeAll(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
IgxGridTestComponent
]
})
.compileComponents();
}));
it('should change chunk size for every record after enlarging the grid and the horizontal dirs are scrambled', async () => {
const fix = TestBed.createComponent(IgxGridTestComponent);
for (let i = 2; i < 100; i++) {
fix.componentInstance.data.push({ index: i, value: i, desc: i, detail: i });
}
fix.componentInstance.columns[0].width = '400px';
fix.componentInstance.columns[1].width = '400px';
fix.componentInstance.columns.push(
{ field: 'desc', header: 'desc', dataType: 'number', width: '400px', hasSummary: false },
{ field: 'detail', header: 'detail', dataType: 'number', width: '400px', hasSummary: false }
);
fix.detectChanges();
fix.componentInstance.grid.verticalScrollContainer.getScroll().scrollTop = 100;
await wait(100);
fix.detectChanges();
fix.componentInstance.grid.verticalScrollContainer.getScroll().scrollTop = 250;
await wait(100);
fix.detectChanges();
fix.componentInstance.grid.width = '1300px';
await wait(100);
fix.detectChanges();
const rows = fix.componentInstance.grid.rowList.toArray();
for (const row of rows) {
expect(row.cells.length).toEqual(4);
}
});
it('should not keep a cached-out template as master after column resizing', async () => {
const fix = TestBed.createComponent(IgxGridTestComponent);
for (let i = 2; i < 100; i++) {
fix.componentInstance.data.push({ index: i, value: i, desc: i, detail: i });
}
fix.componentInstance.columns[0].width = '400px';
fix.componentInstance.columns[1].width = '400px';
fix.componentInstance.columns.push(
{ field: 'desc', header: 'desc', dataType: 'number', width: '400px', hasSummary: false },
{ field: 'detail', header: 'detail', dataType: 'number', width: '400px', hasSummary: false }
);
fix.detectChanges();
fix.componentInstance.grid.groupBy({ fieldName: 'value', dir: SortingDirection.Asc });
fix.detectChanges();
fix.componentInstance.grid.getColumnByName('index').width = '100px';
fix.detectChanges();
await wait(16);
const rows = fix.componentInstance.grid.dataRowList.toArray();
for (const row of rows) {
expect(row.cells.length).toEqual(4);
}
});
it('Should scroll horizontally when press shift + mouse wheel over grid headers', (async () => {
const fix = TestBed.createComponent(IgxGridTestComponent);
for (let i = 2; i < 100; i++) {
fix.componentInstance.data.push({ index: i, value: i, desc: i, detail: i });
}
fix.componentInstance.columns[0].width = '400px';
fix.componentInstance.columns[1].width = '400px';
fix.componentInstance.columns.push(
{ field: 'desc', header: 'desc', dataType: 'number', width: '400px', hasSummary: false },
{ field: 'detail', header: 'detail', dataType: 'number', width: '400px', hasSummary: false }
);
fix.detectChanges();
const grid = fix.componentInstance.grid;
grid.headerContainer.dc.instance._scrollInertia.smoothingDuration = 0;
const initialScroll = grid.verticalScrollContainer.getScroll().scrollTop;
const initialHorScroll = grid.headerContainer.getScroll().scrollLeft;
const displayContainer = grid.headerContainer.dc.instance._viewContainer.element.nativeElement;
await UIInteractions.simulateWheelEvent(displayContainer, 0, -240, true);
fix.detectChanges();
await wait(16);
expect(grid.verticalScrollContainer.getScroll().scrollTop).toBe(initialScroll);
expect(grid.headerContainer.getScroll().scrollLeft).toBeGreaterThan(initialHorScroll + 50);
await UIInteractions.simulateWheelEvent(displayContainer, 0, 240, true);
fix.detectChanges();
await wait(16);
expect(grid.verticalScrollContainer.getScroll().scrollTop).toBe(initialScroll);
expect(grid.headerContainer.getScroll().scrollLeft).toEqual(initialHorScroll);
}));
it('Should scroll horizontally when press shift + mouse wheel over grid data row', (async () => {
const fix = TestBed.createComponent(IgxGridTestComponent);
for (let i = 2; i < 100; i++) {
fix.componentInstance.data.push({ index: i, value: i, desc: i, detail: i });
}
fix.componentInstance.columns[0].width = '400px';
fix.componentInstance.columns[1].width = '400px';
fix.componentInstance.columns.push(
{ field: 'desc', header: 'desc', dataType: 'number', width: '400px', hasSummary: false },
{ field: 'detail', header: 'detail', dataType: 'number', width: '400px', hasSummary: false }
);
fix.detectChanges();
const grid = fix.componentInstance.grid;
grid.rowList.first.virtDirRow.dc.instance._scrollInertia.smoothingDuration = 0;
const initialScroll = grid.verticalScrollContainer.getScroll().scrollTop;
const initialHorScroll = grid.rowList.first.virtDirRow.getScroll().scrollLeft;
const cell = grid.gridAPI.get_cell_by_index(3, 'value');
UIInteractions.simulateClickAndSelectEvent(cell);
fix.detectChanges();
const displayContainer = grid.rowList.first.virtDirRow.dc.instance._viewContainer.element.nativeElement;
await UIInteractions.simulateWheelEvent(displayContainer, 0, -240, true);
fix.detectChanges();
await wait(16);
expect(grid.verticalScrollContainer.getScroll().scrollTop).toBe(initialScroll);
expect(grid.headerContainer.getScroll().scrollLeft).toBeGreaterThan(initialHorScroll + 50);
await UIInteractions.simulateWheelEvent(displayContainer, 0, -240, true);
fix.detectChanges();
await wait(16);
expect(grid.verticalScrollContainer.getScroll().scrollTop).toBe(initialScroll);
expect(grid.headerContainer.getScroll().scrollLeft).toBeGreaterThanOrEqual(2 * (initialHorScroll + 50));
}));
});
describe('IgxGrid - default rendering for rows and columns', () => {
beforeAll(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
IgxGridDefaultRenderingComponent,
IgxGridColumnPercentageWidthComponent,
IgxGridWrappedInContComponent,
IgxGridFormattingComponent,
IgxGridFixedContainerHeightComponent
]
})
.compileComponents();
}));
it('should init columns with width >= 136px when 5 rows and 5 columns are rendered', () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
fix.componentInstance.initColumnsRows(5, 5);
fix.detectChanges();
// fakeAsync is not needed. Need a second change detection cycle for height changes to be applied.
fix.detectChanges();
const grid = fix.componentInstance.grid;
expect(grid.columnList.get(0).width).not.toBeLessThan(136);
expect(grid.columnList.get(2).width).not.toBeLessThan(136);
expect(grid.width).toMatch('100%');
expect(grid.rowList.length).toBeGreaterThan(0);
});
it('should init columns with width >= 136px when 30 rows and 10 columns are rendered', () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
fix.componentInstance.initColumnsRows(30, 10);
fix.detectChanges();
// fakeAsync is not needed. Need a second change detection cycle for height changes to be applied.
fix.detectChanges();
const grid = fix.componentInstance.grid;
expect(grid.columnList.get(0).width).not.toBeLessThan(136);
expect(grid.columnList.get(4).width).not.toBeLessThan(136);
expect(grid.columnList.get(6).width).not.toBeLessThan(136);
expect(grid.width).toMatch('100%');
expect(grid.rowList.length).toBeGreaterThan(0);
});
it(`should init columns with width >= 136px and a horizontal scrollbar
when 1000 rows and 30 columns are rendered`, () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
fix.componentInstance.initColumnsRows(1000, 30);
fix.detectChanges();
// fakeAsync is not needed. Need a second change detection cycle for height changes to be applied.
fix.detectChanges();
const grid = fix.componentInstance.grid;
expect(grid.columnList.get(0).width).not.toBeLessThan(136);
expect(grid.columnList.get(4).width).not.toBeLessThan(136);
expect(grid.columnList.get(14).width).not.toBeLessThan(136);
expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(true);
});
it(`should init columns with width >= 136px and a horizontal scrollbar
when 200 rows and 150 columns are rendered`, () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
fix.componentInstance.initColumnsRows(200, 150);
fix.detectChanges();
// fakeAsync is not needed. Need a second change detection cycle for height changes to be applied.
fix.detectChanges();
const grid = fix.componentInstance.grid;
expect(grid.columnList.get(0).width).not.toBeLessThan(136);
expect(grid.columnList.get(4).width).not.toBeLessThan(136);
expect(grid.columnList.get(100).width).not.toBeLessThan(136);
expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(true);
expect(grid.rowList.length).toBeGreaterThan(0);
});
it('should account for columns with set width when determining default column width when grid has 100% width', () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
const grid = fix.componentInstance.grid;
fix.componentInstance.initColumnsRows(5, 5);
fix.componentInstance.changeInitColumns = true;
fix.detectChanges();
// fakeAsync is not needed. Need a second change detection cycle for height changes to be applied.
fix.detectChanges();
expect(grid.width).toEqual('100%');
expect(grid.columnList.get(0).width).toEqual('100px');
expect(grid.columnList.get(4).width).toEqual('100px');
const actualGridWidth = grid.nativeElement.clientWidth;
const expectedDefWidth = Math.max(Math.floor((actualGridWidth -
parseInt(grid.columnList.get(0).width, 10) -
parseInt(grid.columnList.get(4).width, 10)) / 3),
parseInt(MIN_COL_WIDTH, 10));
expect(parseInt(grid.columnWidth, 10)).toEqual(expectedDefWidth);
expect(parseInt(grid.columnList.get(1).width, 10)).toEqual(expectedDefWidth);
expect(parseInt(grid.columnList.get(2).width, 10)).toEqual(expectedDefWidth);
expect(parseInt(grid.columnList.get(3).width, 10)).toEqual(expectedDefWidth);
grid.columnList.forEach((column) => {
const width = parseInt(column.width, 10);
const minWidth = parseInt(grid.columnWidth, 10);
if (column.index !== 0 && column.index !== 4) {
expect(width).toBeGreaterThanOrEqual(minWidth);
}
});
expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(false);
expect(grid.rowList.length).toBeGreaterThan(0);
});
it('should account for columns with set width when determining default column width when grid has px width', () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
const grid = fix.componentInstance.grid;
grid.width = '600px';
fix.componentInstance.initColumnsRows(5, 5);
fix.componentInstance.changeInitColumns = true;
fix.detectChanges();
// fakeAsync is not needed. Need a second change detection cycle for height changes to be applied.
fix.detectChanges();
expect(grid.width).toEqual('600px');
expect(grid.columnList.get(0).width).toEqual('100px');
expect(grid.columnList.get(4).width).toEqual('100px');
const actualGridWidth = grid.nativeElement.clientWidth;
const expectedDefWidth = Math.max(Math.floor((actualGridWidth -
parseInt(grid.columnList.get(0).width, 10) -
parseInt(grid.columnList.get(4).width, 10)) / 3),
parseInt(MIN_COL_WIDTH, 10));
expect(parseInt(grid.columnWidth, 10)).toEqual(expectedDefWidth);
expect(parseInt(grid.columnList.get(1).width, 10)).toEqual(expectedDefWidth);
expect(parseInt(grid.columnList.get(2).width, 10)).toEqual(expectedDefWidth);
expect(parseInt(grid.columnList.get(3).width, 10)).toEqual(expectedDefWidth);
grid.columnList.forEach((column) => {
const width = parseInt(column.width, 10);
const minWidth = parseInt(grid.columnWidth, 10);
if (column.index !== 0 && column.index !== 4) {
expect(width).toBeGreaterThanOrEqual(minWidth);
}
});
expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(true);
expect(grid.rowList.length).toBeGreaterThan(0);
});
it(`should account for columns with set width when determining default column width when grid has 100% width
and there are enough rows to cover the grid's height`, () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
const grid = fix.componentInstance.grid;
fix.componentInstance.initColumnsRows(30, 5);
fix.componentInstance.changeInitColumns = true;
fix.detectChanges();
// fakeAsync is not needed. Need a second change detection cycle for height changes to be applied.
fix.detectChanges();
expect(grid.width).toEqual('100%');
expect(grid.columnList.get(0).width).toEqual('100px');
expect(grid.columnList.get(4).width).toEqual('100px');
const actualGridWidth = grid.unpinnedWidth;
const expectedDefWidth = Math.max(Math.floor((actualGridWidth -
parseInt(grid.columnList.get(0).width, 10) -
parseInt(grid.columnList.get(4).width, 10)) / 3),
parseInt(MIN_COL_WIDTH, 10));
expect(parseInt(grid.columnWidth, 10)).toEqual(expectedDefWidth);
expect(parseInt(grid.columnList.get(1).width, 10)).toEqual(expectedDefWidth);
expect(parseInt(grid.columnList.get(2).width, 10)).toEqual(expectedDefWidth);
expect(parseInt(grid.columnList.get(3).width, 10)).toEqual(expectedDefWidth);
grid.columnList.forEach((column) => {
const width = parseInt(column.width, 10);
const minWidth = parseInt(grid.columnWidth, 10);
if (column.index !== 0 && column.index !== 4) {
expect(width).toBeGreaterThanOrEqual(minWidth);
}
});
expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(false);
expect(grid.rowList.length).toBeGreaterThan(0);
});
it(`should account for columns with set width when determining default column width when grid has 100% width
and there are enough rows to cover the grid's height and enough columns to cover the grid's width`, () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
const grid = fix.componentInstance.grid;
fix.componentInstance.initColumnsRows(1000, 30);
fix.componentInstance.changeInitColumns = true;
fix.detectChanges();
// fakeAsync is not needed. Need a second change detection cycle for height changes to be applied.
fix.detectChanges();
expect(grid.width).toEqual('100%');
expect(grid.columnList.get(0).width).toEqual('200px');
expect(grid.columnList.get(3).width).toEqual('200px');
expect(grid.columnList.get(5).width).toEqual('200px');
expect(grid.columnList.get(10).width).toEqual('200px');
expect(grid.columnList.get(25).width).toEqual('200px');
const actualGridWidth = grid.nativeElement.clientWidth;
const expectedDefWidth = Math.max(Math.floor((actualGridWidth - 5 * 200) / 25), parseInt(MIN_COL_WIDTH, 10));
expect(parseInt(grid.columnWidth, 10)).toEqual(expectedDefWidth);
expect(parseInt(grid.columnList.get(1).width, 10)).toEqual(expectedDefWidth);
expect(parseInt(grid.columnList.get(2).width, 10)).toEqual(expectedDefWidth);
expect(parseInt(grid.columnList.get(4).width, 10)).toEqual(expectedDefWidth);
grid.columnList.forEach((column) => {
const width = parseInt(column.width, 10);
const minWidth = parseInt(grid.columnWidth, 10);
if (column.index !== 0 && column.index !== 3 && column.index !== 5 &&
column.index !== 10 && column.index !== 25) {
expect(width).toEqual(minWidth);
}
});
expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(true);
expect(grid.rowList.length).toBeGreaterThan(0);
});
it(`should account for columns with set width when determining default column width when grid has px width
and there are enough rows to cover the grid's height and enough columns to cover the grid's width`, () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
const grid = fix.componentInstance.grid;
grid.width = '800px';
fix.componentInstance.initColumnsRows(1000, 30);
fix.componentInstance.changeInitColumns = true;
fix.detectChanges();
// fakeAsync is not needed. Need a second change detection cycle for height changes to be applied.
fix.detectChanges();
expect(grid.width).toEqual('800px');
expect(grid.columnList.get(0).width).toEqual('200px');
expect(grid.columnList.get(3).width).toEqual('200px');
expect(grid.columnList.get(5).width).toEqual('200px');
expect(grid.columnList.get(10).width).toEqual('200px');
expect(grid.columnList.get(25).width).toEqual('200px');
const actualGridWidth = grid.nativeElement.clientWidth;
const expectedDefWidth = Math.max(Math.floor((actualGridWidth - 5 * 200) / 25), parseInt(MIN_COL_WIDTH, 10));
expect(parseInt(grid.columnWidth, 10)).toEqual(expectedDefWidth);
grid.columnList.forEach((column) => {
const width = parseInt(column.width, 10);
const minWidth = parseInt(grid.columnWidth, 10);
if (column.index !== 0 && column.index !== 3 && column.index !== 5 &&
column.index !== 10 && column.index !== 25) {
expect(width).toEqual(minWidth);
}
});
expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(true);
expect(grid.rowList.length).toBeGreaterThan(0);
});
it(`should account for columns with set width when determining default column width when grid has 100% width
and there are 10000 rows and 150 columns`, () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
const grid = fix.componentInstance.grid;
fix.componentInstance.initColumnsRows(10000, 150);
fix.componentInstance.changeInitColumns = true;
fix.detectChanges();
// fakeAsync is not needed. Need a second change detection cycle for height changes to be applied.
fix.detectChanges();
expect(grid.width).toEqual('100%');
expect(grid.columnList.get(0).width).toEqual('500px');
expect(grid.columnList.get(3).width).toEqual('500px');
expect(grid.columnList.get(5).width).toEqual('500px');
expect(grid.columnList.get(10).width).toEqual('500px');
expect(grid.columnList.get(50).width).toEqual('500px');
grid.columnList.forEach((column) => {
const width = parseInt(column.width, 10);
const minWidth = parseInt(grid.columnWidth, 10);
if (column.index !== 0 && column.index !== 3 && column.index !== 5 &&
column.index !== 10 && column.index !== 50) {
expect(width).toEqual(minWidth);
}
});
expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(true);
expect(grid.rowList.length).toBeGreaterThan(0);
});
it(`should account for columns with set width when determining default column width when grid has px width
and there are 10000 rows and 150 columns`, () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
const grid = fix.componentInstance.grid;
grid.width = '800px';
fix.componentInstance.initColumnsRows(10000, 150);
fix.componentInstance.changeInitColumns = true;
fix.detectChanges();
// fakeAsync is not needed. Need a second change detection cycle for height changes to be applied.
fix.detectChanges();
expect(grid.width).toEqual('800px');
expect(grid.columnList.get(0).width).toEqual('500px');
expect(grid.columnList.get(3).width).toEqual('500px');
expect(grid.columnList.get(5).width).toEqual('500px');
expect(grid.columnList.get(10).width).toEqual('500px');
expect(grid.columnList.get(50).width).toEqual('500px');
grid.columnList.forEach((column) => {
const width = parseInt(column.width, 10);
const minWidth = parseInt(grid.columnWidth, 10);
if (column.index !== 0 && column.index !== 3 && column.index !== 5 &&
column.index !== 10 && column.index !== 50) {
expect(width).toEqual(minWidth);
}
});
expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(true);
expect(grid.rowList.length).toBeGreaterThan(0);
});
it('should render all records if height is explicitly set to null.', () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
const grid = fix.componentInstance.grid;
fix.componentInstance.initColumnsRows(20, 5);
grid.height = null;
fix.detectChanges();
// fakeAsync is not needed. Need a second change detection cycle for height changes to be applied.
fix.detectChanges();
const recsCount = grid.data.length;
// tbody should have height equal to all items * item height
expect(grid.tbody.nativeElement.clientHeight).toEqual(recsCount * 51);
expect(grid.rowList.length).toBeGreaterThan(0);
});
it('should match width and height of parent container when width/height are set in %', () => {
const fix = TestBed.createComponent(IgxGridWrappedInContComponent);
const grid = fix.componentInstance.grid;
fix.componentInstance.data = fix.componentInstance.fullData;
fix.componentInstance.outerWidth = 800;
fix.componentInstance.outerHeight = 600;
fix.componentInstance.grid.width = '50%';
fix.componentInstance.grid.height = '50%';
fix.detectChanges();
// fakeAsync is not needed. Need a second change detection cycle for height changes to be applied.
fix.detectChanges();
expect(window.getComputedStyle(grid.nativeElement).height).toMatch('300px');
expect(window.getComputedStyle(grid.nativeElement).width).toMatch('400px');
expect(grid.rowList.length).toBeGreaterThan(0);
});
it(`should render 10 records if height is unset and parent container's height is unset`, () => {
const fix = TestBed.createComponent(IgxGridWrappedInContComponent);
fix.componentInstance.data = fix.componentInstance.fullData;
fix.detectChanges();
// fakeAsync is not needed. Need a second change detection cycle for height changes to be applied.
fix.detectChanges();
const defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
expect(defaultHeight).not.toBeFalsy();
expect(parseInt(defaultHeight, 10)).toBeGreaterThan(400);
expect(fix.componentInstance.isVerticalScrollbarVisible()).toBeTruthy();
expect(fix.componentInstance.grid.rowList.length).toBeGreaterThanOrEqual(10);
});
it(`should render pixel height when one is set and parent container's height is unset`, () => {
const fix = TestBed.createComponent(IgxGridWrappedInContComponent);
fix.componentInstance.data = fix.componentInstance.fullData;
fix.componentInstance.grid.height = '700px';
fix.detectChanges();
// fakeAsync is not needed. Need a second change detection cycle for height changes to be applied.
fix.detectChanges();
const defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
expect(defaultHeight).not.toBeFalsy();
expect(parseInt(defaultHeight, 10)).toBeGreaterThan(400);
expect(fix.componentInstance.isVerticalScrollbarVisible()).toBeTruthy();
expect(fix.componentInstance.grid.rowList.length).toBeGreaterThanOrEqual(10);
});
it(`should render all records exactly if height is 100% and parent container's height is unset and
there are fewer than 10 records in the data view`, () => {
const fix = TestBed.createComponent(IgxGridWrappedInContComponent);
fix.componentInstance.grid.height = '100%';
fix.componentInstance.data = fix.componentInstance.semiData;
fix.detectChanges();
// fakeAsync is not needed. Need a second change detection cycle for height changes to be applied.
fix.detectChanges();
const defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
expect(defaultHeight).toBeFalsy();
expect(fix.debugElement.query(By.css(TBODY_CLASS))
.nativeElement.getBoundingClientRect().height).toBeGreaterThan(200);
expect(fix.componentInstance.isVerticalScrollbarVisible()).toBeFalsy();
expect(fix.componentInstance.grid.rowList.length).toEqual(5);
});
it(`should render 10 records if height is 100% and parent container's height is unset and
display density is changed`, async () => {
const fix = TestBed.createComponent(IgxGridWrappedInContComponent);
fix.detectChanges();
fix.componentInstance.grid.height = '100%';
fix.componentInstance.data = fix.componentInstance.fullData.slice(0, 10);
fix.detectChanges();
await wait(100);
expect(fix.componentInstance.grid.rowList.length).toEqual(10);
fix.componentInstance.density = DisplayDensity.compact;
fix.detectChanges();
await wait(100);
const defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
const defaultHeightNum = parseInt(defaultHeight, 10);
expect(defaultHeight).not.toBeFalsy();
expect(defaultHeightNum).toBe(330);
expect(fix.componentInstance.isVerticalScrollbarVisible()).toBeFalsy();
expect(fix.componentInstance.grid.rowList.length).toEqual(10);
});
it(`should render grid with correct height when parent container's height is set
and the total row height is smaller than parent height #1861`, fakeAsync(() => {
const fix = TestBed.createComponent(IgxGridFixedContainerHeightComponent);
fix.componentInstance.grid.height = '100%';
fix.componentInstance.paging = true;
fix.componentInstance.data = fix.componentInstance.data.slice(0, 5);
tick();
fix.detectChanges();
const domGrid = fix.debugElement.query(By.css('igx-grid')).nativeElement;
expect(parseInt(window.getComputedStyle(domGrid).height, 10)).toBe(300);
}));
it(`should render grid with correct height when height is in percent and the
sum height of all rows is lower than parent height #1858`, fakeAsync(() => {
const fix = TestBed.createComponent(IgxGridFixedContainerHeightComponent);
fix.componentInstance.grid.height = '100%';
fix.componentInstance.data = fix.componentInstance.data.slice(0, 3);
tick();
fix.detectChanges();
const domGrid = fix.debugElement.query(By.css('igx-grid')).nativeElement;
expect(parseInt(window.getComputedStyle(domGrid).height, 10)).toBe(300);
}));
it('should keep auto-sizing if initial data is empty then set to a new array', fakeAsync(() => {
const fix = TestBed.createComponent(IgxGridWrappedInContComponent);
tick();
fix.detectChanges();
let defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
expect(defaultHeight).toBeFalsy(); // initially body height is null in auto-sizing scenarios with empty data
expect(fix.componentInstance.grid.calcHeight).toBeNull();
fix.componentInstance.data = fix.componentInstance.fullData;
tick();
fix.detectChanges();
defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
const defaultHeightNum = parseInt(defaultHeight, 10);
expect(defaultHeight).not.toBeFalsy();
expect(defaultHeightNum).toBe(510);
expect(fix.componentInstance.grid.calcHeight).toBe(510);
}));
it('should keep auto-sizing if initial data is set to empty array that is then filled', fakeAsync(() => {
const fix = TestBed.createComponent(IgxGridWrappedInContComponent);
fix.detectChanges();
let defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
expect(defaultHeight).toBeFalsy(); // initially body height is null in auto-sizing scenarios with empty data
expect(fix.componentInstance.grid.calcHeight).toBeNull();
fix.componentInstance.data = fix.componentInstance.fullData;
fix.detectChanges();
defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
const defaultHeightNum = parseInt(defaultHeight, 10);
expect(defaultHeight).not.toBeFalsy();
expect(defaultHeightNum).toBe(510);
expect(fix.componentInstance.grid.calcHeight).toBe(510);
}));
it(`should not render with calcHeight null at any point when loading data and
auto-sizing is required and initial data is empty`, () => {
const fix = TestBed.createComponent(IgxGridWrappedInContComponent);
fix.detectChanges();
let defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
expect(defaultHeight).toBeFalsy(); // initially body height is null in auto-sizing scenarios with empty data
expect(fix.componentInstance.grid.calcHeight).toBeNull();
fix.componentInstance.data = Array.from({ length: 100000 }, (_, i) => ({ ID: i, CompanyName: 'CN' + i }));
fix.detectChanges();
defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
const defaultHeightNum = parseInt(defaultHeight, 10);
expect(defaultHeight).not.toBeFalsy();
expect(defaultHeightNum).toBe(510);
expect(fix.componentInstance.grid.calcHeight).toBe(510);
});
it('should keep auto-sizing if initial data is set to small array that is then filled', () => {
const fix = TestBed.createComponent(IgxGridWrappedInContComponent);
fix.componentInstance.data = fix.componentInstance.semiData;
fix.detectChanges();
let defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
expect(defaultHeight).toBeFalsy();
expect(fix.componentInstance.grid.calcHeight).toBeNull();
fix.componentInstance.data = fix.componentInstance.fullData;
fix.componentInstance.cdr.detectChanges();
fix.detectChanges();
defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
const defaultHeightNum = parseInt(defaultHeight, 10);
expect(defaultHeight).not.toBeFalsy();
expect(defaultHeightNum).toBe(510);
expect(fix.componentInstance.grid.calcHeight).toBe(510);
});
it(`should render with calcHeight null if initial data is small but then
auto-size when it is filled`, async () => {
const fix = TestBed.createComponent(IgxGridWrappedInContComponent);
fix.componentInstance.data = fix.componentInstance.semiData;
fix.detectChanges();
let defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
expect(defaultHeight).toBeFalsy();
expect(fix.componentInstance.grid.calcHeight).toBeNull();
fix.componentInstance.data = Array.from({ length: 100000 }, (_, i) => ({ ID: i, CompanyName: 'CN' + i }));
fix.detectChanges();
await wait(500);
defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
const defaultHeightNum = parseInt(defaultHeight, 10);
expect(defaultHeight).not.toBeFalsy();
expect(defaultHeightNum).toBe(510);
expect(fix.componentInstance.grid.calcHeight).toBe(510);
});
it('should keep default height when filtering', fakeAsync(() => {
const fix = TestBed.createComponent(IgxGridWrappedInContComponent);
tick();
fix.detectChanges();
let defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
expect(defaultHeight).toBeFalsy(); // initially body height is null in auto-sizing scenarios with empty data
expect(fix.componentInstance.grid.calcHeight).toBeNull();
fix.componentInstance.data = fix.componentInstance.fullData;
tick();
fix.detectChanges();
defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
let defaultHeightNum = parseInt(defaultHeight, 10);
expect(defaultHeight).not.toBeFalsy();
expect(defaultHeightNum).toBe(510);
expect(fix.componentInstance.grid.calcHeight).toBe(510);
fix.componentInstance.grid.filter('ID', 'ALFKI', IgxStringFilteringOperand.instance().condition('equals'));
tick();
fix.detectChanges();
defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
defaultHeightNum = parseInt(defaultHeight, 10);
expect(defaultHeight).not.toBeFalsy();
expect(defaultHeightNum).toBe(510);
expect(fix.componentInstance.grid.calcHeight).toBe(510);
}));
it('should not keep default height when lower the amount of bound data', async () => {
const fix = TestBed.createComponent(IgxGridWrappedInContComponent);
fix.detectChanges();
let defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
expect(defaultHeight).toBeFalsy(); // initially body height is null in auto-sizing scenarios with empty data
expect(fix.componentInstance.grid.calcHeight).toBeNull();
fix.componentInstance.data = fix.componentInstance.fullData;
fix.detectChanges();
defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
const defaultHeightNum = parseInt(defaultHeight, 10);
expect(defaultHeight).not.toBeFalsy();
expect(defaultHeightNum).toBe(510);
expect(fix.componentInstance.grid.calcHeight).toBe(510);
fix.componentInstance.grid.data = fix.componentInstance.semiData;
fix.detectChanges();
await wait(100);
fix.detectChanges();
defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
expect(defaultHeight).toBeFalsy();
expect(fix.componentInstance.grid.calcHeight).toBeNull();
});
it('should not keep auto-sizing when changing height', fakeAsync(() => {
const fix = TestBed.createComponent(IgxGridWrappedInContComponent);
fix.detectChanges();
let defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
expect(defaultHeight).toBeFalsy(); // initially body height is null in auto-sizing scenarios with empty data
expect(fix.componentInstance.grid.calcHeight).toBeNull();
fix.componentInstance.data = fix.componentInstance.fullData;
fix.detectChanges();
defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
let defaultHeightNum = parseInt(defaultHeight, 10);
expect(defaultHeight).not.toBeFalsy();
expect(defaultHeightNum).toBe(510);
expect(fix.componentInstance.grid.calcHeight).toBe(510);
fix.componentInstance.grid.height = '400px';
fix.detectChanges();
defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
defaultHeightNum = parseInt(defaultHeight, 10);
expect(defaultHeight).not.toBeFalsy();
expect(defaultHeightNum).toBeLessThan(400);
expect(defaultHeightNum).toBeGreaterThan(300);
expect(fix.componentInstance.grid.calcHeight).toBeLessThan(400);
expect(fix.componentInstance.grid.calcHeight).toBeGreaterThan(300);
}));
it('should not auto-size when changing height is determinable', fakeAsync(() => {
const fix = TestBed.createComponent(IgxGridWrappedInContComponent);
fix.componentInstance.outerHeight = 800;
tick();
fix.detectChanges();
let defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
let defaultHeightNum = parseInt(defaultHeight, 10);
expect(defaultHeight).not.toBeFalsy();
expect(defaultHeightNum).toBeLessThan(800);
expect(defaultHeightNum).toBeGreaterThan(700);
expect(fix.componentInstance.grid.calcHeight).toBeLessThan(800);
expect(fix.componentInstance.grid.calcHeight).toBeGreaterThan(700);
fix.componentInstance.data = fix.componentInstance.fullData;
tick();
fix.detectChanges();
defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height;
defaultHeightNum = parseInt(defaultHeight, 10);
expect(defaultHeight).not.toBeFalsy();
expect(defaultHeightNum).toBeLessThan(800);
expect(defaultHeightNum).toBeGreaterThan(700);
expect(fix.componentInstance.grid.calcHeight).toBeLessThan(800);
expect(fix.componentInstance.grid.calcHeight).toBeGreaterThan(700);
fix.componentInstance.data = fix.componentInstance.fullData;
}));
it('should render correct columns if after scrolling right container size changes so that all columns become visible.',
async () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
fix.detectChanges();
const grid = fix.componentInstance.grid;
grid.width = '500px';
fix.componentInstance.initColumnsRows(5, 5);
fix.detectChanges();
await wait(16);
expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(true);
const scrollbar = grid.headerContainer.getScroll();
scrollbar.scrollLeft = 10000;
grid.width = '1500px';
fix.detectChanges();
await wait(100);
expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(false);
const headers = fix.debugElement.queryAll(By.css(COLUMN_HEADER_CLASS));
expect(headers.length).toEqual(5);
for (let i = 0; i < headers.length; i++) {
expect(headers[i].context.column.field).toEqual(grid.columnList.get(i).field);
}
});
it('Should render date and number values based on default formatting', fakeAsync(() => {
const fixture = TestBed.createComponent(IgxGridFormattingComponent);
fixture.detectChanges();
tick(16);
const grid = fixture.componentInstance.grid;
const rows = grid.rowList.toArray();
// verify default number formatting
let expectedValue = '2,760';
expect((rows[0].cells.toArray()[3] as any).element.nativeElement.textContent).toBe(expectedValue);
expectedValue = '1,098';
expect((rows[5].cells.toArray()[3] as any).element.nativeElement.textContent).toBe(expectedValue);
expectedValue = '7,898';
expect((rows[7].cells.toArray()[3] as any).element.nativeElement.textContent).toBe(expectedValue);
// verify formatter function formatting
expectedValue = '2.76e+3';
expect((rows[0].cells.toArray()[5] as any).element.nativeElement.textContent).toBe(expectedValue);
expectedValue = '1.098e+3';
expect((rows[5].cells.toArray()[5] as any).element.nativeElement.textContent).toBe(expectedValue);
expectedValue = '7.898e+3';
expect((rows[7].cells.toArray()[5] as any).element.nativeElement.textContent).toBe(expectedValue);
// verify date formatting
expectedValue = 'Mar 21, 2005';
expect((rows[0].cells.toArray()[4] as any).element.nativeElement.textContent).toBe(expectedValue);
expectedValue = 'Jan 15, 2008';
expect((rows[1].cells.toArray()[4] as any).element.nativeElement.textContent).toBe(expectedValue);
expectedValue = 'Nov 20, 2010';
expect((rows[2].cells.toArray()[4] as any).element.nativeElement.textContent).toBe(expectedValue);
// verify summaries formatting
let avgValue;
let earliestValue;
const summaries = fixture.debugElement.queryAll(By.css('.igx-grid-summary'));
summaries.forEach((summary) => {
const avgLabel = summary.query(By.css('[title=\'Avg\']'));
const earliest = summary.query(By.css('[title=\'Earliest\']'));
if (avgLabel) {
avgValue = avgLabel.nativeElement.nextSibling.innerText;
expect(avgValue).toBe('3,900.4');
}
if (earliest) {
earliestValue = earliest.nativeElement.nextSibling.innerText;
expect(earliestValue).toBe('May 17, 1990');
}
});
}));
it('Should properly handle dates in ISO 8601 format', () => {
const fixture = TestBed.createComponent(IgxGridFormattingComponent);
const grid = fixture.componentInstance.grid;
grid.data = fixture.componentInstance.data.map(rec => {
const newRec = rec as any as {
ProductID: number;
ProductName: string;
InStock: boolean;
UnitsInStock: number;
OrderDate: string;
};
newRec.OrderDate = rec.OrderDate.toISOString();
return newRec;
});
fixture.detectChanges();
// verify cells formatting
const rows = grid.rowList.toArray();
let expectedValue = 'Mar 21, 2005';
expect((rows[0].cells.toArray()[4] as any).element.nativeElement.textContent).toBe(expectedValue);
expectedValue = 'Jan 15, 2008';
expect((rows[1].cells.toArray()[4] as any).element.nativeElement.textContent).toBe(expectedValue);
expectedValue = 'Nov 20, 2010';
expect((rows[2].cells.toArray()[4] as any).element.nativeElement.textContent).toBe(expectedValue);
// verify summaries formatting
let avgValue;
let earliestValue;
const summaries = fixture.debugElement.queryAll(By.css('.igx-grid-summary'));
summaries.forEach((summary) => {
const avgLabel = summary.query(By.css('[title=\'Avg\']'));
const earliest = summary.query(By.css('[title=\'Earliest\']'));
if (avgLabel) {
avgValue = avgLabel.nativeElement.nextSibling.innerText;
expect(avgValue).toBe('3,900.4');
}
if (earliest) {
earliestValue = earliest.nativeElement.nextSibling.innerText;
expect(earliestValue).toBe('May 17, 1990');
}
});
});
it('Should properly handle dates respresented as number of milliseconds', () => {
const fixture = TestBed.createComponent(IgxGridFormattingComponent);
const grid = fixture.componentInstance.grid;
grid.data = fixture.componentInstance.data.map(rec => {
const newRec = rec as any as {
ProductID: number;
ProductName: string;
InStock: boolean;
UnitsInStock: number;
OrderDate: number;
};
newRec.OrderDate = rec.OrderDate.getTime();
return newRec;
});
fixture.detectChanges();
// verify cells formatting
const rows = grid.rowList.toArray();
let expectedValue = 'Mar 21, 2005';
expect((rows[0].cells.toArray()[4] as any).element.nativeElement.textContent).toBe(expectedValue);
expectedValue = 'Jan 15, 2008';
expect((rows[1].cells.toArray()[4] as any).element.nativeElement.textContent).toBe(expectedValue);
expectedValue = 'Nov 20, 2010';
expect((rows[2].cells.toArray()[4] as any).element.nativeElement.textContent).toBe(expectedValue);
// verify summaries formatting
let avgValue;
let earliestValue;
const summaries = fixture.debugElement.queryAll(By.css('.igx-grid-summary'));
summaries.forEach((summary) => {
const avgLabel = summary.query(By.css('[title=\'Avg\']'));
const earliest = summary.query(By.css('[title=\'Earliest\']'));
if (avgLabel) {
avgValue = avgLabel.nativeElement.nextSibling.innerText;
expect(avgValue).toBe('3,900.4');
}
if (earliest) {
earliestValue = earliest.nativeElement.nextSibling.innerText;
expect(earliestValue).toBe('May 17, 1990');
}
});
});
it('Should change dates/number display based on locale #ivy', fakeAsync(() => {
const fixture = TestBed.createComponent(IgxGridFormattingComponent);
const grid = fixture.componentInstance.grid;
grid.data = fixture.componentInstance.data.map(rec => {
const newRec = rec as any as {
ProductID: number;
ProductName: string;
InStock: boolean;
UnitsInStock: number;
OrderDate: number;
};
newRec.OrderDate = rec.OrderDate.getTime();
return newRec;
});
fixture.detectChanges();
let rows = grid.rowList.toArray();
let expectedValue = 'Mar 21, 2005';
expect((rows[0].cells.toArray()[4] as any).element.nativeElement.textContent).toBe(expectedValue);
expectedValue = 'Jan 15, 2008';
expect((rows[1].cells.toArray()[4] as any).element.nativeElement.textContent).toBe(expectedValue);
expectedValue = 'Nov 20, 2010';
expect((rows[2].cells.toArray()[4] as any).element.nativeElement.textContent).toBe(expectedValue);
// verify summaries formatting
let avgValue;
let earliestValue;
let summaries = fixture.debugElement.queryAll(By.css('.igx-grid-summary'));
summaries.forEach((summary) => {
const avgLabel = summary.query(By.css('[title=\'Avg\']'));
const earliest = summary.query(By.css('[title=\'Earliest\']'));
if (avgLabel) {
avgValue = avgLabel.nativeElement.nextSibling.innerText;
expect(avgValue).toBe('3,900.4');
}
if (earliest) {
earliestValue = earliest.nativeElement.nextSibling.innerText;
expect(earliestValue).toBe('May 17, 1990');
}
});
grid.locale = 'de-DE';
grid.columnList.get(5).pipeArgs = {
timezone: 'UTC',
format: 'longDate',
digitsInfo: '1.2-2'
};
grid.columnList.get(4).pipeArgs = {
timezone: 'UTC',
format: 'longDate',
digitsInfo: '1.2-2'
};
grid.columnList.get(3).pipeArgs = {
timezone: 'UTC',
format: 'longDate',
digitsInfo: '1.2-2'
};
tick(300);
fixture.detectChanges();
rows = grid.rowList.toArray();
expectedValue = '21. März 2005';
expect((rows[0].cells.toArray()[4] as any).element.nativeElement.textContent).toBe(expectedValue);
expectedValue = '15. Januar 2008';
expect((rows[1].cells.toArray()[4] as any).element.nativeElement.textContent).toBe(expectedValue);
expectedValue = '20. November 2010';
expect((rows[2].cells.toArray()[4] as any).element.nativeElement.textContent).toBe(expectedValue);
// verify summaries formatting
summaries = fixture.debugElement.queryAll(By.css('.igx-grid-summary'));
summaries.forEach((summary) => {
const avgLabel = summary.query(By.css('[title=\'Avg\']'));
const earliest = summary.query(By.css('[title=\'Earliest\']'));
if (avgLabel) {
avgValue = avgLabel.nativeElement.nextSibling.innerText;
expect(avgValue).toBe('3.900,40');
}
if (earliest) {
earliestValue = earliest.nativeElement.nextSibling.innerText;
expect(earliestValue).toBe('17. Mai 1990');
}
});
}));
it('Should calculate default column width when a column has width in %', async () => {
const fix = TestBed.createComponent(IgxGridColumnPercentageWidthComponent);
fix.componentInstance.initColumnsRows(5, 3);
await wait(16);
fix.detectChanges();
const grid = fix.componentInstance.grid;
expect(grid.columnList.get(1).width).toEqual('150px');
expect(grid.columnList.get(2).width).toEqual('150px');
const hScroll = fix.debugElement.query(By.css(GRID_SCROLL_CLASS));
expect(hScroll.nativeElement.hidden).toBe(true);
grid.columnList.get(0).width = '70%';
fix.detectChanges();
await wait(16);
// check UI
const header0 = fix.debugElement.queryAll(By.css('igx-grid-header-group'))[0];
const header1 = fix.debugElement.queryAll(By.css('igx-grid-header-group'))[1];
const header2 = fix.debugElement.queryAll(By.css('igx-grid-header-group'))[2];
expect(header0.nativeElement.offsetWidth).toEqual(350);
expect(header1.nativeElement.offsetWidth).toEqual(136);
expect(header2.nativeElement.offsetWidth).toEqual(136);
expect(hScroll.nativeElement.hidden).toBe(false);
// check virtualization cache is valid
const virtDir = grid.gridAPI.get_row_by_index(0).virtDirRow;
expect(virtDir.getSizeAt(0)).toEqual(350);
expect(virtDir.getSizeAt(1)).toEqual(136);
expect(virtDir.getSizeAt(2)).toEqual(136);
});
it('Should re-calculate column width when a column has width in % and grid width changes.', async () => {
const fix = TestBed.createComponent(IgxGridColumnPercentageWidthComponent);
fix.componentInstance.initColumnsRows(5, 3);
fix.detectChanges();
await wait(16);
const grid = fix.componentInstance.grid;
const hScroll = fix.debugElement.query(By.css(GRID_SCROLL_CLASS));
expect(hScroll.nativeElement.hidden).toBe(true);
grid.columnList.get(0).width = '70%';
fix.detectChanges();
await wait(16);
grid.width = '1000px';
fix.detectChanges();
await wait(16);
// check UI
const header0 = fix.debugElement.queryAll(By.css('igx-grid-header-group'))[0];
const header1 = fix.debugElement.queryAll(By.css('igx-grid-header-group'))[1];
const header2 = fix.debugElement.queryAll(By.css('igx-grid-header-group'))[2];
expect(header0.nativeElement.offsetWidth).toEqual(700);
expect(header1.nativeElement.offsetWidth).toEqual(150);
expect(header2.nativeElement.offsetWidth).toEqual(150);
expect(hScroll.nativeElement.hidden).toBe(true);
// check virtualization cache is valid
const virtDir = grid.gridAPI.get_row_by_index(0).virtDirRow;
expect(virtDir.getSizeAt(0)).toEqual(700);
expect(virtDir.getSizeAt(1)).toEqual(150);
expect(virtDir.getSizeAt(2)).toEqual(150);
});
it('Should calculate column width when a column has width in % and row selectors are enabled.', async () => {
const fix = TestBed.createComponent(IgxGridColumnPercentageWidthComponent);
fix.componentInstance.initColumnsRows(5, 3);
await wait(16);
fix.detectChanges();
const grid = fix.componentInstance.grid;
const hScroll = fix.debugElement.query(By.css(GRID_SCROLL_CLASS));
grid.rowSelection = GridSelectionMode.multiple;
fix.detectChanges();
grid.columnList.get(0).width = '70%';
fix.detectChanges();
await wait(16);
// check UI
const rowSelectorHeader = grid.theadRow.nativeElement.querySelector('.igx-grid__cbx-selection') as HTMLElement;
const header0 = fix.debugElement.queryAll(By.css('igx-grid-header-group'))[0];
const header1 = fix.debugElement.queryAll(By.css('igx-grid-header-group'))[1];
const header2 = fix.debugElement.queryAll(By.css('igx-grid-header-group'))[2];
const expectedWidth = Math.round(0.7 * (grid.unpinnedWidth + rowSelectorHeader.offsetWidth));
expect(header0.nativeElement.offsetWidth).toBeGreaterThanOrEqual(expectedWidth - 1);
expect(header0.nativeElement.offsetWidth).toBeLessThanOrEqual(expectedWidth + 1);
expect(header1.nativeElement.offsetWidth).toEqual(136);
expect(header2.nativeElement.offsetWidth).toEqual(136);
expect(hScroll.nativeElement.hidden).toBe(false);
// check virtualization cache is valid
const virtDir = grid.gridAPI.get_row_by_index(0).virtDirRow;
expect(virtDir.getSizeAt(0)).toEqual(expectedWidth);
expect(virtDir.getSizeAt(1)).toEqual(136);
expect(virtDir.getSizeAt(2)).toEqual(136);
});
it('Should render correct column widths when having mixed width setting - px, %, null', async () => {
const fix = TestBed.createComponent(IgxGridColumnPercentageWidthComponent);
fix.componentInstance.initColumnsRows(5, 3);
const grid = fix.componentInstance.grid;
const hScroll = fix.debugElement.query(By.css(GRID_SCROLL_CLASS));
fix.detectChanges();
grid.columnList.get(0).width = '50%';
grid.columnList.get(1).width = '100px';
fix.detectChanges();
await wait(16);
const header0 = fix.debugElement.queryAll(By.css('igx-grid-header-group'))[0];
const header1 = fix.debugElement.queryAll(By.css('igx-grid-header-group'))[1];
const header2 = fix.debugElement.queryAll(By.css('igx-grid-header-group'))[2];
expect(header0.nativeElement.offsetWidth).toEqual(250);
expect(header1.nativeElement.offsetWidth).toEqual(100);
expect(header2.nativeElement.offsetWidth).toEqual(150);
expect(hScroll.nativeElement.hidden).toBe(true);
// check virtualization cache is valid
const virtDir = grid.gridAPI.get_row_by_index(0).virtDirRow;
expect(virtDir.getSizeAt(0)).toEqual(250);
expect(virtDir.getSizeAt(1)).toEqual(100);
expect(virtDir.getSizeAt(2)).toEqual(150);
});
it('should render all columns if grid width is set to null.', async () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
fix.componentInstance.initColumnsRows(5, 30);
const grid = fix.componentInstance.grid;
fix.detectChanges();
grid.width = null;
fix.detectChanges();
await wait(16);
// grid should render all columns and all should be visible.
const cells = grid.gridAPI.get_row_by_index(0).cells;
expect(cells.length).toBe(30);
expect(parseInt(grid.hostWidth, 10)).toBe(30 * 136);
});
it('should retain column with in % after hiding/showing grid with 100% width', () => {
const fix = TestBed.createComponent(IgxGridColumnPercentageWidthComponent);
fix.componentInstance.initColumnsRows(5, 3);
const grid = fix.componentInstance.grid;
fix.detectChanges();
grid.width = '100%';
fix.detectChanges();
grid.columnList.get(0).width = '50%';
fix.detectChanges();
// hide
grid.nativeElement.style.display = 'none';
// simulate resize observer reflow
grid.reflow();
expect(grid.columnList.get(0).width).toBe('50%');
grid.nativeElement.style.display = '';
// simulate resize observer reflow
grid.reflow();
expect(grid.columnList.get(0).width).toBe('50%');
});
});
describe('IgxGrid - API methods', () => {
beforeAll(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
IgxGridDefaultRenderingComponent,
IgxGridWrappedInContComponent
]
})
.compileComponents();
}));
it(`When edit a cell onto filtered data through grid method, the row should
disapear and the new value should not persist onto the next row`, fakeAsync(() => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
fix.componentInstance.initColumnsRows(5, 5);
fix.detectChanges();
tick(16);
const grid = fix.componentInstance.grid;
const cols = fix.componentInstance.columns;
const editValue = 0;
grid.filter(cols[1].key, 2, IgxNumberFilteringOperand.instance().condition('greaterThan'));
fix.detectChanges();
grid.getCellByColumn(0, cols[1].key).update(editValue);
fix.detectChanges();
const gridRows = fix.debugElement.queryAll(By.css('igx-grid-row'));
expect(gridRows.length).toEqual(1);
const firstRowCells = gridRows[0].queryAll(By.css('igx-grid-cell'));
const firstCellInputValue = firstRowCells[1].nativeElement.textContent.trim();
expect(firstCellInputValue).toEqual('4');
}));
it(`GetNextCell: should return correctly next cell coordinates`, async () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
fix.componentInstance.initColumnsRows(15, 5);
fix.detectChanges();
await wait(16);
const grid = fix.componentInstance.grid;
grid.height = '500px';
await wait(30);
fix.detectChanges();
grid.getColumnByName('col2').editable = true;
fix.detectChanges();
grid.getColumnByName('col4').editable = true;
fix.detectChanges();
// when the next cell is on the same row
let nextCellCoords = grid.getNextCell(0, 0, (col) => col.editable);
expect(nextCellCoords).toEqual({ rowIndex: 0, visibleColumnIndex: 2 });
// when the next cell is on the next row
nextCellCoords = grid.getNextCell(0, 4, (col) => col.editable);
expect(nextCellCoords).toEqual({ rowIndex: 1, visibleColumnIndex: 2 });
// when the next cell is not in the view
nextCellCoords = grid.getNextCell(9, 4, (col) => col.editable);
expect(nextCellCoords).toEqual({ rowIndex: 10, visibleColumnIndex: 2 });
// when the current row and column index are not valid
nextCellCoords = grid.getNextCell(-10, 14, (col) => col.editable);
expect(nextCellCoords).toEqual({ rowIndex: -10, visibleColumnIndex: 14 });
// when grid has no data
grid.filter('col0', 2, IgxNumberFilteringOperand.instance().condition('greaterThan'));
fix.detectChanges();
nextCellCoords = grid.getNextCell(0, 0, (col) => col.editable);
expect(nextCellCoords).toEqual({ rowIndex: 0, visibleColumnIndex: 0 });
});
it(`GetPreviousCell: should return correctly next cell coordinates`, async () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
fix.componentInstance.initColumnsRows(15, 5);
fix.detectChanges();
await wait(16);
const grid = fix.componentInstance.grid;
grid.height = '500px';
await wait(30);
fix.detectChanges();
grid.getColumnByName('col2').editable = true;
fix.detectChanges();
grid.getColumnByName('col4').editable = true;
fix.detectChanges();
// when the previous cell is on the same row
let prevCellCoords = grid.getPreviousCell(0, 4, (col) => col.editable);
expect(prevCellCoords).toEqual({ rowIndex: 0, visibleColumnIndex: 2 });
// when the previous cell is on the previous row
prevCellCoords = grid.getPreviousCell(1, 2, (col) => col.editable);
expect(prevCellCoords).toEqual({ rowIndex: 0, visibleColumnIndex: 4 });
// when the current row and column index are not valid
prevCellCoords = grid.getPreviousCell(-110, 2, (col) => col.editable);
expect(prevCellCoords).toEqual({ rowIndex: -110, visibleColumnIndex: 2 });
// when there is no previous cell
prevCellCoords = grid.getPreviousCell(0, 2, (col) => col.editable);
expect(prevCellCoords).toEqual({ rowIndex: 0, visibleColumnIndex: 2 });
// when the filter function has no matching colums
prevCellCoords = grid.getPreviousCell(0, 3, (col) => col.pinned);
expect(prevCellCoords).toEqual({ rowIndex: 0, visibleColumnIndex: 3 });
// when grid has no data
grid.filter('col0', 2, IgxNumberFilteringOperand.instance().condition('greaterThan'));
fix.detectChanges();
prevCellCoords = grid.getPreviousCell(99, 0, (col) => col.editable);
expect(prevCellCoords).toEqual({ rowIndex: 99, visibleColumnIndex: 0 });
});
it('should not reset vertical scroll position when calling navigateTo with only rowIndex specified', async () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
fix.componentInstance.initColumnsRows(15, 5);
fix.detectChanges();
const grid = fix.componentInstance.grid;
grid.height = '300px';
grid.width = '500px';
fix.detectChanges();
grid.navigateTo(0);
await wait();
fix.detectChanges();
grid.verticalScrollContainer.getScroll().scrollTop = 200;
await wait();
fix.detectChanges();
expect(grid.verticalScrollContainer.getScroll().scrollTop).toEqual(200);
grid.headerContainer.getScroll().scrollLeft = 100;
await wait();
fix.detectChanges();
expect(grid.verticalScrollContainer.getScroll().scrollTop).toEqual(200);
});
it('should emit onScroll event when scrolling horizontally/vertically', async () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
fix.componentInstance.initColumnsRows(30, 10);
fix.detectChanges();
const grid = fix.componentInstance.grid;
grid.height = '300px';
grid.width = '300px';
fix.detectChanges();
spyOn(grid.gridScroll, 'emit').and.callThrough();
let verticalScrollEvent;
let horizontalScrollEvent;
grid.verticalScrollContainer.getScroll().addEventListener('scroll', (evt) => verticalScrollEvent = evt);
grid.headerContainer.getScroll().addEventListener('scroll', (evt) => horizontalScrollEvent = evt);
grid.navigateTo(20, 0);
fix.detectChanges();
await wait(100);
fix.detectChanges();
expect(grid.gridScroll.emit).toHaveBeenCalledTimes(1);
expect(grid.gridScroll.emit).toHaveBeenCalledWith({
direction: 'vertical',
scrollPosition: grid.verticalScrollContainer.getScrollForIndex(20, true),
event: verticalScrollEvent
});
grid.navigateTo(20, 6);
fix.detectChanges();
await wait(100);
fix.detectChanges();
expect(grid.gridScroll.emit).toHaveBeenCalledTimes(2);
expect(grid.gridScroll.emit).toHaveBeenCalledWith({
direction: 'horizontal',
scrollPosition: grid.headerContainer.getScrollForIndex(6, true),
event: horizontalScrollEvent
});
});
it(`Verify that getRowData returns correct data`, () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
fix.componentInstance.initColumnsRows(5, 5);
fix.detectChanges();
const grid = fix.componentInstance.grid;
const cols = fix.componentInstance.columns;
const row = { col0: 0, col1: 4, col2: 8, col3: 12, col4: 16 };
const secondRow = { col0: 0, col1: 1, col2: 2, col3: 3, col4: 4 };
expect(grid.getRowData(row)).toEqual(row);
grid.primaryKey = 'col1';
fix.detectChanges();
expect(grid.getRowData(4)).toEqual(row);
grid.filter(cols[1].key, 2, IgxNumberFilteringOperand.instance().condition('greaterThan'));
fix.detectChanges();
expect(grid.getRowData(4)).toEqual(row);
expect(grid.getRowData(1)).toEqual(secondRow);
expect(grid.getRowData(7)).toEqual({});
grid.sort({ fieldName: 'col2', dir: SortingDirection.Desc, ignoreCase: true });
fix.detectChanges();
expect(grid.getRowData(4)).toEqual(row);
expect(grid.getRowData(1)).toEqual(secondRow);
expect(grid.getRowData(7)).toEqual({});
});
it(`Verify that getRowByIndex and RowType API returns correct data`, () => {
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
fix.componentInstance.initColumnsRows(35, 5);
fix.detectChanges();
const grid = fix.componentInstance.grid;
const virtRowsLength = grid.dataRowList.length;
const indexToCompare = 32;
let firstRow = grid.getRowByIndex(0);
let secondRow = grid.getRowByIndex(1);
let thirdRow = grid.getRowByIndex(2);
expect(indexToCompare > virtRowsLength).toBe(true);
// Check if the comparable row is within the virt container
expect(grid.gridAPI.get_row_by_index(virtRowsLength - 1) instanceof IgxGridRowComponent).toBe(true);
// Check if the comparable row is outside the virt container
expect(grid.gridAPI.get_row_by_index(virtRowsLength + 1)).toEqual(undefined);
// Get row with the new getRowByIndex method
expect(grid.getRowByIndex(32) instanceof IgxGridRow).toBe(true);
// GroupBy column and get the collapsed grouped row
expect(firstRow instanceof IgxGridRow).toBe(true);
// index
expect(firstRow.index).toBe(0);
expect(firstRow.viewIndex).toBe(0);
expect(firstRow.parent).toBeUndefined();
fix.detectChanges();
grid.groupBy({ fieldName: 'col1', dir: SortingDirection.Asc });
fix.detectChanges();
firstRow = grid.getRowByIndex(0);
secondRow = grid.getRowByIndex(1);
thirdRow = grid.getRowByIndex(2);
// First row is IgxGroupByRow second row is igxGridRow
expect(firstRow instanceof IgxGroupByRow).toBe(true);
expect(secondRow instanceof IgxGridRow).toBe(true);
expect(secondRow.index).toBe(1);
expect(secondRow.viewIndex).toBe(1);
// expand/collapse first group row
firstRow.expanded = true;
fix.detectChanges();
firstRow = grid.getRowByIndex(0);
secondRow = grid.getRowByIndex(1);
thirdRow = grid.getRowByIndex(2);
expect(firstRow.expanded).toBe(true);
firstRow = grid.getRowByIndex(0);
secondRow = grid.getRowByIndex(1);
thirdRow = grid.getRowByIndex(2);
// index
expect(secondRow.index).toBe(1);
expect(secondRow.viewIndex).toBe(1);
// select group row
expect(firstRow.selected).toBeFalse();
expect(secondRow.selected).toBeFalse();
firstRow.children.forEach(row => {
expect(row.selected).toBeFalse();
});
firstRow.selected = !firstRow.selected;
expect(firstRow.selected).toBeTrue();
expect(secondRow.selected).toBeTrue();
firstRow.children.forEach(row => {
expect(row.selected).toBeTrue();
});
firstRow.selected = !firstRow.selected;
expect(firstRow.selected).toBeFalse();
expect(secondRow.selected).toBeFalse();
firstRow.children.forEach(row => {
expect(row.selected).toBeFalse();
});
(firstRow as IgxGroupByRow).toggle();
fix.detectChanges();
expect(firstRow.expanded).toBe(false);
firstRow = grid.getRowByIndex(0);
secondRow = grid.getRowByIndex(1);
thirdRow = grid.getRowByIndex(2);
// First row is still IgxGroupByRow and now the second row is as well IgxGroupByRow
expect(firstRow instanceof IgxGroupByRow).toBe(true);
expect(firstRow.key).toBeUndefined();
expect(secondRow instanceof IgxGroupByRow).toBe(true);
// Check hasChildren and other API members for igxGrid
expect(thirdRow.hasChildren).toBe(false);
expect(thirdRow.children).toBeUndefined();
expect(thirdRow.parent instanceof IgxGroupByRow).toBe(true);
expect(thirdRow.parent.parent).toBeUndefined();
expect(thirdRow.index).toEqual(2);
expect(secondRow.isSummaryRow).toBeUndefined();
// GroupByRow check
expect(thirdRow.isGroupByRow).toBeUndefined();
expect(secondRow.isGroupByRow).toBe(true);
expect(thirdRow.groupRow).toBeUndefined();
expect(secondRow.groupRow).toBeTruthy();
// key and rowData check - first with group row (index 1) and then with IgxGridRow (index 2)
expect(secondRow.key).toBeUndefined();
expect(secondRow.data).toBeUndefined();
expect(secondRow.pinned).toBeUndefined();
expect(secondRow.selected).toBeFalse();
expect(thirdRow.key).toBeTruthy();
expect(thirdRow.data).toBeTruthy();
expect(thirdRow.pinned).toBe(false);
expect(thirdRow.selected).toBe(false);
// Toggle selection
thirdRow.selected = true;
expect(thirdRow.selected).toBe(true);
fix.componentInstance.paging = true;
fix.detectChanges();
grid.perPage = 4;
grid.columnList.forEach(c => c.hasSummary = true);
fix.detectChanges();
firstRow = grid.getRowByIndex(0);
const fourthRow = grid.getRowByIndex(3);
expect(firstRow instanceof IgxGroupByRow).toBe(true);
expect(firstRow.index).toEqual(0);
expect(firstRow.viewIndex).toEqual(0);
expect(fourthRow instanceof IgxSummaryRow).toBe(true);
expect(fourthRow.index).toBe(3);
expect(fourthRow.viewIndex).toBe(3);
grid.page = 1;
grid.cdr.detectChanges();
fix.detectChanges();
firstRow = grid.getRowByIndex(0);
secondRow = grid.getRowByIndex(1);
thirdRow = grid.getRowByIndex(2);
expect(firstRow instanceof IgxGridRow).toBe(true);
expect(firstRow.index).toEqual(0);
expect(firstRow.viewIndex).toEqual(5);
expect(secondRow instanceof IgxSummaryRow).toBe(true);
expect(secondRow.index).toBe(1);
expect(secondRow.viewIndex).toBe(6);
expect(thirdRow instanceof IgxGroupByRow).toBe(true);
expect(thirdRow.index).toBe(2);
expect(thirdRow.viewIndex).toBe(7);
});
it('Verify that getRowByIndex returns correct data when paging is enabled', fakeAsync(() => {
const fix = TestBed.createComponent(IgxGridWrappedInContComponent);
const grid = fix.componentInstance.grid;
fix.componentInstance.data = fix.componentInstance.fullData;
fix.detectChanges();
tick(16);
fix.componentInstance.paging = true;
fix.detectChanges();
tick(16);
grid.notifyChanges(true);
fix.detectChanges();
// Compare the result returned by both get_row_by_index and getRowByIndex
expect(grid.gridAPI.get_row_by_index(fix.componentInstance.pageSize + 1)).toBeUndefined();
expect(grid.getRowByIndex(fix.componentInstance.pageSize - 1) instanceof IgxGridRow).toBe(true);
expect(grid.getRowByIndex(fix.componentInstance.pageSize) instanceof IgxGridRow).toBe(false);
// Change page and check getRowByIndex
grid.page = 1;
fix.detectChanges();
tick();
let firstRow = grid.getRowByIndex(0);
// Return the first row after page change
expect(firstRow instanceof IgxGridRow).toBe(true);
expect(firstRow.index).toBe(0);
expect(firstRow.viewIndex).toBe(5);
// Change page and check getRowByIndex
grid.page = 2;
fix.detectChanges();
tick();
firstRow = grid.getRowByIndex(0);
const secondRow = grid.getRowByIndex(1);
// Return the first row after page change
expect(firstRow instanceof IgxGridRow).toBe(true);
expect(firstRow.index).toBe(0);
expect(firstRow.viewIndex).toBe(10);
expect(secondRow.index).toBe(1);
expect(secondRow.viewIndex).toBe(11);
}));
});
describe('IgxGrid - Integration with other Igx Controls', () => {
let fix;
beforeAll(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
IgxGridInsideIgxTabsComponent
]
})
.compileComponents();
}));
beforeEach(waitForAsync(() => {
fix = TestBed.createComponent(IgxGridInsideIgxTabsComponent);
fix.detectChanges();
}));
it('IgxTabs: should initialize a grid with correct width/height', async () => {
const grid = fix.componentInstance.grid3;
const tab = fix.componentInstance.tabs;
expect(grid.calcHeight).toBe(510);
tab.items.toArray()[2].selected = true;
await wait(100);
fix.detectChanges();
await wait(100);
fix.detectChanges();
const gridHeader = fix.debugElement.query(By.css(THEAD_CLASS));
const headers = fix.debugElement.queryAll(By.css(COLUMN_HEADER_CLASS));
const gridBody = fix.debugElement.query(By.css(TBODY_CLASS));
expect(parseInt(window.getComputedStyle(gridHeader.nativeElement).width, 10)).toBe(600);
expect(headers.length).toBe(4);
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).width, 10)).toBe(600);
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(510);
});
it('IgxTabs: should initialize a grid with correct width/height when there is no column width set', async () => {
const grid = fix.componentInstance.grid2;
const tab = fix.componentInstance.tabs;
expect(grid.calcHeight).toBe(300);
tab.items.toArray()[1].selected = true;
await wait(100);
fix.detectChanges();
await wait(100);
fix.detectChanges();
await wait(100);
fix.detectChanges();
const headers = fix.debugElement.queryAll(By.css(COLUMN_HEADER_CLASS));
expect(headers.length).toBe(4);
const gridBody = fix.debugElement.query(By.css(TBODY_CLASS));
const expectedHeight = grid.nativeElement.offsetHeight
- grid.theadRow.nativeElement.offsetHeight
- grid.tfoot.nativeElement.offsetHeight
- (grid.isHorizontalScrollHidden ? 0 : grid.scrollSize);
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).width, 10) + grid.scrollSize).toBe(500);
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(expectedHeight);
});
it('IgxTabs: should initialize a grid with correct height when paging and summaries are enabled', async () => {
const grid = fix.componentInstance.grid4;
const tab = fix.componentInstance.tabs;
tab.items.toArray()[3].selected = true;
await wait(100);
fix.detectChanges();
await wait(100);
fix.detectChanges();
await wait(100);
fix.detectChanges();
const headers = fix.debugElement.queryAll(By.css(COLUMN_HEADER_CLASS));
const gridBody = fix.debugElement.query(By.css(TBODY_CLASS));
const paging = fix.debugElement.query(By.css('igx-page-nav'));
const summaries = fix.debugElement.queryAll(By.css('igx-grid-summary-cell'));
expect(headers.length).toBe(4);
expect(summaries.length).toBe(4);
const expectedHeight = grid.nativeElement.offsetHeight
- grid.theadRow.nativeElement.offsetHeight
- grid.tfoot.nativeElement.offsetHeight
- grid.footer.nativeElement.offsetHeight
- (grid.isHorizontalScrollHidden ? 0 : grid.scrollSize);
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(expectedHeight);
expect(parseInt(window.getComputedStyle(paging.nativeElement).height, 10)).toBe(36);
});
it('IgxTabs: should initialize a grid with correct height when height = 100%', async () => {
const grid = fix.componentInstance.grid5;
const tab = fix.componentInstance.tabs;
expect(grid.calcHeight).toBe(204);
tab.items.toArray()[4].selected = true;
await wait(100);
fix.detectChanges();
await wait(100);
grid.cdr.detectChanges();
const headers = fix.debugElement.queryAll(By.css(COLUMN_HEADER_CLASS));
const gridBody = fix.debugElement.query(By.css(TBODY_CLASS));
const paging = fix.debugElement.query(By.css('igx-page-nav'));
expect(headers.length).toBe(4);
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(204);
expect(parseInt(window.getComputedStyle(paging.nativeElement).height, 10)).toBe(36);
});
it('IgxTabs: should initialize a grid with correct height height = 100% when parent has height', async () => {
const grid = fix.componentInstance.grid6;
const tab = fix.componentInstance.tabs;
expect(grid.calcHeight).toBe(510);
tab.items.toArray()[5].selected = true;
await wait(100);
fix.detectChanges();
await wait(100);
fix.detectChanges();
await wait(100);
fix.detectChanges();
const gridBody = fix.debugElement.query(By.css(TBODY_CLASS));
const expectedHeight = grid.nativeElement.offsetHeight
- grid.theadRow.nativeElement.offsetHeight
- grid.tfoot.nativeElement.offsetHeight
- (grid.isHorizontalScrollHidden ? 0 : grid.scrollSize);
expect(grid.calcHeight).toBe(expectedHeight);
expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(expectedHeight);
expect(parseInt(window.getComputedStyle(grid.nativeElement).height, 10)).toBe(300);
});
it('IgxTabs: should persist scroll position after changing tabs.', async () => {
const grid = fix.componentInstance.grid2;
fix.detectChanges();
const tab = fix.componentInstance.tabs;
tab.items.toArray()[1].selected = true;
await wait(100);
fix.detectChanges();
await wait(100);
fix.detectChanges();
grid.navigateTo(0, grid.columnList.length - 1);
await wait(100);
fix.detectChanges();
grid.navigateTo(grid.data.length - 1);
await wait(100);
fix.detectChanges();
const scrTop = grid.verticalScrollContainer.getScroll().scrollTop;
const scrLeft = grid.dataRowList.first.virtDirRow.getScroll().scrollLeft;
expect(scrTop).not.toBe(0);
expect(scrLeft).not.toBe(0);
tab.items.toArray()[0].selected = true;
await wait(100);
fix.detectChanges();
tab.items.toArray()[1].selected = true;
await wait(100);
fix.detectChanges();
await wait(100);
// check scrollTop/scrollLeft was persisted.
expect(grid.verticalScrollContainer.getScroll().scrollTop).toBe(scrTop);
expect(grid.dataRowList.first.virtDirRow.getScroll().scrollLeft).toBe(scrLeft);
});
});
describe('IgxGrid - footer section', () => {
beforeAll(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
IgxGridWithCustomFooterComponent
]
})
.compileComponents();
}));
it('should be able to display custom content', () => {
const fix = TestBed.createComponent(IgxGridWithCustomFooterComponent);
fix.detectChanges();
const footer = fix.debugElement.query(By.css('igx-grid-footer')).nativeElement;
const footerContent = footer.textContent.trim();
expect(footerContent).toEqual('Custom content');
const grid = fix.componentInstance.grid;
const expectedHeight = parseInt(grid.height, 10) - grid.theadRow.nativeElement.offsetHeight - grid.scrollSize - 100;
expect(expectedHeight - grid.calcHeight).toBeLessThanOrEqual(1);
});
});
describe('IgxGrid - with custom pagination template', () => {
beforeAll(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
IgxGridWithCustomPaginationTemplateComponent
]
})
.compileComponents();
}));
it('should have access to grid context', fakeAsync(() => {
const fix = TestBed.createComponent(IgxGridWithCustomPaginationTemplateComponent);
tick();
fix.detectChanges();
flush();
fix.detectChanges();
const totalRecords = fix.componentInstance.grid.totalRecords.toString();
const paginationContent = fix.debugElement.query(By.css('.records')).nativeElement;
const paginationText = paginationContent.textContent.trim();
expect(paginationText).toEqual(totalRecords);
}));
});
// TODO: Enable performance tests again
xdescribe('IgxGrid - Performance tests #perf', () => {
const MAX_RAW_RENDER = 1967; // two average diffs from 7.3 rendering performance
const MAX_GROUPED_RENDER = 1500;
const MAX_VER_SCROLL_O = 220;
const MAX_HOR_SCROLL_O = 220;
const MAX_VER_SCROLL_U = 380;
const MAX_HOR_SCROLL_U = 380;
const MAX_FOCUS = 120;
let observer: MutationObserver;
beforeAll(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
IgxGridPerformanceComponent
]
})
.compileComponents();
}));
afterEach(() => {
observer?.disconnect();
});
it('should render the grid in a certain amount of time', async () => {
const fix = TestBed.createComponent(IgxGridPerformanceComponent);
fix.detectChanges();
expect(fix.componentInstance.delta)
.withContext('Rendering took: ' + fix.componentInstance.delta +
'ms but should have taken at most: ' + MAX_RAW_RENDER + 'ms')
.toBeLessThan(MAX_RAW_RENDER);
});
it('should render grouped grid in a certain amount of time', async () => {
const fix = TestBed.createComponent(IgxGridPerformanceComponent);
fix.componentInstance.groupingExpressions.push({
fieldName: 'field0',
dir: SortingDirection.Asc
});
fix.detectChanges();
expect(fix.componentInstance.delta)
.withContext('Rendering took: ' + fix.componentInstance.delta +
'ms but should have taken at most: ' + MAX_GROUPED_RENDER + 'ms')
.toBeLessThan(MAX_GROUPED_RENDER);
});
xit('should scroll (optimized delta) the grid vertically in a certain amount of time', async (done) => {
const fix = TestBed.createComponent(IgxGridPerformanceComponent);
fix.detectChanges();
await wait(16);
const startTime = new Date().getTime();
const config: MutationObserverInit = {
attributes: true,
attributeOldValue: true,
attributeFilter: ['ng-reflect-value']
};
const callback = () => {
let ready = true;
const rows = fix.componentInstance.grid.rowList.toArray();
for (let i = 0; i < 4; i++) {
if (rows[i].cells.first.nativeElement.attributes['ng-reflect-value'].nodeValue !== String(i + 3)) {
ready = false;
break;
}
}
if (ready) {
const delta = new Date().getTime() - startTime;
expect(delta)
.withContext('Scrolling took: ' + delta + 'ms but should have taken at most: ' + MAX_VER_SCROLL_O + 'ms')
.toBeLessThan(MAX_VER_SCROLL_O);
observer.disconnect();
done();
}
};
observer = new MutationObserver(callback);
observer.observe(fix.componentInstance.grid.rowList.first.cells.first.nativeElement, config);
fix.componentInstance.verticalScroll.scrollTop = 120;
await wait(100);
fix.detectChanges();
});
xit('should scroll (unoptimized delta) the grid vertically in a certain amount of time', async (done) => {
const fix = TestBed.createComponent(IgxGridPerformanceComponent);
fix.detectChanges();
await wait(16);
const startTime = new Date().getTime();
const config: MutationObserverInit = {
attributes: true,
attributeOldValue: true,
attributeFilter: ['ng-reflect-value']
};
const callback = (mutationsList) => {
const cellMutated = mutationsList.filter(mutation =>
mutation.oldValue === '60' && mutation.target.attributes['ng-reflect-value'].nodeValue === '84').length === 1;
if (cellMutated) {
const delta = new Date().getTime() - startTime;
expect(delta)
.withContext('Scrolling took: ' + delta + 'ms but should have taken at most: ' + MAX_VER_SCROLL_U + 'ms')
.toBeLessThan(MAX_VER_SCROLL_U);
observer.disconnect();
done();
}
};
observer = new MutationObserver(callback);
observer.observe(fix.componentInstance.grid.rowList.last.cells.first.nativeElement, config);
fix.componentInstance.verticalScroll.scrollTop = 800;
await wait(100);
fix.detectChanges();
});
xit('should scroll (optimized delta) the grid horizontally in a certain amount of time', async (done) => {
const fix = TestBed.createComponent(IgxGridPerformanceComponent);
fix.detectChanges();
await wait(16);
const startTime = new Date().getTime();
const config: MutationObserverInit = {
attributes: true,
attributeOldValue: true,
attributeFilter: ['ng-reflect-value']
};
const callback = mutationsList => {
const cellMutated = mutationsList.filter(mutation =>
mutation.oldValue === '1' && mutation.target.attributes['ng-reflect-value'].nodeValue === '22').length === 1;
if (cellMutated) {
const delta = new Date().getTime() - startTime;
expect(delta)
.withContext('Scrolling took: ' + delta + 'ms but should have taken at most: ' + MAX_HOR_SCROLL_O + 'ms')
.toBeLessThan(MAX_HOR_SCROLL_O);
observer.disconnect();
done();
}
};
observer = new MutationObserver(callback);
observer.observe(fix.componentInstance.grid.rowList.last.cells.toArray()[1].nativeElement, config);
fix.componentInstance.horizontalScroll.scrollLeft = 250;
await wait(100);
fix.detectChanges();
});
xit('should scroll (unoptimized delta) the grid horizontally in a certain amount of time', async (done) => {
const fix = TestBed.createComponent(IgxGridPerformanceComponent);
fix.detectChanges();
await wait(16);
const startTime = new Date().getTime();
const config: MutationObserverInit = {
attributes: true,
attributeOldValue: true,
attributeFilter: ['ng-reflect-value']
};
const callback = mutationsList => {
const cellMutated = mutationsList.filter(mutation =>
mutation.oldValue === '60' && mutation.target.attributes['ng-reflect-value'].nodeValue === '8').length === 1;
if (cellMutated) {
const delta = new Date().getTime() - startTime;
expect(delta)
.withContext('Scrolling took: ' + delta + 'ms but should have taken at most: ' + MAX_HOR_SCROLL_U + 'ms')
.toBeLessThan(MAX_HOR_SCROLL_U);
observer.disconnect();
done();
}
};
observer = new MutationObserver(callback);
observer.observe(fix.componentInstance.grid.rowList.last.cells.first.nativeElement, config);
fix.componentInstance.horizontalScroll.scrollLeft = 800;
await wait(100);
fix.detectChanges();
});
xit('should focus a cell in a certain amount of time', async (done) => {
const fix = TestBed.createComponent(IgxGridPerformanceComponent);
fix.detectChanges();
await wait(16);
const startTime = new Date().getTime();
const config: MutationObserverInit = {
attributes: true,
attributeOldValue: true,
attributeFilter: ['aria-selected']
};
const callback = mutationsList => {
const cellMutated = mutationsList.filter(mutation =>
mutation.oldValue === 'false' && mutation.target.attributes['aria-selected'].nodeValue === 'true').length === 1;
if (cellMutated) {
const delta = new Date().getTime() - startTime;
expect(delta)
.withContext('Focusing took: ' + delta + 'ms but should have taken at most: ' + MAX_FOCUS + 'ms')
.toBeLessThan(MAX_FOCUS);
observer.disconnect();
done();
}
};
observer = new MutationObserver(callback);
observer.observe(fix.componentInstance.grid.rowList.first.cells.first.nativeElement, config);
// UIInteractions.simulateClickAndSelectEvent(fix.componentInstance.grid.rowList.first.cells.first.nativeElement);
await wait(16);
fix.detectChanges();
});
});
describe('Setting null data', () => {
beforeAll(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
IgxGridNoDataComponent,
IgxGridTestComponent
]
})
.compileComponents();
}));
it('should not throw error when data is null', () => {
const fix = TestBed.createComponent(IgxGridNoDataComponent);
fix.componentInstance.grid.batchEditing = true;
expect(() => fix.detectChanges()).not.toThrow();
});
it('should not throw error when data is set to null', () => {
const fix = TestBed.createComponent(IgxGridTestComponent);
fix.componentInstance.data = null;
expect(() => fix.detectChanges()).not.toThrow();
});
it('should not throw error when data is set to null and transactions are enabled', () => {
const fix = TestBed.createComponent(IgxGridTestComponent);
fix.componentInstance.grid.batchEditing = true;
fix.componentInstance.data = null;
expect(() => fix.detectChanges()).not.toThrow();
});
});
});
@Component({
template: `<div style="width: 800px; height: 600px;">
<igx-grid #grid [data]="data" [autoGenerate]="autoGenerate" [autoGenerateExclude]="autoGenerateExclude" (columnInit)="columnCreated($event)">
<igx-column *ngFor="let column of columns;" [field]="column.field" [hasSummary]="column.hasSummary"
[header]="column.field" [width]="column.width">
</igx-column>
</igx-grid>
</div>`,
standalone: true,
imports: [IgxGridComponent, IgxColumnComponent, NgFor]
})
export class IgxGridTestComponent {
@ViewChild('grid', { static: true }) public grid: IgxGridComponent;
public data: any[] = [{ index: 1, value: 1 }];
public columns = [
{ field: 'index', header: 'index', dataType: 'number', width: null, hasSummary: false },
{ field: 'value', header: 'value', dataType: 'number', width: null, hasSummary: false }
];
public autoGenerate = false;
public autoGenerateExclude = [];
public columnEventCount = 0;
constructor(public cdr: ChangeDetectorRef) { }
public columnCreated(column: IgxColumnComponent) {
this.columnEventCount++;
column.filterable = true;
column.sortable = true;
}
public isHorizontalScrollbarVisible() {
const scrollbar = this.grid.headerContainer.getScroll();
if (scrollbar) {
return scrollbar.offsetWidth < (scrollbar.children.item(0) as HTMLElement).offsetWidth;
}
return false;
}
public getVerticalScrollHeight() {
const scrollbar = this.grid.verticalScrollContainer.getScroll();
if (scrollbar) {
return parseInt(scrollbar.style.height, 10);
}
return 0;
}
public isVerticalScrollbarVisible() {
const scrollbar = this.grid.verticalScrollContainer.getScroll();
if (scrollbar && scrollbar.offsetHeight > 0) {
return scrollbar.offsetHeight < (scrollbar.children.item(0) as HTMLElement).offsetHeight;
}
return false;
}
public generateData(rows) {
const d = [];
for (let r = 0; r < rows; r++) {
const record = {};
for (let c = 0; c < this.columns.length; c++) {
record[this.columns[c].field] = c * r;
}
d.push(record);
}
this.data = d;
}
public clearData() {
this.data = [];
}
}
@Component({
template: `<igx-grid #grid [data]="data" (columnInit)="initColumns($event)">
<igx-column *ngFor="let col of columns" [field]="col.key" [header]="col.key" [dataType]="col.dataType">
</igx-column>
<igx-paginator *ngIf="paging"></igx-paginator>
</igx-grid>`,
standalone: true,
imports: [IgxGridComponent, IgxColumnComponent, IgxPaginatorComponent, NgFor, NgIf]
})
export class IgxGridDefaultRenderingComponent {
@ViewChild('grid', { read: IgxGridComponent, static: true })
public grid: IgxGridComponent;
public columns = [];
public data = [];
public paging = false;
public changeInitColumns = false;
public initColumnsRows(rowsNumber: number, columnsNumber: number): void {
this.columns = [];
this.data = [];
let i; let j: number;
for (i = 0; i < columnsNumber; i++) {
this.columns.push({
key: 'col' + i,
dataType: 'number'
});
}
for (i = 0; i < rowsNumber; i++) {
const record = {};
for (j = 0; j < columnsNumber; j++) {
record[this.columns[j].key] = j * i;
}
this.data.push(record);
}
}
public isHorizonatScrollbarVisible() {
const scrollbar = this.grid.headerContainer.getScroll();
return scrollbar.offsetWidth < (scrollbar.children.item(0) as HTMLElement).offsetWidth;
}
public initColumns(column) {
if (this.changeInitColumns) {
switch (this.grid.columnList.length) {
case 5:
if (column.index === 0 || column.index === 4) {
column.width = '100px';
}
break;
case 30:
if (column.index === 0 || column.index === 5 || column.index === 3 || column.index === 10 || column.index === 25) {
column.width = '200px';
}
break;
case 150:
if (column.index === 0 || column.index === 5 || column.index === 3 || column.index === 10 || column.index === 50) {
column.width = '500px';
}
break;
}
}
}
}
@Component({
template: `<igx-grid #grid [data]="data" [width]="'500px'" (columnInit)="initColumns($event)">
<igx-column *ngFor="let col of columns" [field]="col.key" [header]="col.key" [dataType]="col.dataType">
</igx-column>
</igx-grid>`,
standalone: true,
imports: [IgxGridComponent, IgxColumnComponent, NgFor]
})
export class IgxGridColumnPercentageWidthComponent extends IgxGridDefaultRenderingComponent {
public override initColumns(column) {
if (column.index === 0) {
column.width = '40%';
}
}
}
@Component({
template: `<div>
<igx-grid #grid [data]="data" height='300px' [displayDensity]="'compact'" [autoGenerate]="true"
>
<igx-grid-footer>
<div style='height:100px;'>
Custom content
</div>
</igx-grid-footer>
</igx-grid>
</div>`,
standalone: true,
imports: [IgxGridComponent, IgxGridFooterComponent]
})
export class IgxGridWithCustomFooterComponent extends IgxGridTestComponent {
@ViewChild(IgxGridComponent, { static: true }) public override grid: IgxGridComponent;
public override data = SampleTestData.foodProductData();
}
@Component({
template: `<div [style.width.px]="outerWidth" [style.height.px]="outerHeight">
<igx-grid #grid [data]="data" [displayDensity]="density" [autoGenerate]="true">
<igx-paginator *ngIf="paging" [perPage]="pageSize"></igx-paginator>
</igx-grid>
</div>`,
standalone: true,
imports: [IgxGridComponent, IgxPaginatorComponent, NgIf]
})
export class IgxGridWrappedInContComponent extends IgxGridTestComponent {
public override data = [];
public fullData = [
{ ID: 'ALFKI', CompanyName: 'Alfreds Futterkiste' },
{ ID: 'ANATR', CompanyName: 'Ana Trujillo Emparedados y helados' },
{ ID: 'ANTON', CompanyName: 'Antonio Moreno Taquería' },
{ ID: 'AROUT', CompanyName: 'Around the Horn' },
{ ID: 'BERGS', CompanyName: 'Berglunds snabbköp' },
{ ID: 'BLAUS', CompanyName: 'Blauer See Delikatessen' },
{ ID: 'BLONP', CompanyName: 'Blondesddsl père et fils' },
{ ID: 'BOLID', CompanyName: 'Bólido Comidas preparadas' },
{ ID: 'BONAP', CompanyName: 'Bon app\'' },
{ ID: 'BOTTM', CompanyName: 'Bottom-Dollar Markets' },
{ ID: 'BSBEV', CompanyName: 'B\'s Beverages' },
{ ID: 'CACTU', CompanyName: 'Cactus Comidas para llevar' },
{ ID: 'CENTC', CompanyName: 'Centro comercial Moctezuma' },
{ ID: 'CHOPS', CompanyName: 'Chop-suey Chinese' },
{ ID: 'COMMI', CompanyName: 'Comércio Mineiro' },
{ ID: 'CONSH', CompanyName: 'Consolidated Holdings' },
{ ID: 'DRACD', CompanyName: 'Drachenblut Delikatessen' },
{ ID: 'DUMON', CompanyName: 'Du monde entier' },
{ ID: 'EASTC', CompanyName: 'Eastern Connection' },
{ ID: 'ERNSH', CompanyName: 'Ernst Handel' },
{ ID: 'FAMIA', CompanyName: 'Familia Arquibaldo' },
{ ID: 'FISSA', CompanyName: 'FISSA Fabrica Inter' },
{ ID: 'FOLIG', CompanyName: 'Folies gourmandes' },
{ ID: 'FOLKO', CompanyName: 'Folk och fä HB' },
{ ID: 'FRANK', CompanyName: 'Frankenversand' },
{ ID: 'FRANR', CompanyName: 'France restauration' },
{ ID: 'FRANS', CompanyName: 'Franchi S.p.A.' }
];
public get semiData(): any[] {
return this.fullData.slice(0, 5);
}
public height = null;
public paging = false;
public pageSize = 5;
public density: DisplayDensity = DisplayDensity.comfortable;
public outerWidth = 800;
public outerHeight: number;
}
@Component({
template: `<div style="height:300px">
<igx-grid #grid [data]="data" [displayDensity]="density" [autoGenerate]="true">
<igx-paginator *ngIf="paging" [perPage]="pageSize"></igx-paginator>
</igx-grid>
</div>`,
standalone: true,
imports: [IgxGridComponent]
})
export class IgxGridFixedContainerHeightComponent extends IgxGridWrappedInContComponent {
public override paging = false;
public override pageSize = 5;
public override density: DisplayDensity = DisplayDensity.comfortable;
}
@Component({
template: `
<igx-grid [data]="data" (columnInit)="columnCreated($event)">
<igx-column field="ID"></igx-column>
<igx-column field="Name"></igx-column>
</igx-grid>
`,
standalone: true,
imports: [IgxGridComponent, IgxColumnComponent]
})
export class IgxGridMarkupDeclarationComponent extends IgxGridTestComponent {
@ViewChild(IgxGridComponent, { read: IgxGridComponent, static: true })
public instance: IgxGridComponent;
public override data = [
{ ID: 1, Name: 'Johny' },
{ ID: 2, Name: 'Sally' },
{ ID: 3, Name: 'Tim' }
];
}
@Component({
template: `<div>
<igx-grid [data]="data" (columnInit)="columnCreated($event)">
<igx-column field="ID"></igx-column>
<igx-column field="Name"></igx-column>
</igx-grid>
</div>
`,
standalone: true,
imports: [IgxGridComponent, IgxColumnComponent]
})
export class IgxGridEmptyMessage100PercentComponent extends IgxGridTestComponent {
@ViewChild(IgxGridComponent, { read: IgxGridComponent, static: true })
public override grid: IgxGridComponent;
public override data = [];
}
@Injectable()
export class LocalService {
public records: Observable<any[]>;
private _records: BehaviorSubject<any[]>;
private dataStore: any[];
constructor() {
this.dataStore = [];
this._records = new BehaviorSubject([]);
this.records = this._records.asObservable();
}
public nullData() {
this._records.next(null);
}
public getData(data?: IForOfState, cb?: (any) => void): any {
const size = data.chunkSize === 0 ? 10 : data.chunkSize;
this.dataStore = this.generateData(data.startIndex, data.startIndex + size);
this._records.next(this.dataStore);
const count = 1000;
if (cb) {
cb(count);
}
}
public generateData(start, end) {
const dummyData = [];
for (let i = start; i < end; i++) {
dummyData.push({ Col1: 10 * i });
}
return dummyData;
}
}
@Component({
template: `
<igx-grid [data]="data | async" (dataPreLoad)="dataLoading($event)" [height]="'600px'">
<igx-column [sortable]="true" [filterable]="true" [field]="'Col1'" [header]="'Col1'">
</igx-column>
</igx-grid>
`,
providers: [LocalService],
standalone: true,
imports: [IgxGridComponent, IgxColumnComponent, AsyncPipe]
})
export class IgxGridRemoteVirtualizationComponent implements OnInit, AfterViewInit {
@ViewChild(IgxGridComponent, { read: IgxGridComponent, static: true })
public instance: IgxGridComponent;
public data;
constructor(private localService: LocalService, public cdr: ChangeDetectorRef) { }
public ngOnInit(): void {
this.data = this.localService.records;
}
public nullData() {
this.localService.nullData();
}
public ngAfterViewInit() {
this.localService.getData(this.instance.virtualizationState, (count) => {
this.instance.totalItemCount = count;
this.cdr.detectChanges();
});
}
public dataLoading(evt) {
this.localService.getData(evt, () => {
this.cdr.detectChanges();
});
}
}
@Component({
template: `
<igx-grid [data]="data | async" (dataPreLoad)="dataLoading($event)" [isLoading]="true" [autoGenerate]="true" [height]="'600px'">
</igx-grid>
<ng-template #customTemplate>
<span>Loading...</span>
</ng-template>
`,
providers: [LocalService],
standalone: true,
imports: [IgxGridComponent, AsyncPipe]
})
export class IgxGridRemoteOnDemandComponent {
@ViewChild(IgxGridComponent, { read: IgxGridComponent, static: true })
public instance: IgxGridComponent;
@ViewChild('customTemplate', { read: TemplateRef, static: true })
public customTemaplate: TemplateRef<any>;
public data;
constructor(private localService: LocalService, public cdr: ChangeDetectorRef) { }
public bind() {
this.data = this.localService.records;
this.localService.getData(this.instance.virtualizationState, (count) => {
this.instance.totalItemCount = count;
this.cdr.detectChanges();
});
}
public dataLoading(evt) {
this.localService.getData(evt, () => {
this.cdr.detectChanges();
});
}
}
@Component({
template: GridTemplateStrings.declareGrid('', '', `<igx-column field="ProductID" header="Product ID">
</igx-column>
<igx-column field="ProductName">
</igx-column>
<igx-column field="InStock" [dataType]="'boolean'">
</igx-column>
<igx-column field="UnitsInStock" [dataType]="'number'" [hasSummary]="true">
</igx-column>
<igx-column field="OrderDate" width="200px" [dataType]="'date'" [hasSummary]="true">
</igx-column><igx-column field="UnitsInStock" [formatter]="formatNum" [dataType]="'number'" [hasSummary]="true">
</igx-column>`),
standalone: true,
imports: [IgxGridComponent, IgxColumnComponent]
})
export class IgxGridFormattingComponent extends BasicGridComponent {
@ViewChild(IgxGridComponent, { static: true }) public override grid: IgxGridComponent;
public override data = SampleTestData.foodProductData();
public width = '600px';
public height = '400px';
public value: any;
public formatNum(value) {
return value.toExponential().toString();
}
}
@Component({
template: `
<div style="width: 600px; height: 400px;">
<igx-tabs #tabs>
<igx-tab-item>
<igx-tab-header>
<span igxTabHeaderLabel>Tab 1</span>
</igx-tab-header>
<igx-tab-content>This is Tab 1 content.</igx-tab-content>
</igx-tab-item>
<igx-tab-item>
<igx-tab-header>
<span igxTabHeaderLabel>Tab 2</span>
</igx-tab-header>
<igx-tab-content>
<igx-grid #grid2 [data]="data" [primaryKey]="'id'" [width]="'500px'" [height]="'300px'">
<igx-column
*ngFor="let column of columns"
[field]="column.field"
[header]="column.field"
>
</igx-column>
</igx-grid>
</igx-tab-content>
</igx-tab-item>
<igx-tab-item>
<igx-tab-header>
<span igxTabHeaderLabel>Tab 3</span>
</igx-tab-header>
<igx-tab-content>
<igx-grid #grid3 [data]="data" [primaryKey]="'id'">
<igx-column
*ngFor="let column of columns"
[field]="column.field"
[header]="column.field"
[width]="column.width + 'px'"
>
</igx-column>
</igx-grid>
</igx-tab-content>
</igx-tab-item>
<igx-tab-item>
<igx-tab-header>
<span igxTabHeaderLabel>Tab 4</span>
</igx-tab-header>
<igx-tab-content>
<igx-grid #grid4 [data]="data" [primaryKey]="'id'" [width]="'500px'" [height]="'300px'">
<igx-column
*ngFor="let column of columns"
[field]="column.field"
[header]="column.field"
[hasSummary]="true"
>
</igx-column>
<igx-paginator [perPage]="3"></igx-paginator>
</igx-grid>
</igx-tab-content>
</igx-tab-item>
<igx-tab-item>
<igx-tab-header>
<span igxTabHeaderLabel>Tab 5</span>
</igx-tab-header>
<igx-tab-content>
<igx-grid #grid5 [data]="data" [primaryKey]="'id'" [width]="'500px'" [height]="'100%'">
<igx-column
*ngFor="let column of columns"
[field]="column.field"
[header]="column.field"
>
</igx-column>
<igx-paginator [perPage]="4"></igx-paginator>
</igx-grid>
</igx-tab-content>
</igx-tab-item>
<igx-tab-item>
<igx-tab-header>
<span igxTabHeaderLabel>Tab 6</span>
</igx-tab-header>
<igx-tab-content>
<div style='height:300px;'>
<igx-grid #grid6 [data]="data" [primaryKey]="'id'" [width]="'500px'" [height]="'100%'">
<igx-column
*ngFor="let column of columns"
[field]="column.field"
[header]="column.field"
>
</igx-column>
</igx-grid>
</div>
</igx-tab-content>
</igx-tab-item>
</igx-tabs>
</div>
`,
standalone: true,
imports: [IgxGridComponent, IgxColumnComponent, IgxTabsComponent, IgxTabHeaderComponent, IgxTabContentComponent, IgxTabItemComponent, IgxPaginatorComponent, NgFor]
})
export class IgxGridInsideIgxTabsComponent {
@ViewChild('grid2', { read: IgxGridComponent, static: true })
public grid2: IgxGridComponent;
@ViewChild('grid3', { read: IgxGridComponent, static: true })
public grid3: IgxGridComponent;
@ViewChild('grid4', { read: IgxGridComponent, static: true })
public grid4: IgxGridComponent;
@ViewChild('grid5', { read: IgxGridComponent, static: true })
public grid5: IgxGridComponent;
@ViewChild('grid6', { read: IgxGridComponent, static: true })
public grid6: IgxGridComponent;
@ViewChild(IgxTabsComponent, { read: IgxTabsComponent, static: true })
public tabs: IgxTabsComponent;
public columns = [
{ field: 'id', width: 100 },
{ field: '1', width: 100 },
{ field: '2', width: 100 },
{ field: '3', width: 100 }
];
public data = [];
constructor() {
const data = [];
for (let j = 1; j <= 10; j++) {
const item = {};
item['id'] = j;
for (let k = 2, len = this.columns.length; k <= len; k++) {
const field = this.columns[k - 1].field;
item[field] = `item${j}-${k}`;
}
data.push(item);
}
this.data = data;
}
}
@Component({
template: `
<igx-grid #grid [data]="data" [autoGenerate]="true">
<igx-paginator>
<igx-paginator-content>
<h2 *ngIf="grid.rendered$ | async" class='records'>{{grid.totalRecords}}</h2>
</igx-paginator-content>
</igx-paginator>
</igx-grid>
`,
standalone: true,
imports: [IgxGridComponent, IgxColumnComponent, IgxPaginatorComponent, IgxPaginatorContentDirective, NgIf, AsyncPipe]
})
export class IgxGridWithCustomPaginationTemplateComponent {
@ViewChild('grid', { read: IgxGridComponent, static: true })
public grid: IgxGridComponent;
public data = SampleTestData.foodProductData();
}
@Component({
template: `<igx-grid #grid [width]="'2000px'" [height]="'2000px'" [data]="data"
[autoGenerate]="autoGenerate" [displayDensity]="'compact'" [groupingExpressions]="groupingExpressions">
<igx-column *ngFor="let column of columns" [field]="column.field" [header]="column.field" [width]="column.width"></igx-column>
</igx-grid>`,
standalone: true,
imports: [IgxGridComponent, IgxColumnComponent, NgFor]
})
export class IgxGridPerformanceComponent implements AfterViewInit, OnInit {
@ViewChild('grid', { read: IgxGridComponent, static: true }) public grid: IgxGridComponent;
public columns = [];
public data = [];
public startTime;
public delta;
public groupingExpressions: Array<ISortingExpression> = [];
public autoGenerate = false;
public get verticalScroll() {
return this.grid.verticalScrollContainer.getScroll();
}
public get horizontalScroll() {
return this.grid.headerContainer.getScroll();
}
public ngOnInit() {
const cols = []; const d = [];
for (let i = 0; i < 30; i++) {
cols.push({ field: 'field' + i, width: '100px', hasSummary: false });
}
for (let i = 0; i < 10000; i++) {
const r = {};
r['field0'] = i;
for (let j = 1; j < 30; j++) {
r['field' + j] = j;
}
d.push(r);
}
this.columns = cols;
this.data = d;
this.startTime = new Date().getTime();
}
public ngAfterViewInit() {
this.delta = new Date().getTime() - this.startTime;
}
}
@Component({
template: `
<igx-grid>
<igx-column field="ID"></igx-column>
<igx-column field="Name" [hasSummary]="true"></igx-column>
<igx-paginator></igx-paginator>
</igx-grid>
`,
standalone: true,
imports: [IgxGridComponent, IgxColumnComponent, IgxPaginatorComponent]
})
export class IgxGridNoDataComponent {
@ViewChild(IgxGridComponent, { static: true }) public grid: IgxGridComponent;
}