igniteui-angular-sovn
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
983 lines (801 loc) • 59.5 kB
text/typescript
import { configureTestSuite } from '../../test-utils/configure-suite';
import { TestBed, tick, fakeAsync, ComponentFixture, waitForAsync } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { IgxChildGridRowComponent, IgxHierarchicalGridComponent } from './hierarchical-grid.component';
import { wait, UIInteractions } from '../../test-utils/ui-interactions.spec';
import { DefaultSortingStrategy, SortingDirection } from '../../data-operations/sorting-strategy';
import { IgxColumnMovingDragDirective } from '../moving/moving.drag.directive';
import { IgxHierarchicalRowComponent } from './hierarchical-row.component';
import { IgxStringFilteringOperand } from '../../data-operations/filtering-condition';
import { take } from 'rxjs/operators';
import {
IgxHierarchicalGridTestBaseComponent,
IgxHierarchicalGridTestCustomToolbarComponent,
IgxHierarchicalGridWithTransactionProviderComponent
} from '../../test-utils/hierarchical-grid-components.spec';
import { GridFunctions, GridSelectionFunctions } from '../../test-utils/grid-functions.spec';
import { HierarchicalGridFunctions } from '../../test-utils/hierarchical-grid-functions.spec';
import { GridSelectionMode, ColumnPinningPosition, RowPinningPosition } from '../common/enums';
import { IgxPaginatorComponent } from '../../paginator/paginator.component';
import { SampleTestData } from '../../test-utils/sample-test-data.spec';
import { DisplayDensity } from '../../core/density';
describe('IgxHierarchicalGrid Integration #hGrid', () => {
let fixture: ComponentFixture<IgxHierarchicalGridTestBaseComponent>;
let hierarchicalGrid: IgxHierarchicalGridComponent;
const DEBOUNCE_TIME = 30;
const FILTERING_ROW_CLASS = 'igx-grid-filtering-row';
const FILTERING_CELL_CLASS = 'igx-grid-filtering-cell';
configureTestSuite();
beforeAll(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
IgxHierarchicalGridTestBaseComponent,
IgxHierarchicalGridTestCustomToolbarComponent,
IgxHierarchicalGridWithTransactionProviderComponent
]
}).compileComponents();
}))
beforeEach(waitForAsync(() => {
fixture = TestBed.createComponent(IgxHierarchicalGridTestBaseComponent);
fixture.detectChanges();
hierarchicalGrid = fixture.componentInstance.hgrid;
}));
describe('MCH', () => {
it('should allow declaring column groups.', fakeAsync(() => {
const expectedColumnGroups = 1;
const expectedLevel = 1;
expect(hierarchicalGrid.columns.filter(col => col.columnGroup).length).toEqual(expectedColumnGroups);
expect(hierarchicalGrid.getColumnByName('ProductName').level).toEqual(expectedLevel);
expect(GridFunctions.getColumnHeaders(fixture).length).toEqual(3);
const firstRow = hierarchicalGrid.dataRowList.first;
// the first row's cell should contain an expand indicator
expect(HierarchicalGridFunctions.hasExpander(firstRow)).toBeTruthy();
hierarchicalGrid.expandRow(firstRow.key);
tick(DEBOUNCE_TIME);
fixture.detectChanges();
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
expect(childGrid.columns.filter(col => col.columnGroup).length).toEqual(expectedColumnGroups);
expect(childGrid.getColumnByName('ProductName').level).toEqual(expectedLevel);
expect(GridFunctions.getColumnHeaders(fixture).length).toEqual(6);
}));
it('should apply height correctly with and without filtering', fakeAsync(() => {
let filteringCells = fixture.debugElement.queryAll(By.css(FILTERING_CELL_CLASS));
expect(hierarchicalGrid.nativeElement.offsetHeight).toBe(600);
hierarchicalGrid.height = '800px';
tick();
fixture.detectChanges();
expect(hierarchicalGrid.nativeElement.offsetHeight).toBe(800);
expect(filteringCells.length).toBe(3);
hierarchicalGrid.allowFiltering = false;
fixture.detectChanges();
expect(hierarchicalGrid.nativeElement.offsetHeight).toBe(800);
filteringCells = fixture.debugElement.queryAll(By.css(FILTERING_CELL_CLASS));
expect(filteringCells.length).toBe(0);
}));
});
describe('Selection', () => {
it('should allow only one cell to be selected in the whole hierarchical grid.', (async () => {
let firstRow = hierarchicalGrid.dataRowList.first as IgxHierarchicalRowComponent;
hierarchicalGrid.expandRow(firstRow.key);
expect(firstRow.expanded).toBeTruthy();
let fCell = firstRow.cells.toArray()[0];
// select parent cell
GridFunctions.focusCell(fixture, fCell);
await wait(100);
fixture.detectChanges();
expect(fCell.selected).toBeTruthy();
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const fChildCell = childGrid.dataRowList.first.cells.first;
// select child cell
GridFunctions.focusCell(fixture, fChildCell);
await wait(100);
fixture.detectChanges();
expect(fChildCell.selected).toBeTruthy();
expect(fCell.selected).toBeFalsy();
// select parent cell
firstRow = hierarchicalGrid.dataRowList.toArray()[0] as IgxHierarchicalRowComponent;
fCell = firstRow.cells.toArray()[0];
GridFunctions.focusCell(fixture, fCell);
await wait(100);
fixture.detectChanges();
expect(fChildCell.selected).toBeFalsy();
expect(fCell.selected).toBeTruthy();
}));
});
describe('Updating', () => {
it(`should have separate instances of updating service for
parent and children and the same for children of the same island`, fakeAsync(() => {
const firstLayoutInstances: IgxHierarchicalGridComponent[] = [];
hierarchicalGrid.childLayoutList.first.gridCreated.pipe(take(2)).subscribe((args) => {
firstLayoutInstances.push(args.grid);
});
const dataRows = hierarchicalGrid.dataRowList.toArray();
// expand 1st row
hierarchicalGrid.expandRow(dataRows[0].key);
tick(DEBOUNCE_TIME);
fixture.detectChanges();
// expand 2nd row
hierarchicalGrid.expandRow(dataRows[1].key);
tick(DEBOUNCE_TIME);
fixture.detectChanges();
// test instances
expect(firstLayoutInstances.length).toEqual(2);
expect(hierarchicalGrid.transactions).not.toBe(firstLayoutInstances[0].transactions);
expect(firstLayoutInstances[0].transactions).not.toBe(firstLayoutInstances[1].transactions);
}));
it('should contain all transactions for a row island', fakeAsync(() => {
const firstLayoutInstances: IgxHierarchicalGridComponent[] = [];
hierarchicalGrid.childLayoutList.first.gridCreated.pipe(take(2)).subscribe((args) => {
firstLayoutInstances.push(args.grid);
});
hierarchicalGrid.batchEditing = true;
tick();
fixture.detectChanges();
const dataRows = hierarchicalGrid.dataRowList.toArray();
// expand 1st row
hierarchicalGrid.expandRow(dataRows[0].key);
tick();
fixture.detectChanges();
// expand 2nd row
hierarchicalGrid.expandRow(dataRows[1].key);
tick();
fixture.detectChanges();
firstLayoutInstances[0].updateRow({ ProductName: 'Changed' }, '00');
firstLayoutInstances[1].updateRow({ ProductName: 'Changed' }, '10');
expect(hierarchicalGrid.transactions.getTransactionLog().length).toEqual(0);
expect(firstLayoutInstances[0].transactions.getTransactionLog().length).toEqual(1);
expect(fixture.componentInstance.rowIsland.transactions.getTransactionLog().length).toEqual(0);
}));
it('should remove expand indicator for uncommitted added rows', fakeAsync(() => {
hierarchicalGrid.batchEditing = true;
fixture.detectChanges();
hierarchicalGrid.data = hierarchicalGrid.data.slice(0, 3);
fixture.detectChanges();
hierarchicalGrid.addRow({ ID: -1, ProductName: 'Name1' });
fixture.detectChanges();
const rows = HierarchicalGridFunctions.getHierarchicalRows(fixture);
const lastRow = rows[rows.length - 1];
expect(lastRow.query(By.css('igx-icon')).nativeElement).toHaveClass('igx-icon--inactive');
hierarchicalGrid.transactions.commit(hierarchicalGrid.data);
fixture.detectChanges();
expect(lastRow.query(By.css('igx-icon')).nativeElement).not.toHaveClass('igx-icon--inactive');
}));
it('should now allow expanding uncommitted added rows', fakeAsync(() => {
/* using the API here assumes keyboard interactions to expand/collapse would also be blocked */
hierarchicalGrid.batchEditing = true;
fixture.detectChanges();
hierarchicalGrid.data = hierarchicalGrid.data.slice(0, 3);
fixture.detectChanges();
hierarchicalGrid.addRow({ ID: -1, ProductName: 'Name1' });
fixture.detectChanges();
const dataRows = hierarchicalGrid.dataRowList;
hierarchicalGrid.expandRow(dataRows.last.key);
let childRows = fixture.debugElement.queryAll(By.directive(IgxChildGridRowComponent));
expect(childRows.length).toEqual(0);
hierarchicalGrid.transactions.commit(hierarchicalGrid.data);
fixture.detectChanges();
hierarchicalGrid.expandRow(dataRows.last.key);
tick(DEBOUNCE_TIME);
fixture.detectChanges();
childRows = fixture.debugElement.queryAll(By.directive(IgxChildGridRowComponent));
expect(childRows.length).toEqual(1);
}));
it('should revert changes when transactions are cleared for child grids', fakeAsync(() => {
hierarchicalGrid.batchEditing = true;
fixture.detectChanges();
let childGrid;
hierarchicalGrid.childLayoutList.first.gridCreated.pipe(take(1)).subscribe((args) => {
childGrid = args.grid;
});
// expand first row
hierarchicalGrid.expandRow(hierarchicalGrid.dataRowList.first.key);
childGrid.updateRow({ ProductName: 'Changed' }, '00');
fixture.detectChanges();
expect(childGrid.gridAPI.get_cell_by_index(0, 'ProductName').nativeElement.innerText).toEqual('Changed');
childGrid.transactions.clear();
fixture.detectChanges();
expect(childGrid.gridAPI.get_cell_by_index(0, 'ProductName').nativeElement.innerText).toEqual('Product: A0');
}));
it('should return correctly the rowData', () => {
hierarchicalGrid.primaryKey = 'ID';
fixture.detectChanges();
const rowData = hierarchicalGrid.getRowByKey('2').data;
expect(hierarchicalGrid.getRowData('2')).toEqual(rowData);
hierarchicalGrid.sort({ fieldName: 'ChildLevels', dir: SortingDirection.Desc, ignoreCase: true });
fixture.detectChanges();
expect(hierarchicalGrid.getRowData('2')).toEqual(rowData);
expect(hierarchicalGrid.getRowData('101')).toEqual({});
hierarchicalGrid.filter('ID', '1', IgxStringFilteringOperand.instance().condition('startsWith'));
fixture.detectChanges();
expect(hierarchicalGrid.getRowData('2')).toEqual(rowData);
expect(hierarchicalGrid.getRowData('101')).toEqual({});
});
it('should respect transaction service that is provided in the providers array', fakeAsync(() => {
fixture = TestBed.createComponent(IgxHierarchicalGridWithTransactionProviderComponent);
tick();
fixture.detectChanges();
hierarchicalGrid = fixture.componentInstance.hgrid;
expect(hierarchicalGrid.transactions.enabled).toBeTruthy();
expect(hierarchicalGrid.batchEditing).toBeFalsy();
let childGrid: IgxHierarchicalGridComponent;
hierarchicalGrid.childLayoutList.first.gridCreated.pipe(take(1)).subscribe((args) => {
childGrid = args.grid;
});
// expand first row
hierarchicalGrid.expandRow(hierarchicalGrid.dataRowList.first.key);
expect(childGrid).toBeDefined();
expect(childGrid.transactions.enabled).toBeTruthy();
childGrid.updateRow({ ProductName: 'Changed' }, '00');
expect(childGrid.transactions.getAggregatedChanges(false).length).toBe(1);
}));
});
describe('Sorting', () => {
it('should display correct child data for expanded row after sorting.', fakeAsync(() => {
/* this test doesn't need scrolling as it only cares about the child grid getting assigned to the correct parent */
hierarchicalGrid.data = hierarchicalGrid.data.slice(0, 3);
fixture.detectChanges();
// expand first row
hierarchicalGrid.expandRow(hierarchicalGrid.dataRowList.first.key);
hierarchicalGrid.sort({
fieldName: 'ID', dir: SortingDirection.Desc, ignoreCase: false, strategy: DefaultSortingStrategy.instance()
});
fixture.detectChanges();
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const firstChildCell = childGrid.dataRowList.first.cells.first;
expect(hierarchicalGrid.gridAPI.get_row_by_index(3) instanceof IgxChildGridRowComponent).toBeTruthy();
expect(childGrid.data).toBe(fixture.componentInstance.data[0]['childData']);
expect(firstChildCell.value).toBe('00');
}));
it('should allow sorting via headers in child grids', fakeAsync(() => {
// expand first row
hierarchicalGrid.expandRow(hierarchicalGrid.dataRowList.first.key);
// enable sorting
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
childGrid.columns[0].sortable = true;
fixture.detectChanges();
const childHeader = GridFunctions.getColumnHeader('ID', fixture, childGrid);
GridFunctions.clickHeaderSortIcon(childHeader);
fixture.detectChanges();
GridFunctions.clickHeaderSortIcon(childHeader);
fixture.detectChanges();
expect(childGrid.dataRowList.first.cells.first.value).toBe('09');
const icon = GridFunctions.getHeaderSortIcon(childHeader);
expect(icon).not.toBeNull();
expect(icon.nativeElement.textContent.toLowerCase().trim()).toBe('arrow_downward');
}));
});
describe('Filtering', () => {
it('should enable filter-row for root and child grids', fakeAsync(() => {
let filteringCells = fixture.debugElement.queryAll(By.css(FILTERING_CELL_CLASS));
expect(filteringCells.length).toEqual(3);
GridFunctions.clickFilterCellChipUI(fixture, 'ID');
expect(document.querySelectorAll(FILTERING_ROW_CLASS).length).toEqual(1);
// expand first row
hierarchicalGrid.expandRow(hierarchicalGrid.dataRowList.first.key);
filteringCells = fixture.debugElement.queryAll(By.css(FILTERING_CELL_CLASS));
expect(filteringCells.length).toEqual(6);
GridFunctions.clickFilterCellChipUI(fixture, 'ProductName', hierarchicalGrid.gridAPI.getChildGrids(false)[0]);
expect(document.querySelectorAll(FILTERING_ROW_CLASS).length).toEqual(2);
}));
it('should not lose child grid states after filtering in parent grid.', fakeAsync(() => {
// expand first row
hierarchicalGrid.expandRow(hierarchicalGrid.dataRowList.first.key);
tick(DEBOUNCE_TIME);
fixture.detectChanges();
let childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
let firstChildCell = childGrid.dataRowList.first.cells.first;
UIInteractions.simulateClickAndSelectEvent(firstChildCell);
tick(DEBOUNCE_TIME);
fixture.detectChanges();
expect(firstChildCell.selected).toBe(true);
// apply some filter
hierarchicalGrid.filter('ID', '0', IgxStringFilteringOperand.instance().condition('contains'), true);
expect(hierarchicalGrid.getRowByIndex(0).expanded).toBe(true);
expect(hierarchicalGrid.gridAPI.get_row_by_index(1) instanceof IgxChildGridRowComponent).toBeTruthy();
childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
firstChildCell = childGrid.dataRowList.first.cells.first;
expect(firstChildCell.selected).toBe(true);
}));
it('should show empty filter message when there are no records matching the filter', fakeAsync(() => {
fixture.componentInstance.data = [];
fixture.detectChanges();
const gridBody = fixture.debugElement.query(By.css('.igx-grid__tbody-content'));
expect(gridBody.nativeElement.innerText).toMatch(hierarchicalGrid.emptyGridMessage);
fixture.componentInstance.data = SampleTestData.generateHGridData(40, 3);
fixture.detectChanges();
hierarchicalGrid.filter('ID', '123450', IgxStringFilteringOperand.instance().condition('contains'), true);
fixture.detectChanges();
expect(gridBody.nativeElement.innerText).toMatch(hierarchicalGrid.emptyFilteredGridMessage);
}));
it('should apply classes to the header when filter row is visible', fakeAsync(() => {
hierarchicalGrid.rowSelection = GridSelectionMode.multiple;
fixture.detectChanges();
const headerExpander: HTMLElement = HierarchicalGridFunctions.getExpander(fixture);
const headerCheckbox: HTMLElement = GridSelectionFunctions.getRowCheckboxDiv(fixture.nativeElement);
expect(HierarchicalGridFunctions.isExpander(headerExpander, '--push')).toBeFalsy();
expect(GridSelectionFunctions.isCheckbox(headerCheckbox, '--push')).toBeFalsy();
// open filter row
GridFunctions.clickFilterCellChipUI(fixture, 'ID');
expect(HierarchicalGridFunctions.isExpander(headerExpander, '--push')).toBeTruthy();
expect(GridSelectionFunctions.isCheckbox(headerCheckbox, '--push')).toBeTruthy();
}));
});
describe('Summaries', () => {
const SUMMARIES_MARGIN_CLASS = '.igx-grid__summaries-patch';
it('should allow defining summaries for child grid and child should be sized correctly.', fakeAsync(() => {
// expand first row
hierarchicalGrid.expandRow(hierarchicalGrid.dataRowList.first.key);
// summaries seem to require this additional change detection call with Ivy disabled to display for the child grid
fixture.detectChanges();
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0] as IgxHierarchicalGridComponent;
const expander = (childGrid.dataRowList.first as IgxHierarchicalRowComponent).expander;
// Expect expansion cell to be rendered and sized the same as the expansion cell inside the grid
const summaryRow = childGrid.summariesRowList.first.nativeElement;
const summaryRowIndentation = summaryRow.querySelector(SUMMARIES_MARGIN_CLASS);
expect(summaryRow.children.length).toEqual(2);
expect(summaryRowIndentation.offsetWidth).toEqual(expander.nativeElement.offsetWidth);
const gridHeight = childGrid.nativeElement.offsetHeight;
const childElements: HTMLElement[] = Array.from(childGrid.nativeElement.children) as HTMLElement [];
const elementsHeight = childElements.map(elem => elem.offsetHeight).reduce((total, height) => total + height, 0);
// Expect the combined height of all elements (header, body, footer etc) to equal the calculated height of the grid.
expect(elementsHeight).toEqual(gridHeight);
// expand first row of child
childGrid.expandRow(childGrid.dataRowList.first.key);
const grandChild = childGrid.gridAPI.getChildGrids(false)[0];
const grandChildSummaryRow = grandChild.summariesRowList.first.nativeElement;
const childSummaryRowIndentation = grandChildSummaryRow.querySelector(SUMMARIES_MARGIN_CLASS);
expect(grandChildSummaryRow.children.length).toEqual(1);
expect(childSummaryRowIndentation).toBeNull();
}));
it('should size summaries with row selectors for parent and child grids correctly.', fakeAsync(() => {
hierarchicalGrid.rowSelection = GridSelectionMode.multiple;
fixture.detectChanges();
// expand first row
hierarchicalGrid.expandRow(hierarchicalGrid.dataRowList.first.key);
// summaries seem to require this additional change detection call with Ivy disabled to display for the child grid
fixture.detectChanges();
const rootExpander = (hierarchicalGrid.dataRowList.first as IgxHierarchicalRowComponent).expander;
const rootCheckbox = hierarchicalGrid.headerSelectorContainer;
const rootSummaryRow = hierarchicalGrid.summariesRowList.first.nativeElement;
const rootSummaryIndentation = rootSummaryRow.querySelector(SUMMARIES_MARGIN_CLASS);
expect(rootSummaryRow.children.length).toEqual(2);
expect(rootSummaryIndentation.offsetWidth)
.toEqual(rootExpander.nativeElement.offsetWidth + rootCheckbox.nativeElement.offsetWidth);
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const expander = childGrid.dataRowList.first.expander;
// Expect expansion cell to be rendered and sized the same as the expansion cell inside the grid
const summaryRow = childGrid.summariesRowList.first.nativeElement;
const childSummaryIndentation = summaryRow.querySelector(SUMMARIES_MARGIN_CLASS);
expect(summaryRow.children.length).toEqual(2);
expect(childSummaryIndentation.offsetWidth).toEqual(expander.nativeElement.offsetWidth);
}));
it('should size summaries for parent and child grids correctly when display density is changed and summaryRowHeight is set to falsy value', () => {
hierarchicalGrid.displayDensity = DisplayDensity.comfortable;
fixture.detectChanges();
hierarchicalGrid.expandRow(hierarchicalGrid.dataRowList.first.key);
hierarchicalGrid.summaryRowHeight = 0;
fixture.detectChanges();
let childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
let tFoot = hierarchicalGrid.nativeElement.querySelector('.igx-grid__tfoot');
let childTFoot = childGrid.nativeElement.querySelector('.igx-grid__tfoot');
expect(tFoot.getBoundingClientRect().height).toBe(hierarchicalGrid.defaultSummaryHeight);
expect(childTFoot.getBoundingClientRect().height).toBe(hierarchicalGrid.defaultSummaryHeight);
hierarchicalGrid.displayDensity = DisplayDensity.cosy;
hierarchicalGrid.summaryRowHeight = 0;
fixture.detectChanges();
childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
tFoot = hierarchicalGrid.nativeElement.querySelector('.igx-grid__tfoot');
childTFoot = childGrid.nativeElement.querySelector('.igx-grid__tfoot');
expect(tFoot.getBoundingClientRect().height).toBe(hierarchicalGrid.defaultSummaryHeight);
expect(childTFoot.getBoundingClientRect().height).toBe(hierarchicalGrid.defaultSummaryHeight);
hierarchicalGrid.displayDensity = DisplayDensity.compact;
hierarchicalGrid.summaryRowHeight = 0;
fixture.detectChanges();
childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
tFoot = hierarchicalGrid.nativeElement.querySelector('.igx-grid__tfoot');
childTFoot = childGrid.nativeElement.querySelector('.igx-grid__tfoot');
expect(tFoot.getBoundingClientRect().height).toBe(hierarchicalGrid.defaultSummaryHeight);
expect(childTFoot.getBoundingClientRect().height).toBe(hierarchicalGrid.defaultSummaryHeight);
})
it('should render summaries for column inside a column group.', fakeAsync(() => {
const count = fixture.componentInstance.rowIsland.columns.length - 1;
fixture.componentInstance.rowIsland.columns[0].hasSummary = false;
fixture.componentInstance.rowIsland.columns[count].hasSummary = true;
fixture.detectChanges();
// expand first row
hierarchicalGrid.expandRow(hierarchicalGrid.dataRowList.first.key);
// summaries seem to require this additional change detection call with Ivy disabled to display for the child grid
fixture.detectChanges();
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const summaryRow = childGrid.summariesRowList.first;
expect(summaryRow.nativeElement.children.length).toEqual(2);
expect(summaryRow.summaryCells.length).toEqual(3);
}));
});
describe('Paging', () => {
it('should work on data records only when paging is enabled and should not be affected by child grid rows.', fakeAsync(() => {
fixture.componentInstance.paging = true;
fixture.detectChanges();
hierarchicalGrid.notifyChanges();
fixture.detectChanges();
expect(hierarchicalGrid.dataView.length).toEqual(15);
const dataRows = hierarchicalGrid.dataRowList.toArray();
// expand 1st row
hierarchicalGrid.expandRow(dataRows[0].key);
tick(DEBOUNCE_TIME);
fixture.detectChanges();
expect(hierarchicalGrid.dataView.length).toEqual(16);
// expand 2nd row
hierarchicalGrid.expandRow(dataRows[1].key);
tick(DEBOUNCE_TIME);
fixture.detectChanges();
expect(hierarchicalGrid.dataView.length).toEqual(17);
expect(hierarchicalGrid.dataView.pop().ID).toEqual('14');
}));
it('should preserve expansion states after changing pages.', fakeAsync(() => {
fixture.componentInstance.paging = true;
fixture.detectChanges();
let dataRows = hierarchicalGrid.dataRowList.toArray() as IgxHierarchicalRowComponent[];
// expand 1st row
hierarchicalGrid.expandRow(dataRows[0].key);
// expand 2nd row
hierarchicalGrid.expandRow(dataRows[1].key);
expect(dataRows[0].expanded).toBeTruthy();
expect(dataRows[1].expanded).toBeTruthy();
expect(hierarchicalGrid.dataView.length).toEqual(17);
let childGrids = hierarchicalGrid.gridAPI.getChildGrids(false);
expect(childGrids.length).toEqual(2);
expect(childGrids[0].dataRowList.first.cells.first.value).toEqual('00');
// Go to next page
GridFunctions.navigateToNextPage(hierarchicalGrid.nativeElement);
fixture.detectChanges();
dataRows = hierarchicalGrid.dataRowList.toArray() as IgxHierarchicalRowComponent[];
expect(dataRows[0].cells.first.value).toEqual('15');
expect(dataRows[0].expanded).toBeFalsy();
expect(dataRows[1].expanded).toBeFalsy();
expect(hierarchicalGrid.dataView.length).toEqual(15);
childGrids = hierarchicalGrid.gridAPI.getChildGrids(false);
// Return to previous page
GridFunctions.navigateToPrevPage(hierarchicalGrid.nativeElement);
fixture.detectChanges();
dataRows = hierarchicalGrid.dataRowList.toArray() as IgxHierarchicalRowComponent[];
expect(dataRows[0].cells.first.value).toEqual('0');
expect(dataRows[0].expanded).toBeTruthy();
expect(dataRows[1].expanded).toBeTruthy();
expect(hierarchicalGrid.dataView.length).toEqual(17);
childGrids = hierarchicalGrid.gridAPI.getChildGrids(false);
expect(childGrids[0].dataRowList.first.cells.first.value).toEqual('00');
}));
it('should allow scrolling to the last row after page size has been changed and rows are expanded.', fakeAsync(() => {
/* it's better to avoid scrolling and only check for scrollbar availability */
/* scrollbar doesn't update its visibility in fakeAsync tests */
fixture.componentInstance.paging = true;
fixture.detectChanges();
hierarchicalGrid.perPage = 20;
hierarchicalGrid.height = '800px';
fixture.componentInstance.rowIsland.height = '200px';
tick();
fixture.detectChanges();
expect(hierarchicalGrid.hasVerticalScroll()).toBeTruthy();
hierarchicalGrid.perPage = 5;
tick(DEBOUNCE_TIME);
fixture.detectChanges();
expect(hierarchicalGrid.hasVerticalScroll()).toBeFalsy();
const dataRows = hierarchicalGrid.dataRowList.toArray() as IgxHierarchicalRowComponent[];
// expand 1st row
hierarchicalGrid.expandRow(dataRows[0].key);
tick(DEBOUNCE_TIME);
fixture.detectChanges();
expect(hierarchicalGrid.hasVerticalScroll()).toBeFalsy();
expect(hierarchicalGrid.gridAPI.get_row_by_index(1) instanceof IgxChildGridRowComponent).toBeTruthy();
// expand 3rd row
hierarchicalGrid.expandRow(dataRows[3].key);
tick(DEBOUNCE_TIME);
fixture.detectChanges();
expect(hierarchicalGrid.gridAPI.get_row_by_index(4) instanceof IgxChildGridRowComponent).toBeTruthy();
}));
it('should correctly hide/show vertical scrollbar after page is changed.', (async () => {
/* scrollbar doesn't update its visibility in fakeAsync tests */
fixture.componentInstance.paging = true;
fixture.detectChanges();
hierarchicalGrid.perPage = 5;
fixture.detectChanges();
expect(hierarchicalGrid.hasVerticalScroll()).toBeFalsy();
// expand 1st row
hierarchicalGrid.expandRow(hierarchicalGrid.dataRowList.first.key);
await wait(DEBOUNCE_TIME);
expect(hierarchicalGrid.hasVerticalScroll()).toBeTruthy();
// change page
hierarchicalGrid.page = 1;
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
expect(hierarchicalGrid.hasVerticalScroll()).toBeFalsy();
// change page
hierarchicalGrid.page = 0;
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
expect(hierarchicalGrid.hasVerticalScroll()).toBeTruthy();
}));
});
describe('Hiding', () => {
it('should leave no feature UI elements when all columns are hidden', fakeAsync(() => {
fixture.componentInstance.paging = true;
fixture.detectChanges();
hierarchicalGrid.rowSelection = GridSelectionMode.multiple;
hierarchicalGrid.rowDraggable = true;
tick(DEBOUNCE_TIME);
fixture.detectChanges();
let headers = GridFunctions.getColumnHeaders(fixture);
let gridRows = HierarchicalGridFunctions.getHierarchicalRows(fixture);
const paging = GridFunctions.getGridPaginator(fixture);
let rowSelectors = GridSelectionFunctions.getCheckboxes(fixture);
let dragIndicators = GridFunctions.getDragIndicators(fixture);
let expander = HierarchicalGridFunctions.getExpander(fixture, '[hidden]');
expect(headers.length).toBeGreaterThan(0);
expect(gridRows.length).toBeGreaterThan(0);
expect(paging).not.toBeNull();
expect(rowSelectors.length).toBeGreaterThan(0);
expect(dragIndicators.length).toBeGreaterThan(0);
// this check executes correctly on Ivy only
// expect(Object.keys(expanders[0].attributes)).not.toContain('hidden');
expect(expander).toBeNull();
expect(hierarchicalGrid.hasVerticalScroll()).toBeTruthy();
hierarchicalGrid.columnList.forEach((col) => col.hidden = true);
tick(DEBOUNCE_TIME);
fixture.detectChanges();
headers = GridFunctions.getColumnHeaders(fixture);
gridRows = HierarchicalGridFunctions.getHierarchicalRows(fixture);
rowSelectors = GridSelectionFunctions.getCheckboxes(fixture);
dragIndicators = GridFunctions.getDragIndicators(fixture);
expander = HierarchicalGridFunctions.getExpander(fixture, '[hidden]');
expect(headers.length).toBe(0);
expect(gridRows.length).toBe(0);
expect(rowSelectors.length).toBe(0);
expect(dragIndicators.length).toBe(0);
// this check executes correctly on Ivy only
// expect(Object.keys(expanders[0].attributes)).toContain('hidden');
expect(expander).not.toBeNull();
expect(hierarchicalGrid.hasVerticalScroll()).toBeFalsy();
}));
});
describe('Toolbar', () => {
it('should be displayed correctly for child layout and hiding should apply to the correct child.', fakeAsync(() => {
pending('Change test for new scrollbar structure');
hierarchicalGrid.expandRow(hierarchicalGrid.dataRowList.first.key);
tick();
fixture.detectChanges();
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const toolbar = childGrid.nativeElement.querySelector('igx-grid-toolbar');
const hidingUI = toolbar.querySelector('igx-grid-toolbar-hiding') as HTMLElement;
// Check if visible columns and headers are rendered correctly
expect(childGrid.visibleColumns.length).toEqual(4);
// Check if hiding button & dropdown are init
expect(toolbar).toBeDefined();
expect(hidingUI).toBeDefined();
hidingUI.click();
tick();
fixture.detectChanges();
// // Check if the child grid columns are the one used by the hiding UI
// childGrid.visibleColumns.forEach((column, index) => expect(toolbar.columnHidingUI.columns[index]).toEqual(column));
// // Instead of clicking we can just toggle the checkbox
// toolbar.columnHidingUI.columnItems.toArray()[2].toggle();
// fixture.detectChanges();
// And it should hide the column of the child grid
// expect(childGrid.visibleColumns.length).toEqual(3);
}));
it('should be displayed correctly for child layout and pinning should apply to the correct child.', fakeAsync(() => {
pending('Change test for new scrollbar structure');
hierarchicalGrid.expandRow(hierarchicalGrid.dataRowList.first.key);
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const toolbar = childGrid.nativeElement.querySelector('igx-grid-toolbar');
// Check if visible columns and headers are rendered correctly
expect(childGrid.visibleColumns.length).toEqual(4);
// Check if pinning button & dropdown are init
expect(toolbar).toBeDefined();
expect(toolbar.querySelector('igx-grid-toolbar-pinning')).toBeDefined();
// Check if the child grid columns are the one used by the pinning UI
// childGrid.visibleColumns.forEach((column, index) => expect(toolbar.columnPinningUI.columns[index]).toEqual(column));
// Instead of clicking we can just toggle the checkbox
// toolbar.columnPinningUI.columnItems.toArray()[1].toggle();
fixture.detectChanges();
// Check pinned state
expect(childGrid.getColumnByName('ChildLevels').pinned).toBeTruthy();
expect(childGrid.getColumnByName('ProductName').pinned).toBeTruthy();
expect(childGrid.getColumnByName('ID').pinned).toBeFalsy();
}));
it('should read from custom templates per level', fakeAsync(() => {
pending('Change test for new scrollbar structure');
fixture = TestBed.createComponent(IgxHierarchicalGridTestCustomToolbarComponent);
tick();
fixture.detectChanges();
hierarchicalGrid = fixture.componentInstance.hgrid;
hierarchicalGrid.expandRow(hierarchicalGrid.dataRowList.first.key);
tick(DEBOUNCE_TIME);
fixture.detectChanges();
const toolbars = fixture.debugElement.queryAll(By.css('igx-grid-toolbar'));
expect(toolbars.length).toEqual(3);
expect(toolbars[0].query(By.css('button')).nativeElement.innerText.trim()).toEqual('Parent Button');
expect(toolbars[1].query(By.css('button')).nativeElement.innerText.trim()).toEqual('Child 1 Button');
expect(toolbars[2].query(By.css('button')).nativeElement.innerText.trim()).toEqual('Child 2 Button');
}));
it('should have same width as the grid whole width', fakeAsync(() => {
fixture = TestBed.createComponent(IgxHierarchicalGridTestCustomToolbarComponent);
tick();
fixture.detectChanges();
hierarchicalGrid = fixture.componentInstance.hgrid;
const toolbar = fixture.debugElement.query(By.css('igx-grid-toolbar'));
expect(toolbar.nativeElement.offsetWidth).toEqual(hierarchicalGrid.nativeElement.offsetWidth);
}));
});
describe('Moving', () => {
// TODO: Revise this test! That DOM digging is sloppy
xit('should not be possible to drag move a column from another grid.', (async () => {
hierarchicalGrid.expandRow(hierarchicalGrid.dataRowList.first.key);
const childGrids = fixture.debugElement.queryAll(By.css('igx-child-grid-row'));
const childHeader = childGrids[0].queryAll(By.css('igx-grid-header'))[0].nativeElement;
const mainHeaders = hierarchicalGrid.nativeElement
.querySelectorAll('igx-grid-header[ng-reflect-grid-i-d="' + hierarchicalGrid.id + '"]');
const childHeaderX = childHeader.getBoundingClientRect().x + childHeader.getBoundingClientRect().width / 2;
const childHeaderY = childHeader.getBoundingClientRect().y + childHeader.getBoundingClientRect().height / 2;
const mainHeaderX = mainHeaders[0].getBoundingClientRect().x + mainHeaders[0].getBoundingClientRect().width / 2;
const mainHeaderY = mainHeaders[0].getBoundingClientRect().y + mainHeaders[0].getBoundingClientRect().height / 2;
UIInteractions.simulatePointerEvent('pointerdown', childHeader, childHeaderX, childHeaderY);
await wait();
fixture.detectChanges();
UIInteractions.simulatePointerEvent('pointermove', childHeader, childHeaderX, childHeaderY - 10);
await wait(100);
fixture.detectChanges();
UIInteractions.simulatePointerEvent('pointermove', childHeader, mainHeaderX + 50, mainHeaderY);
await wait(100);
fixture.detectChanges();
// The moving indicator shouldn't show that a column can be moved.
const childGroupHeader = childGrids[0].query(By.css('igx-grid-header')).injector.get(IgxColumnMovingDragDirective);
const dragElem = childGroupHeader.ghostElement;
const dragIcon = dragElem.querySelector('i');
expect(dragElem).toBeDefined();
expect(dragIcon.innerText.trim()).toEqual('block');
UIInteractions.simulatePointerEvent('pointerup', childHeader, mainHeaderX + 50, mainHeaderY);
await wait();
fixture.detectChanges();
expect(hierarchicalGrid.columnList.length).toEqual(4);
// expect(mainHeaders.length).toEqual(3);
// expect(mainHeaders[0].children[0].innerText.trim()).toEqual('ID');
// expect(mainHeaders[1].children[0].innerText.trim()).toEqual('ChildLevels');
// expect(mainHeaders[2].children[0].innerText.trim()).toEqual('ProductName');
}));
});
describe('Pinning', () => {
it('should be possible by templating the header and getting column reference for child grid', fakeAsync(() => {
hierarchicalGrid.expandRow(hierarchicalGrid.dataRowList.first.key);
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
let childHeader = GridFunctions.getColumnHeaders(fixture)[3];
const firstHeaderIcon = childHeader.query(By.css('.igx-icon'));
expect(GridFunctions.isHeaderPinned(childHeader.parent)).toBeFalsy();
expect(childGrid.columns[0].pinned).toBeFalsy();
expect(firstHeaderIcon).toBeDefined();
UIInteractions.simulateClickAndSelectEvent(firstHeaderIcon);
fixture.detectChanges();
tick();
childHeader = GridFunctions.getColumnHeaders(fixture)[3];
expect(childGrid.columns[0].pinned).toBeTruthy();
expect(GridFunctions.isHeaderPinned(childHeader.parent)).toBeTruthy();
}));
it('should be applied correctly for child grid with multi-column header.', fakeAsync(() => {
fixture.componentInstance.rowIsland.columnList.find(x => x.header === 'Information').pinned = true;
tick(DEBOUNCE_TIME);
fixture.detectChanges();
hierarchicalGrid.expandRow(hierarchicalGrid.dataRowList.first.key);
tick(DEBOUNCE_TIME);
fixture.detectChanges();
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
// check unpinned/pinned columns
expect(childGrid.pinnedColumns.length).toBe(3);
expect(childGrid.unpinnedColumns.length).toBe(1);
// check cells
expect(childGrid.gridAPI.get_row_by_index(0).cells.length).toBe(3);
let cell = childGrid.gridAPI.get_cell_by_index(0, 'ChildLevels');
expect(cell.visibleColumnIndex).toEqual(0);
expect(GridFunctions.isCellPinned(cell)).toBeTruthy();
cell = childGrid.gridAPI.get_cell_by_index(0, 'ProductName');
expect(cell.visibleColumnIndex).toEqual(1);
expect(GridFunctions.isCellPinned(cell)).toBeTruthy();
cell = childGrid.gridAPI.get_cell_by_index(0, 'ID');
expect(cell.visibleColumnIndex).toEqual(2);
expect(GridFunctions.isCellPinned(cell)).toBeFalsy();
}));
it('should be applied correctly even on the right side', fakeAsync(() => {
hierarchicalGrid = fixture.componentInstance.hgrid;
hierarchicalGrid.columnList.find(x => x.field === 'ID').pinned = true;
hierarchicalGrid.pinning.columns = 1;
hierarchicalGrid.cdr.detectChanges();
tick();
fixture.detectChanges();
const rightMostGridPart = hierarchicalGrid.nativeElement.getBoundingClientRect().right;
const leftMostGridPart = hierarchicalGrid.nativeElement.getBoundingClientRect().left;
const leftMostRightPinnedCellsPart = hierarchicalGrid.gridAPI.get_cell_by_index(0, 'ID')
.nativeElement.getBoundingClientRect().left;
const pinnedCellWidth = hierarchicalGrid.getCellByColumn(0, 'ID').width;
// Expects that right pinning has been in action
expect(leftMostGridPart).not.toEqual(leftMostRightPinnedCellsPart);
// Expects that pinned column is in the visible grid's area
expect(leftMostRightPinnedCellsPart).toBeLessThan(rightMostGridPart);
// Expects that the whole pinned column is visible
expect(leftMostRightPinnedCellsPart + Number.parseInt(pinnedCellWidth, 10)).toBeLessThan(rightMostGridPart);
}));
});
describe('Row Pinning', () => {
const FIXED_ROW_CONTAINER = '.igx-grid__tr--pinned';
const FIXED_ROW_CONTAINER_TOP = 'igx-grid__tr--pinned-top';
const FIXED_ROW_CONTAINER_BOTTOM = 'igx-grid__tr--pinned-bottom';
beforeEach(() => {
hierarchicalGrid.width = '800px';
hierarchicalGrid.height = '500px';
fixture.detectChanges();
});
it('should pin rows to top ', (() => {
hierarchicalGrid.pinRow('0');
expect(hierarchicalGrid.pinnedRows.length).toBe(1);
hierarchicalGrid.unpinRow('0');
expect(hierarchicalGrid.pinnedRows.length).toBe(0);
hierarchicalGrid.pinRow('0');
expect(hierarchicalGrid.pinnedRows.length).toBe(1);
let pinRowContainer = fixture.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer.length).toBe(1);
expect(pinRowContainer[0].nativeElement.classList.contains(FIXED_ROW_CONTAINER_TOP)).toBeTruthy();
expect(pinRowContainer[0].nativeElement.classList.contains(FIXED_ROW_CONTAINER_BOTTOM)).toBeFalsy();
expect(pinRowContainer[0].children[0].context.key).toBe('0');
expect(hierarchicalGrid.getRowByIndex(1).key).toBe('0');
expect(hierarchicalGrid.getRowByIndex(2).key).toBe('1');
expect(hierarchicalGrid.getRowByIndex(3).key).toBe('2');
hierarchicalGrid.pinRow('2');
pinRowContainer = fixture.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer[0].children.length).toBe(2);
expect(pinRowContainer[0].children[0].context.key).toBe('0');
expect(pinRowContainer[0].children[1].context.key).toBe('2');
expect(hierarchicalGrid.getRowByIndex(2).key).toBe('0');
expect(hierarchicalGrid.getRowByIndex(3).key).toBe('1');
expect(hierarchicalGrid.getRowByIndex(4).key).toBe('2');
fixture.detectChanges();
expect(hierarchicalGrid.pinnedRowHeight).toBe(2 * hierarchicalGrid.renderedRowHeight + 2);
const expectedHeight = parseInt(hierarchicalGrid.height, 10) -
hierarchicalGrid.pinnedRowHeight - 18 - hierarchicalGrid.theadRow.nativeElement.offsetHeight;
expect(hierarchicalGrid.calcHeight - expectedHeight).toBeLessThanOrEqual(1);
}));
it('should pin rows to bottom', (() => {
fixture.componentInstance.pinningConfig = { columns: ColumnPinningPosition.Start, rows: RowPinningPosition.Bottom };
fixture.detectChanges();
// Pin 2nd row
hierarchicalGrid.pinRow('1');
fixture.detectChanges();
expect(hierarchicalGrid.pinnedRows.length).toBe(1);
let pinRowContainer = fixture.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer.length).toBe(1);
expect(pinRowContainer[0].nativeElement.classList.contains(FIXED_ROW_CONTAINER_TOP)).toBeFalsy();
expect(pinRowContainer[0].nativeElement.classList.contains(FIXED_ROW_CONTAINER_BOTTOM)).toBeTruthy();
expect(pinRowContainer[0].children.length).toBe(1);
expect(pinRowContainer[0].children[0].context.key).toBe('1');
expect(pinRowContainer[0].children[0].context.index).toBe(fixture.componentInstance.data.length);
expect(pinRowContainer[0].children[0].nativeElement)
.toBe(hierarchicalGrid.gridAPI.get_row_by_index(fixture.componentInstance.data.length).nativeElement);
expect(hierarchicalGrid.getRowByIndex(0).key).toBe('0');
expect(hierarchicalGrid.getRowByIndex(1).key).toBe('1');
expect(hierarchicalGrid.getRowByIndex(2).key).toBe('2');
// Pin 1st row
hierarchicalGrid.pinRow('0');
fixture.detectChanges();
pinRowContainer = fixture.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer[0].children.length).toBe(2);
expect(pinRowContainer[0].children[0].context.key).toBe('1');
expect(pinRowContainer[0].children[1].context.key).toBe('0');
expect(hierarchicalGrid.getRowByIndex(0).key).toBe('0');
expect(hierarchicalGrid.getRowByIndex(1).key).toBe('1');
expect(hierarchicalGrid.getRowByIndex(2).key).toBe('2');
fixture.detectChanges();
// Check last pinned is fully in view
const last = pinRowContainer[0].children[1].context.nativeElement;
expect(last.getBoundingClientRect().bottom - hierarchicalGrid.tbody.nativeElement.getBoundingClientRect().bottom).toBe(0);
// 2 records pinned + 2px border
expect(hierarchicalGrid.pinnedRowHeight).toBe(2 * hierarchicalGrid.renderedRowHeight + 2);
const expectedHe