igniteui-angular-sovn
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
1,152 lines (973 loc) • 170 kB
text/typescript
import { Component, ViewChild, TemplateRef, QueryList } from '@angular/core';
import { formatNumber } from '@angular/common'
import { fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { IgxStringFilteringOperand } from '../../data-operations/filtering-condition';
import { IgxColumnComponent } from '../columns/column.component';
import { IgxGridComponent } from './grid.component';
import { IgxGroupAreaDropDirective, IgxGroupByRowTemplateDirective, IgxHeaderCollapsedIndicatorDirective, IgxHeaderExpandedIndicatorDirective, IgxRowCollapsedIndicatorDirective, IgxRowExpandedIndicatorDirective } from './grid.directives';
import { IgxColumnMovingDragDirective } from '../moving/moving.drag.directive';
import { IgxGridRowComponent } from './grid-row.component';
import { IgxChipComponent } from '../../chips/chip.component';
import { wait, UIInteractions } from '../../test-utils/ui-interactions.spec';
import { DefaultSortingStrategy, ISortingExpression, SortingDirection } from '../../data-operations/sorting-strategy';
import { configureTestSuite } from '../../test-utils/configure-suite';
import { DataParent, SampleTestData } from '../../test-utils/sample-test-data.spec';
import { MultiColumnHeadersWithGroupingComponent } from '../../test-utils/grid-samples.spec';
import { GridSelectionFunctions, GridFunctions, GRID_SCROLL_CLASS } from '../../test-utils/grid-functions.spec';
import { GridSelectionMode } from '../common/enums';
import { ControlsFunction } from '../../test-utils/controls-functions.spec';
import { IGroupingExpression } from '../../data-operations/grouping-expression.interface';
import { IgxPaginatorComponent } from '../../paginator/paginator.component';
import { NgFor, NgIf } from '@angular/common';
import { IgxCheckboxComponent } from '../../checkbox/checkbox.component';
import { IgxGroupByRowSelectorDirective } from '../selection/row-selectors';
import { IgxGrouping } from '../public_api';
describe('IgxGrid - GroupBy #grid', () => {
const COLUMN_HEADER_CLASS = '.igx-grid-th';
const COLUMN_HEADER_GROUP_CLASS = '.igx-grid-thead__item';
const GRID_RESIZE_CLASS = '.igx-grid-th__resize-line';
const SORTING_ICON_ASC_CONTENT = 'arrow_upward';
const DISABLED_CHIP = 'igx-chip--disabled';
const CHIP = 'igx-chip';
configureTestSuite((() => {
return TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
DefaultGridComponent,
GroupableGridComponent,
CustomTemplateGridComponent,
GroupByDataMoreColumnsComponent,
GroupByEmptyColumnFieldComponent,
GridGroupByRowCustomSelectorsComponent,
GridGroupByCaseSensitiveComponent,
GridGroupByTestDateTimeDataComponent,
MultiColumnHeadersWithGroupingComponent
]
});
}));
const checkGroups = (groupRows, expectedGroupOrder, grExpr?) => {
// verify group rows are sorted correctly, their indexes in the grid are correct and their group records match the group value.
let count = 0;
const maxLevel = grExpr ? grExpr.length - 1 : 0;
for (const groupRow of groupRows) {
const recs = groupRow.groupRow.records;
const val = groupRow.groupRow.value;
const index = groupRow.index;
const field = groupRow.groupRow.expression.fieldName;
const level = groupRow.groupRow.level;
expect(level).toEqual(grExpr ? grExpr.indexOf(groupRow.groupRow.expression) : 0);
expect(index).toEqual(count);
count++;
expect(val).toEqual(expectedGroupOrder[groupRows.indexOf(groupRow)]);
for (const rec of recs) {
if (level === maxLevel) {
count++;
}
if (groupRow.groupRow.expression.ignoreCase) {
expect(rec[field]?.toString().toLowerCase()).toEqual(val?.toString().toLowerCase());
} else {
expect(rec[field]).toEqual(val);
}
}
}
};
const checkChips = (chips: QueryList<IgxChipComponent>, grouping: IGroupingExpression[]) => {
chips.forEach((chip, index) => {
const content = chip.nativeElement.querySelector('.igx-chip__content').textContent.trim();
const icon = chip.nativeElement.querySelector('[igxsuffix]').textContent.trim();
expect(content).toBe(grouping[index].fieldName);
if (icon === SORTING_ICON_ASC_CONTENT) {
expect(grouping[index].dir).toBe(SortingDirection.Asc);
} else {
expect(grouping[index].dir).toBe(SortingDirection.Desc);
}
});
};
it('should allow grouping by different data types.', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
fix.detectChanges();
// group by string column
const grid = fix.componentInstance.instance;
grid.groupBy({
fieldName: 'ProductName', dir: SortingDirection.Desc, ignoreCase: false
});
fix.detectChanges();
// verify grouping expressions
const grExprs = grid.groupingExpressions;
expect(grExprs.length).toEqual(1);
expect(grExprs[0].fieldName).toEqual('ProductName');
// verify rows
let groupRows = grid.groupsRowList.toArray();
let dataRows = grid.dataRowList.toArray();
expect(groupRows.length).toEqual(5);
expect(dataRows.length).toEqual(8);
checkGroups(groupRows, ['NetAdvantage', 'Ignite UI for JavaScript', 'Ignite UI for Angular', '', null]);
// ungroup
grid.clearGrouping('ProductName');
tick();
fix.detectChanges();
// verify no groups are present
expect(grid.groupsRowList.toArray().length).toEqual(0);
// group by number
grid.groupBy({
fieldName: 'Downloads', dir: SortingDirection.Desc, ignoreCase: false
});
fix.detectChanges();
groupRows = grid.groupsRowList.toArray();
dataRows = grid.dataRowList.toArray();
expect(groupRows.length).toEqual(6);
expect(dataRows.length).toEqual(8);
checkGroups(groupRows, [1000, 254, 100, 20, 0, null]);
// ungroup and group by boolean column
grid.clearGrouping('Downloads');
tick();
fix.detectChanges();
grid.groupBy({ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false });
fix.detectChanges();
groupRows = grid.groupsRowList.toArray();
dataRows = grid.dataRowList.toArray();
expect(groupRows.length).toEqual(3);
expect(dataRows.length).toEqual(8);
checkGroups(groupRows, [true, false, null]);
// ungroup and group by date column
grid.clearGrouping('Released');
tick();
fix.detectChanges();
grid.groupBy({
fieldName: 'ReleaseDate', dir: SortingDirection.Asc, ignoreCase: false
});
fix.detectChanges();
groupRows = grid.groupsRowList.toArray();
dataRows = grid.dataRowList.toArray();
expect(groupRows.length).toEqual(4);
expect(dataRows.length).toEqual(8);
const expectedValue1 = groupRows[1].nativeElement.nextElementSibling.querySelectorAll('igx-grid-cell')[3].textContent;
const actualValue1 = groupRows[1].element.nativeElement.querySelector('.igx-group-label__text').textContent;
const expectedValue2 = groupRows[2].nativeElement.nextElementSibling.querySelectorAll('igx-grid-cell')[3].textContent;
const actualValue2 = groupRows[2].element.nativeElement.querySelector('.igx-group-label__text').textContent;
const expectedValue3 = groupRows[3].nativeElement.nextElementSibling.querySelectorAll('igx-grid-cell')[3].textContent;
const actualValue3 = groupRows[3].element.nativeElement.querySelector('.igx-group-label__text').textContent;
expect(actualValue1).toEqual(expectedValue1);
expect(actualValue2).toEqual(expectedValue2);
expect(actualValue3).toEqual(expectedValue3);
checkGroups(
groupRows,
[null, fix.componentInstance.prevDay, fix.componentInstance.today, fix.componentInstance.nextDay]);
}));
it('should only account for year, month and day when grouping by \'date\' dataType column', fakeAsync(() => {
const fix = TestBed.createComponent(GridGroupByTestDateTimeDataComponent);
fix.detectChanges();
const grid = fix.componentInstance.grid;
fix.detectChanges();
grid.groupBy({
fieldName: 'DateField', dir: SortingDirection.Asc, ignoreCase: false
});
fix.detectChanges();
const groupRows = grid.groupsRowList.toArray();
expect(groupRows.length).toEqual(3);
const targetTestVal = new Date(2012, 1, 12);
const index = groupRows.findIndex(gr => new Date(gr.groupRow.value).getTime() === targetTestVal.getTime());
expect(groupRows[index].groupRow.records.length).toEqual(2);
// compare the date values in the target group which are two identical dates with different time values
const field = groupRows[index].groupRow.expression.fieldName;
const record1 = groupRows[index].groupRow.records[0];
const record2 = groupRows[index].groupRow.records[1];
const rec1Date = new Date(record1[field]);
const rec2Date = new Date(record2[field]);
// the time portions of the two records differ, so the Dates are not equal even though they are in the same group
expect(rec1Date.getTime()).not.toEqual(rec2Date.getTime());
// the date portions are the same, so they are in the same group, as the column type is `date`
expect(rec1Date.getDate()).toEqual(rec2Date.getDate());
expect(rec1Date.getMonth()).toEqual(rec2Date.getMonth());
expect(rec1Date.getFullYear()).toEqual(rec2Date.getFullYear());
}));
it('should only account for hours, minutes, seconds and ms when grouping by \'time\' dataType column', fakeAsync(() => {
const fix = TestBed.createComponent(GridGroupByTestDateTimeDataComponent);
fix.detectChanges();
const grid = fix.componentInstance.grid;
grid.groupBy({
fieldName: 'TimeField', dir: SortingDirection.Asc, ignoreCase: false
});
fix.detectChanges();
const groupRows = grid.groupsRowList.toArray();
expect(groupRows.length).toEqual(3);
const targetTestVal = new Date(new Date().setHours(3, 20, 0, 1));
const index = groupRows.findIndex(gr => new Date(gr.groupRow.value).getHours() === targetTestVal.getHours()
&& new Date(gr.groupRow.value).getMinutes() === targetTestVal.getMinutes()
&& new Date(gr.groupRow.value).getSeconds() === targetTestVal.getSeconds()
&& new Date(gr.groupRow.value).getMilliseconds() === targetTestVal.getMilliseconds());
expect(groupRows[index].groupRow.records.length).toEqual(3);
// compare the date values in the target group which are three different dates with same time values
const field = groupRows[index].groupRow.expression.fieldName;
const record1 = groupRows[index].groupRow.records[0];
const record2 = groupRows[index].groupRow.records[1];
const record3 = groupRows[index].groupRow.records[2];
const rec1Date = new Date(record1[field]);
const rec2Date = new Date(record2[field]);
const rec3Date = new Date(record3[field]);
// the date portions of the following records differ, so the Dates are not equal even though they are in the same group
expect(rec1Date.getTime()).not.toEqual(rec2Date.getTime());
// the below are equal by date as well
expect(rec2Date.getTime()).toEqual(rec3Date.getTime());
// the time portions of the not equal dates are the same, so they are in the same group, as the column type is `time`
expect(rec1Date.getHours()).toEqual(rec2Date.getHours());
expect(rec1Date.getMinutes()).toEqual(rec2Date.getMinutes());
expect(rec1Date.getSeconds()).toEqual(rec2Date.getSeconds());
expect(rec1Date.getMilliseconds()).toEqual(rec2Date.getMilliseconds());
}));
it('should account for all date values when grouping by \'dateTime\' dataType column', fakeAsync(() => {
const fix = TestBed.createComponent(GridGroupByTestDateTimeDataComponent);
fix.detectChanges();
const grid = fix.componentInstance.grid;
grid.groupBy({
fieldName: 'DateTimeField', dir: SortingDirection.Asc, ignoreCase: false
});
fix.detectChanges();
// there are two identical DateTime values, so 4 groups out of 5 data records
const groupRows = grid.groupsRowList.toArray();
expect(groupRows.length).toEqual(4);
const targetTestVal = new Date(new Date('2003-03-17').setHours(3, 20, 0, 1));
const index = groupRows.findIndex(gr => new Date(gr.groupRow.value).getTime() === targetTestVal.getTime());
expect(groupRows[index].groupRow.records.length).toEqual(2);
// compare the date values in the target group which are two identical dates - date and time portions
const field = groupRows[index].groupRow.expression.fieldName;
const record1 = groupRows[index].groupRow.records[0];
const record2 = groupRows[index].groupRow.records[1];
const rec1Date = new Date(record1[field]);
const rec2Date = new Date(record2[field]);
expect(rec1Date.getTime()).toEqual(rec2Date.getTime());
}));
it('should display time value in the group by row when grouped by a \'time\' column', fakeAsync(() => {
const fix = TestBed.createComponent(GridGroupByTestDateTimeDataComponent);
fix.detectChanges();
const grid = fix.componentInstance.grid;
grid.groupBy({
fieldName: 'TimeField', dir: SortingDirection.Asc, ignoreCase: false
});
fix.detectChanges();
const groupRows = grid.groupsRowList.toArray();
const expectedValue1 = groupRows[0].nativeElement.nextElementSibling.querySelectorAll('igx-grid-cell')[3].textContent;
const actualValue1 = groupRows[0].element.nativeElement.querySelector('.igx-group-label__text').textContent;
expect(expectedValue1).toEqual(actualValue1);
}));
it('should display time value in the group by row when grouped by a \'dateTime\' column', fakeAsync(() => {
const fix = TestBed.createComponent(GridGroupByTestDateTimeDataComponent);
fix.detectChanges();
const grid = fix.componentInstance.grid;
grid.groupBy({
fieldName: 'DateTimeField', dir: SortingDirection.Asc, ignoreCase: false
});
fix.detectChanges();
const groupRows = grid.groupsRowList.toArray();
const expectedValue1 = groupRows[0].nativeElement.nextElementSibling.querySelectorAll('igx-grid-cell')[4].textContent;
const actualValue1 = groupRows[0].element.nativeElement.querySelector('.igx-group-label__text').textContent;
expect(expectedValue1).toEqual(actualValue1);
}));
it('should allow grouping by multiple columns.', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
fix.detectChanges();
fix.componentInstance.height = null;
tick();
fix.detectChanges();
// group by 2 columns
const grid = fix.componentInstance.instance;
grid.groupBy({
fieldName: 'ProductName', dir: SortingDirection.Desc, ignoreCase: false
});
grid.groupBy({ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false });
fix.detectChanges();
let groupRows = grid.groupsRowList.toArray();
let dataRows = grid.dataRowList.toArray();
// verify groups and data rows count
expect(groupRows.length).toEqual(13);
expect(dataRows.length).toEqual(8);
// verify groups
checkGroups(groupRows,
['NetAdvantage', true, false, 'Ignite UI for JavaScript', true,
false, 'Ignite UI for Angular', false, null, '', true, null, true],
grid.groupingExpressions);
// group by 3rd column
grid.groupBy({
fieldName: 'Downloads', dir: SortingDirection.Desc, ignoreCase: false
});
fix.detectChanges();
groupRows = grid.groupsRowList.toArray();
dataRows = grid.dataRowList.toArray();
// verify groups and data rows count
expect(groupRows.length).toEqual(21);
expect(dataRows.length).toEqual(8);
// verify groups
checkGroups(groupRows,
['NetAdvantage', true, 1000, false, 1000, 'Ignite UI for JavaScript', true, null, false, 254, 'Ignite UI for Angular',
false, 20, null, 1000, '', true, 100, null, true, 0],
grid.groupingExpressions);
}));
it('should allow grouping with a custom comparer', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
fix.detectChanges();
fix.componentInstance.data[0].ReleaseDate = new Date(2017, 1, 1, 15, 30, 0, 0);
fix.componentInstance.data[1].ReleaseDate = new Date(2017, 1, 1, 20, 30, 0, 0);
fix.componentInstance.height = null;
const grid = fix.componentInstance.instance;
fix.detectChanges();
grid.groupBy({
fieldName: 'ReleaseDate',
dir: SortingDirection.Desc,
groupingComparer: (a: Date, b: Date) => {
if (a instanceof Date && b instanceof Date &&
a.getFullYear() === b.getFullYear() &&
a.getMonth() === b.getMonth() &&
a.getDate() === b.getDate()) {
return 0;
}
return DefaultSortingStrategy.instance().compareValues(a, b);
}
});
tick();
fix.detectChanges();
let groupRows = grid.groupsRowList.toArray();
// verify groups count
expect(groupRows.length).toEqual(5);
// now click the chip to change sorting, the grouping expression should hold
// the comparer and reapply the same grouping again
const chips = grid.groupArea.chips;
// click grouping direction arrow
grid.groupArea.handleClick(chips.get(0).id);
tick();
fix.detectChanges();
// chips = fix.nativeElement.querySelectorAll('igx-chip');
expect(chips.length).toBe(1);
checkChips(chips, grid.groupingExpressions);
expect(chips.get(0).nativeElement.querySelectorAll('igx-icon')[0].textContent.trim()).toBe('arrow_upward');
groupRows = grid.groupsRowList.toArray();
expect(groupRows.length).toEqual(5);
}));
it('should allows expanding/collapsing groups.', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
grid.primaryKey = 'ID';
fix.detectChanges();
grid.groupBy({ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false });
fix.detectChanges();
let groupRows = grid.groupsRowList.toArray();
let dataRows = grid.dataRowList.toArray();
// verify groups and data rows count
expect(groupRows.length).toEqual(3);
expect(dataRows.length).toEqual(8);
// toggle grouprow - collapse
expect(groupRows[0].expanded).toEqual(true);
grid.toggleGroup(groupRows[0].groupRow);
tick();
fix.detectChanges();
expect(groupRows[0].expanded).toEqual(false);
groupRows = grid.groupsRowList.toArray();
dataRows = grid.dataRowList.toArray();
expect(groupRows.length).toEqual(3);
expect(dataRows.length).toEqual(4);
// verify collapsed group sub records are not rendered
// behavioral change! row should not be returned, as its parent is collapsed
for (const rec of groupRows[0].groupRow.records) {
expect(grid.getRowByKey(rec.ID)).toBeUndefined();
}
// toggle grouprow - expand
grid.toggleGroup(groupRows[0].groupRow);
tick();
fix.detectChanges();
expect(groupRows[0].expanded).toEqual(true);
for (const rec of groupRows[0].groupRow.records) {
expect(grid.getRowByKey(rec.ID)).not.toBeUndefined();
}
groupRows = grid.groupsRowList.toArray();
dataRows = grid.dataRowList.toArray();
expect(groupRows.length).toEqual(3);
expect(dataRows.length).toEqual(8);
// verify expanded group sub records are rendered
for (const rec of groupRows[0].groupRow.records) {
expect(grid.getRowByKey(rec.ID)).not.toBeUndefined();
}
const groupRow = grid.getRowByIndex(0);
expect(groupRow.isGroupByRow).toBe(true);
expect(groupRow.expanded).toBe(true);
groupRow.expanded = false;
tick();
fix.detectChanges();
expect(groupRow.expanded).toBe(false);
grid.clearGrouping();
tick();
grid.groupBy({ fieldName: 'ReleaseDate', dir: SortingDirection.Desc });
fix.detectChanges();
grid.toggleGroup(grid.groupsRowList.first.groupRow);
tick();
fix.detectChanges();
expect(groupRows[0].expanded).toEqual(false);
}));
it('should allow changing the order of the groupBy columns.', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
fix.detectChanges();
// set groupingExpressions
const grid = fix.componentInstance.instance;
const exprs: ISortingExpression[] = [
{ fieldName: 'ProductName', dir: SortingDirection.Desc, ignoreCase: true },
{ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: true }
];
grid.groupingExpressions = exprs;
tick();
fix.detectChanges();
let groupRows = grid.groupsRowList.toArray();
let dataRows = grid.dataRowList.toArray();
expect(groupRows.length).toEqual(13);
expect(dataRows.length).toEqual(8);
// verify groups
checkGroups(groupRows,
['NetAdvantage', true, false, 'Ignite UI for JavaScript', true,
false, 'Ignite UI for Angular', false, null, '', true, null, true],
grid.groupingExpressions);
// change order
grid.groupingExpressions = [
{ fieldName: 'Released', dir: SortingDirection.Asc, ignoreCase: true },
{ fieldName: 'ProductName', dir: SortingDirection.Asc, ignoreCase: true }
];
tick();
grid.sortingExpressions = [
{ fieldName: 'Released', dir: SortingDirection.Asc, ignoreCase: true },
{ fieldName: 'ProductName', dir: SortingDirection.Asc, ignoreCase: true }
];
tick();
fix.detectChanges();
groupRows = grid.groupsRowList.toArray();
dataRows = grid.dataRowList.toArray();
expect(groupRows.length).toEqual(11);
expect(dataRows.length).toEqual(8);
// verify groups
checkGroups(groupRows,
[null, 'Ignite UI for Angular', false, 'Ignite UI for Angular', 'Ignite UI for JavaScript',
'NetAdvantage', true, null, '', 'Ignite UI for JavaScript', 'NetAdvantage'],
grid.groupingExpressions);
}));
it('should group records correctly when ignoreCase is set to true.', fakeAsync(() => {
const fix = TestBed.createComponent(GridGroupByCaseSensitiveComponent);
fix.detectChanges();
// set groupingExpressions
const grid = fix.componentInstance.instance;
const exprs: ISortingExpression[] = [
{ fieldName: 'ContactTitle', dir: SortingDirection.Asc, ignoreCase: true }
];
grid.groupingExpressions = exprs;
tick();
fix.detectChanges();
const groupRows = grid.groupsRowList.toArray();
const dataRows = grid.dataRowList.toArray();
expect(groupRows.length).toEqual(2);
expect(dataRows.length).toEqual(5);
// verify groups
checkGroups(groupRows,
['Order Administrator', 'Owner'],
grid.groupingExpressions);
}));
it('should allow setting expand/collapse state', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
grid.primaryKey = 'ID';
fix.detectChanges();
grid.groupsExpanded = false;
grid.groupBy({ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false });
fix.detectChanges();
let groupRows = grid.groupsRowList.toArray();
let dataRows = grid.dataRowList.toArray();
expect(groupRows.length).toEqual(3);
expect(dataRows.length).toEqual(0);
for (const grRow of groupRows) {
expect(grRow.expanded).toBe(false);
}
grid.groupsExpanded = true;
tick();
grid.cdr.detectChanges();
groupRows = grid.groupsRowList.toArray();
dataRows = grid.dataRowList.toArray();
expect(groupRows.length).toEqual(3);
expect(dataRows.length).toEqual(8);
for (const grRow of groupRows) {
expect(grRow.expanded).toBe(true);
}
}));
it('should trigger an groupingDone event when a column is grouped with the correct params.', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
grid.primaryKey = 'ID';
fix.detectChanges();
grid.groupBy({ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false });
fix.detectChanges();
const currExpr = fix.componentInstance.currentSortExpressions;
expect(currExpr.expressions.length).toEqual(1);
expect(currExpr.expressions[0].fieldName).toEqual('Released');
expect(currExpr.groupedColumns.length).toEqual(1);
expect(currExpr.groupedColumns[0].field).toEqual('Released');
expect(currExpr.ungroupedColumns.length).toEqual(0);
}));
it('should trigger an groupingDone event when a column is ungrouped with the correct params.', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
grid.primaryKey = 'ID';
fix.detectChanges();
grid.groupBy([
{ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false },
{ fieldName: 'ReleaseDate', dir: SortingDirection.Desc, ignoreCase: false }
]);
tick();
fix.detectChanges();
grid.clearGrouping('Released');
tick();
fix.detectChanges();
const currExpr = fix.componentInstance.currentSortExpressions;
expect(currExpr.expressions.length).toEqual(1);
expect(currExpr.expressions[0].fieldName).toEqual('ReleaseDate');
expect(currExpr.groupedColumns.length).toEqual(0);
expect(currExpr.ungroupedColumns.length).toEqual(1);
expect(currExpr.ungroupedColumns[0].field).toEqual('Released');
}));
it('should trigger an groupingDone event when multiple columns are grouped with the correct params.', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
grid.primaryKey = 'ID';
fix.detectChanges();
grid.groupBy([
{ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false },
{ fieldName: 'ProductName', dir: SortingDirection.Asc, ignoreCase: false },
{ fieldName: 'ReleaseDate', dir: SortingDirection.Desc, ignoreCase: false }
]);
tick();
fix.detectChanges();
const currExpr = fix.componentInstance.currentSortExpressions;
expect(currExpr.expressions.length).toEqual(3);
expect(currExpr.expressions[0].fieldName).toEqual('Released');
expect(currExpr.expressions[1].fieldName).toEqual('ProductName');
expect(currExpr.expressions[2].fieldName).toEqual('ReleaseDate');
expect(currExpr.groupedColumns.length).toEqual(3);
expect(currExpr.groupedColumns[0].field).toEqual('Released');
expect(currExpr.groupedColumns[1].field).toEqual('ProductName');
expect(currExpr.groupedColumns[2].field).toEqual('ReleaseDate');
expect(currExpr.ungroupedColumns.length).toEqual(0);
}));
it('should trigger an groupingDone event when multiple columns are ungrouped with the correct params.', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
grid.primaryKey = 'ID';
fix.detectChanges();
grid.groupBy([
{ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false },
{ fieldName: 'ReleaseDate', dir: SortingDirection.Desc, ignoreCase: false },
{ fieldName: 'ProductName', dir: SortingDirection.Asc, ignoreCase: false },
{ fieldName: 'Downloads', dir: SortingDirection.Asc, ignoreCase: false }
]);
tick();
fix.detectChanges();
grid.clearGrouping(['Released', 'ProductName', 'Downloads']);
tick();
fix.detectChanges();
const currExpr = fix.componentInstance.currentSortExpressions;
expect(currExpr.expressions.length).toEqual(1);
expect(currExpr.expressions[0].fieldName).toEqual('ReleaseDate');
expect(currExpr.groupedColumns.length).toEqual(0);
expect(currExpr.ungroupedColumns.length).toEqual(3);
expect(currExpr.ungroupedColumns[0].field).toEqual('Released');
expect(currExpr.ungroupedColumns[1].field).toEqual('ProductName');
expect(currExpr.ungroupedColumns[2].field).toEqual('Downloads');
}));
it(`should trigger an groupingDone event when the user pushes a new array of grouping expressions, which results in
both grouping and ungrouping at the same time.`, fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
grid.primaryKey = 'ID';
fix.detectChanges();
grid.groupBy([
{ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false },
{ fieldName: 'ReleaseDate', dir: SortingDirection.Desc, ignoreCase: false },
{ fieldName: 'ProductName', dir: SortingDirection.Asc, ignoreCase: false }
]);
tick();
fix.detectChanges();
const newExpressions = [
{ fieldName: 'ReleaseDate', dir: SortingDirection.Desc, ignoreCase: false },
{ fieldName: 'ProductName', dir: SortingDirection.Asc, ignoreCase: false },
{ fieldName: 'Downloads', dir: SortingDirection.Asc, ignoreCase: false }
];
grid.groupingExpressions = newExpressions;
tick();
fix.detectChanges();
const currExpr = fix.componentInstance.currentSortExpressions;
expect(currExpr.expressions.length).toEqual(3);
expect(currExpr.expressions[0].fieldName).toEqual('ReleaseDate');
expect(currExpr.expressions[1].fieldName).toEqual('ProductName');
expect(currExpr.expressions[2].fieldName).toEqual('Downloads');
expect(currExpr.ungroupedColumns.length).toEqual(1);
expect(currExpr.ungroupedColumns[0].field).toEqual('Released');
expect(currExpr.groupedColumns.length).toEqual(1);
expect(currExpr.groupedColumns[0].field).toEqual('Downloads');
}));
it('should allow setting custom template for group row content and expand/collapse icons.', fakeAsync(() => {
const fix = TestBed.createComponent(CustomTemplateGridComponent);
const grid = fix.componentInstance.instance;
fix.detectChanges();
grid.groupBy({ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false });
fix.detectChanges();
const groupRows = grid.groupsRowList.toArray();
for (const grRow of groupRows) {
const elem = grRow.groupContent.nativeElement;
const grVal = grRow.groupRow.value === null ? '' : grRow.groupRow.value.toString();
const expectedText = 'Grouping by "Is it Released". ' +
'Total items with value:' + grVal +
' are ' + grRow.groupRow.records.length;
expect(elem.innerText.trim(['\n', '\r', ' '])).toEqual(expectedText);
const expander = grRow.nativeElement.querySelector('.igx-grid__grouping-indicator');
expect(expander.innerText).toBe('EXPANDED');
}
groupRows[0].toggle();
const expndr = groupRows[0].nativeElement.querySelector('.igx-grid__grouping-indicator');
expect(expndr.innerText).toBe('COLLAPSED');
expect(grid.headerGroupContainer.nativeElement.innerText).toBe('EXPANDED');
grid.toggleAllGroupRows();
fix.detectChanges();
expect(grid.headerGroupContainer.nativeElement.innerText).toBe('COLLAPSED');
}));
it('should allow setting custom template for group row via Input.', fakeAsync(() => {
const fix = TestBed.createComponent(CustomTemplateGridComponent);
const grid = fix.componentInstance.instance;
fix.detectChanges();
grid.groupRowTemplate = fix.componentInstance.customGroupBy;
fix.detectChanges();
grid.groupBy({ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false });
fix.detectChanges();
const grRow = grid.groupsRowList.toArray()[0];
const elem = grRow.groupContent.nativeElement;
expect(elem.innerText.trim()).toEqual('CUSTOM GROUP BY');
}));
it('should have the correct ARIA attributes on the group rows.', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
grid.primaryKey = 'ID';
fix.detectChanges();
grid.groupBy({ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false });
fix.detectChanges();
const groupRows = grid.groupsRowList.toArray();
for (const grRow of groupRows) {
const elem = grRow.element.nativeElement;
expect(elem.attributes['aria-describedby'].value).toEqual(grid.id + '_Released');
expect(elem.attributes['aria-expanded'].value).toEqual('true');
}
}));
it('should not apply grouping if the grouping expressions value is the same reference', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
fix.detectChanges();
// group by string column
const grid = fix.componentInstance.instance;
fix.detectChanges();
grid.groupBy({
fieldName: 'ReleaseDate', dir: SortingDirection.Asc, ignoreCase: false
});
fix.detectChanges();
spyOn(grid.groupingExpressionsChange, 'emit');
fix.detectChanges();
const firstCellElem = grid.gridAPI.get_cell_by_index(2, 'Downloads');
UIInteractions.simulateClickAndSelectEvent(firstCellElem);
fix.detectChanges();
expect(grid.groupingExpressionsChange.emit).toHaveBeenCalledTimes(0);
}));
it('should emit groupingExpressionsChange when a group is sorted through the chip', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
fix.detectChanges();
// group by string column
const grid = fix.componentInstance.instance;
fix.detectChanges();
grid.groupBy({
fieldName: 'ReleaseDate', dir: SortingDirection.Asc, ignoreCase: false
});
fix.detectChanges();
spyOn(grid.groupingExpressionsChange, 'emit');
fix.detectChanges();
const chips = grid.groupArea.chips;
grid.groupArea.handleClick(chips.first.id);
fix.detectChanges();
expect(grid.groupingExpressionsChange.emit).toHaveBeenCalledTimes(1);
}));
it('should group unbound column with custom grouping strategy', fakeAsync(() => {
const fix = TestBed.createComponent(GroupableGridComponent);
fix.componentInstance.data.forEach((r, i) => {
r['fieldValue1'] = Math.floor(i / 3);
r['fieldValue2'] = Math.floor(i / 4);
});
fix.detectChanges();
fix.componentInstance.instance.groupBy({
fieldName: 'UnboundField', dir: SortingDirection.Desc, ignoreCase: false
});
fix.detectChanges();
const groupRows = fix.componentInstance.instance.groupsRowList.toArray();
expect(groupRows.length).toEqual(4);
}));
it('should update chip state after columns change.', () => {
const fix = TestBed.createComponent(GroupByDataMoreColumnsComponent);
fix.detectChanges();
const grid = fix.componentInstance.instance;
grid.groupingExpressions = [
{
dir: SortingDirection.Asc,
fieldName: "NewColumn"
}
];
fix.detectChanges();
// no such column initially, so chip is disabled.
const chips = grid.groupArea.chips;
expect(chips.first.disabled).toBeTrue();
const newCols = [...fix.componentInstance.columns];
newCols.push({
field: "NewColumn",
width: 100
});
fix.componentInstance.columns = newCols;
fix.detectChanges();
// column now exists and has groupable=true, so chip should be enabled.
expect(chips.first.disabled).toBeFalse();
});
it('should update chip state on column groupable prop change', () => {
const fix = TestBed.createComponent(DefaultGridComponent);
fix.detectChanges();
const grid = fix.componentInstance.instance;
const column = grid.getColumnByName('ProductName');
grid.groupBy({
fieldName: 'ProductName', dir: SortingDirection.Desc, ignoreCase: false
});
fix.detectChanges();
// initially should not be disabled.
const chips = grid.groupArea.chips;
expect(chips.first.disabled).toBeFalse();
// should get disbaled on groupable=false
column.groupable = false;
fix.detectChanges();
expect(chips.first.disabled).toBeTrue();
});
// GroupBy + Sorting integration
it('should apply sorting on each group\'s records when non-grouped column is sorted.', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
fix.componentInstance.enableSorting = true;
tick();
fix.detectChanges();
grid.groupBy({
fieldName: 'ProductName', dir: SortingDirection.Desc, ignoreCase: false
});
fix.detectChanges();
const groupRows = grid.groupsRowList.toArray();
const dataRows = grid.dataRowList.toArray();
// verify groups and data rows count
expect(groupRows.length).toEqual(5);
expect(dataRows.length).toEqual(8);
grid.sort({ fieldName: 'Released', dir: SortingDirection.Asc, ignoreCase: false });
fix.detectChanges();
// verify groups
checkGroups(groupRows, ['NetAdvantage', 'Ignite UI for JavaScript', 'Ignite UI for Angular', '', null]);
// verify data records order
const expectedDataRecsOrder = [false, true, false, true, null, false, true, true];
dataRows.forEach((row, index) => {
expect(row.data.Released).toEqual(expectedDataRecsOrder[index]);
});
}));
it('should apply the specified sort order on the group rows when already grouped columnn is sorted in asc/desc order.',
fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
fix.componentInstance.enableSorting = true;
fix.detectChanges();
grid.groupBy({
fieldName: 'ProductName', dir: SortingDirection.Desc, ignoreCase: false
});
fix.detectChanges();
let groupRows = grid.groupsRowList.toArray();
let dataRows = grid.dataRowList.toArray();
// verify groups and data rows count
expect(groupRows.length).toEqual(5);
expect(dataRows.length).toEqual(8);
// verify group order
checkGroups(groupRows, ['NetAdvantage', 'Ignite UI for JavaScript', 'Ignite UI for Angular', '', null]);
grid.sort({
fieldName: 'ProductName',
dir: SortingDirection.Asc,
ignoreCase: false
});
fix.detectChanges();
groupRows = grid.groupsRowList.toArray();
dataRows = grid.dataRowList.toArray();
// verify group order
checkGroups(groupRows, [null, '', 'Ignite UI for Angular', 'Ignite UI for JavaScript', 'NetAdvantage']);
}));
it('should remove grouping when already grouped columnn is sorted with order "None" via the API.', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
fix.componentInstance.enableSorting = true;
fix.detectChanges();
grid.groupBy({
fieldName: 'ProductName', dir: SortingDirection.Desc, ignoreCase: false
});
fix.detectChanges();
let groupRows = grid.groupsRowList.toArray();
let dataRows = grid.dataRowList.toArray();
// verify groups and data rows count
expect(groupRows.length).toEqual(5);
expect(dataRows.length).toEqual(8);
// verify group order
checkGroups(groupRows, ['NetAdvantage', 'Ignite UI for JavaScript', 'Ignite UI for Angular', '', null]);
grid.sort({ fieldName: 'ProductName', dir: SortingDirection.None, ignoreCase: false });
fix.detectChanges();
groupRows = grid.groupsRowList.toArray();
dataRows = grid.dataRowList.toArray();
// verify groups and data rows count
expect(groupRows.length).toEqual(0);
expect(dataRows.length).toEqual(8);
}));
it('should not be able to sort the column when is already grouped by', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
fix.componentInstance.enableSorting = true;
fix.detectChanges();
grid.groupBy({
fieldName: 'Downloads', dir: SortingDirection.Desc, ignoreCase: false
});
fix.detectChanges();
const headers = fix.debugElement.queryAll(By.css(COLUMN_HEADER_CLASS));
//header sort icon should not be displayed
const sortIcon = headers[0].query(By.css('.sort-icon'));
expect(sortIcon).toBeNull()
}));
it('should group by the specified field when grouping by an already sorted field.', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
fix.componentInstance.enableSorting = true;
fix.detectChanges();
grid.sort({ fieldName: 'ProductName', dir: SortingDirection.Desc, ignoreCase: false });
fix.detectChanges();
expect(grid.sortingExpressions.length).toBe(1);
grid.groupBy({
fieldName: 'ProductName', dir: SortingDirection.Asc, ignoreCase: false
});
fix.detectChanges();
const groupRows = grid.groupsRowList.toArray();
// verify group order
checkGroups(groupRows, [null, '', 'Ignite UI for Angular', 'Ignite UI for JavaScript', 'NetAdvantage']);
expect(grid.sortingExpressions.length).toBe(0);
expect(grid.groupingExpressions.length).toBe(1);
}));
it('should allow grouping of already sorted column', waitForAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
fix.componentInstance.enableSorting = true;
fix.detectChanges();
grid.sort({ fieldName: 'ProductName', dir: SortingDirection.Desc, ignoreCase: false });
fix.detectChanges();
grid.groupBy({
fieldName: 'ProductName', dir: SortingDirection.Desc, ignoreCase: false
});
fix.detectChanges();
const groupRows = grid.groupsRowList.toArray();
const dataRows = grid.dataRowList.toArray();
// verify groups and data rows count
expect(groupRows.length).toEqual(5);
expect(dataRows.length).toEqual(8);
expect(grid.groupingExpressions.length).toEqual(1);
}));
// GroupBy + Virtualization integration
it('should virtualize data and group records.', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
fix.componentInstance.width = '600px';
fix.componentInstance.height = '300px';
grid.columnWidth = '200px';
fix.detectChanges();
grid.groupBy({
fieldName: 'ProductName', dir: SortingDirection.Desc, ignoreCase: false
});
grid.groupBy({ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false });
fix.detectChanges();
expect(grid.groupsRowList.toArray().length).toEqual(3);
expect(grid.dataRowList.toArray().length).toEqual(2);
expect(grid.rowList.toArray().length).toEqual(5);
}));
it('should recalculate visible chunk data and scrollbar size when expanding/collapsing group rows.', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
fix.componentInstance.width = '600px';
tick();
fix.componentInstance.height = '300px';
tick();
grid.columnWidth = '200px';
tick();
fix.detectChanges();
grid.groupBy({
fieldName: 'ProductName', dir: SortingDirection.Desc, ignoreCase: false
});
grid.groupBy({ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false });
fix.detectChanges();
const origScrollHeight = parseInt((grid.verticalScrollContainer.getScroll().children[0] as HTMLElement).style.height, 10);
// collapse all group rows currently in the view
const grRows = grid.groupsRowList.toArray();
grRows[0].toggle();
tick();
fix.detectChanges();
// verify rows are updated
expect(grid.groupsRowList.toArray().length).toEqual(4);
expect(grid.dataRowList.toArray().length).toEqual(1);
expect(grid.rowList.toArray().length).toEqual(5);
// verify scrollbar is updated - 4 rows x 51px are hidden.
expect(parseInt((grid.verticalScrollContainer.getScroll().children[0] as HTMLElement).style.height, 10))
.toEqual(origScrollHeight - 204);
grRows[0].toggle();
tick();
fix.detectChanges();
expect(grid.groupsRowList.toArray().length).toEqual(3);
expect(grid.dataRowList.toArray().length).toEqual(2);
expect(grid.rowList.toArray().length).toEqual(5);
expect(parseInt((grid.verticalScrollContainer.getScroll().children[0] as HTMLElement).style.height, 10))
.toEqual(origScrollHeight);
}));
it('should persist group row expand/collapse state when scrolling.', async () => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
fix.componentInstance.width = '500px';
fix.componentInstance.height = '300px';
grid.columnWidth = '200px';
await wait();
fix.detectChanges();
grid.groupBy({
fieldName: 'ProductName', dir: SortingDirection.Desc, ignoreCase: false
});
grid.groupBy({ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false });
fix.detectChanges();
let groupRow = grid.groupsRowList.toArray()[0];
groupRow.toggle();
await wait();
expect(groupRow.expanded).toBe(false);
fix.detectChanges();
// scroll to bottom
grid.verticalScrollContainer.getScroll().scrollTop = 10000;
await wait(100);
fix.detectChanges();
// scroll back to the top
grid.verticalScrollContainer.getScroll().scrollTop = 0;
await wait(100);
fix.detectChanges();
groupRow = grid.groupsRowList.toArray()[0];
expect(groupRow.expanded).toBe(false);
});
it('should retain focused group after expanding/collapsing row via KB - Alt + ArrowUp/ArrowDown', async () => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
fix.componentInstance.width = '500px';
fix.componentInstance.height = '300px';
grid.columnWidth = '200px';
await wait();
fix.detectChanges();
grid.groupBy({
fieldName: 'ProductName', dir: SortingDirection.Desc, ignoreCase: false
});
fix.detectChanges();
// scroll to bottom
grid.verticalScrollContainer.getScroll().