import { configureTestSuite } from '../../test-utils/configure-suite'; import { TestBed, fakeAsync, tick, ComponentFixture, waitForAsync } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { IGridCreatedEventArgs } from './public_api'; import { ChangeDetectorRef, Component, ViewChild, AfterViewInit, QueryList } from '@angular/core'; import { IgxChildGridRowComponent, IgxHierarchicalGridComponent } from './hierarchical-grid.component'; import { wait, UIInteractions } from '../../test-utils/ui-interactions.spec'; import { IgxRowIslandComponent } from './row-island.component'; import { IgxHierarchicalRowComponent } from './hierarchical-row.component'; import { By } from '@angular/platform-browser'; import { DisplayDensity } from '../../core/density'; import { IgxStringFilteringOperand } from '../../data-operations/filtering-condition'; import { IgxHeaderCollapsedIndicatorDirective, IgxHeaderExpandedIndicatorDirective, IgxRowCollapsedIndicatorDirective, IgxRowExpandedIndicatorDirective } from '../grid/public_api'; import { GridSelectionMode } from '../common/enums'; import { GridFunctions } from '../../test-utils/grid-functions.spec'; import { IgxGridCellComponent } from '../cell.component'; import { NgFor, NgIf } from '@angular/common'; import { IgxIconComponent } from '../../icon/icon.component'; import { IgxExcelStyleColumnOperationsTemplateDirective, IgxExcelStyleFilterOperationsTemplateDirective, IgxGridExcelStyleFilteringComponent } from '../filtering/excel-style/excel-style-filtering.component'; import { IgxExcelStyleHeaderComponent } from '../filtering/excel-style/excel-style-header.component'; import { IgxExcelStyleSortingComponent } from '../filtering/excel-style/excel-style-sorting.component'; import { IgxExcelStyleSearchComponent } from '../filtering/excel-style/excel-style-search.component'; import { IgxCellHeaderTemplateDirective } from '../columns/templates.directive'; import { CellType, ColumnType, IGridCellEventArgs, IgxColumnComponent, IgxRowEditActionsDirective, IgxRowEditTextDirective } from '../public_api'; describe('Basic IgxHierarchicalGrid #hGrid', () => { configureTestSuite(); beforeAll(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ NoopAnimationsModule, IgxHierarchicalGridTestBaseComponent, IgxHierarchicalGridMultiLayoutComponent, IgxHierarchicalGridSizingComponent, IgxHGridRemoteOnDemandComponent, IgxHierarchicalGridColumnsUpdateComponent, IgxHierarchicalGridHidingPinningColumnsComponent, IgxHierarchicalGridToggleRIComponent, IgxHierarchicalGridCustomRowEditOverlayComponent, IgxHierarchicalGridAutoSizeColumnsComponent, IgxHierarchicalGridCustomTemplateComponent, IgxHierarchicalGridCustomFilteringTemplateComponent, IgxHierarchicalGridToggleRIAndColsComponent ] }).compileComponents(); })) describe('Init IgxHierarchicalGrid #hGrid', () => { let fixture; let hierarchicalGrid: IgxHierarchicalGridComponent; beforeEach(() => { fixture = TestBed.createComponent(IgxHierarchicalGridTestBaseComponent); fixture.detectChanges(); hierarchicalGrid = fixture.componentInstance.hgrid; }); it('should render expansion indicator as the first element of each expandable row.', () => { fixture.componentInstance.data = [ {ID: 0, ProductName: 'Product: A0'}, {ID: 1, ProductName: 'Product: A1', childData: fixture.componentInstance.generateDataUneven(1, 1)}, {ID: 2, ProductName: 'Product: A2', childData: fixture.componentInstance.generateDataUneven(1, 1)} ]; fixture.detectChanges(); const row1 = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; expect(row1.hasChildren).toBe(true); const rowElems = fixture.debugElement.queryAll(By.directive(IgxHierarchicalRowComponent)); expect(rowElems[0].query(By.css('igx-icon')).nativeElement.innerText).toEqual('chevron_right'); const row2 = hierarchicalGrid.gridAPI.get_row_by_index(1) as IgxHierarchicalRowComponent; expect(row2.hasChildren).toBe(true); expect(rowElems[1].query(By.css('igx-icon')).nativeElement.innerText).toEqual('chevron_right'); const row3 = hierarchicalGrid.gridAPI.get_row_by_index(1) as IgxHierarchicalRowComponent; expect(row3.hasChildren).toBe(true); expect(rowElems[2].query(By.css('igx-icon')).nativeElement.innerText).toEqual('chevron_right'); }); it('should allow expand/collapse rows through the UI', () => { const row1 = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; expect(row1.expanded).toBe(false); UIInteractions.simulateClickAndSelectEvent(row1.expander); fixture.detectChanges(); expect(row1.expanded).toBe(true); expect(hierarchicalGrid.gridAPI.getChildGrids(false).length).toBe(1); expect(hierarchicalGrid.gridAPI.get_row_by_index(1) instanceof IgxChildGridRowComponent).toBe(true); UIInteractions.simulateClickAndSelectEvent(row1.expander); fixture.detectChanges(); expect(row1.expanded).toBe(false); expect(hierarchicalGrid.gridAPI.get_row_by_index(1) instanceof IgxHierarchicalRowComponent).toBe(true); }); it('should change expand/collapse indicators when state of the row changes', () => { const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; const rowElem = fixture.debugElement.queryAll(By.directive(IgxHierarchicalRowComponent))[0]; expect(rowElem.query(By.css('igx-icon')).nativeElement.innerText).toEqual('chevron_right'); UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); expect(rowElem.query(By.css('igx-icon')).nativeElement.innerText).toEqual('expand_more'); }); it('should collapse all rows that belongs to a grid via header collapse icon', () => { const headerExpanderElem = fixture.debugElement.queryAll(By.css('.igx-grid__hierarchical-expander--header'))[0]; let icon = headerExpanderElem.query(By.css('igx-icon')).componentInstance; let iconTxt = headerExpanderElem.query(By.css('igx-icon')).nativeElement.textContent.toLowerCase(); expect(iconTxt).toBe('unfold_less'); expect(icon.getActive).toBe(false); // expand row const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); icon = headerExpanderElem.query(By.css('igx-icon')).componentInstance; iconTxt = headerExpanderElem.query(By.css('igx-icon')).nativeElement.textContent.toLowerCase(); expect(iconTxt).toBe('unfold_less'); expect(icon.getActive).toBe(true); expect(hierarchicalGrid.expansionStates.size).toEqual(1); UIInteractions.simulateClickAndSelectEvent(icon.el); fixture.detectChanges(); const rows = hierarchicalGrid.dataRowList.toArray() as IgxHierarchicalRowComponent[]; rows.forEach((r) => { expect(r.expanded).toBe(false); }); icon = headerExpanderElem.query(By.css('igx-icon')).componentInstance; iconTxt = headerExpanderElem.query(By.css('igx-icon')).nativeElement.textContent.toLowerCase(); expect(iconTxt).toBe('unfold_less'); expect(icon.getActive).toBe(false); expect(hierarchicalGrid.expansionStates.size).toEqual(0); }); it ('checks if attributes are correctly assigned when grid has or does not have data', () => { // 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 hierarchicalGrid.filter('index','111',IgxStringFilteringOperand.instance().condition('contains'),true); hierarchicalGrid.markForCheck(); fixture.detectChanges(); expect(container.getAttribute('role')).toMatch('row'); // clear grid data and check if attribute is now 'row' hierarchicalGrid.clearFilter(); fixture.componentInstance.clearData(); fixture.detectChanges(); expect(container.getAttribute('role')).toMatch('row'); }); it('should allow applying initial expansions state for certain rows through expansionStates option', () => { // set first row as expanded. const state = new Map(); state.set(fixture.componentInstance.data[0], true); hierarchicalGrid.expansionStates = state; hierarchicalGrid.cdr.detectChanges(); const row1 = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; // verify row is expanded expect(row1.expanded).toBe(true); expect(hierarchicalGrid.gridAPI.getChildGrids(false).length).toBe(1); expect(hierarchicalGrid.gridAPI.get_row_by_index(1) instanceof IgxChildGridRowComponent).toBe(true); }); it('should allow defining more than one nested row islands', () => { const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0]; const childRow = childGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(childRow.expander); fixture.detectChanges(); // should have 3 level hierarchy const allChildren = hierarchicalGrid.gridAPI.getChildGrids(true); expect(allChildren.length).toBe(2); expect(hierarchicalGrid.gridAPI.get_row_by_index(1) instanceof IgxChildGridRowComponent).toBe(true); expect(childGrid.gridAPI.get_row_by_index(1) instanceof IgxChildGridRowComponent).toBe(true); }); it('should retain expansion states when scrolling', async () => { const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); expect(row.expanded).toBe(true); // scroll to bottom hierarchicalGrid.verticalScrollContainer.scrollTo(hierarchicalGrid.dataView.length - 1); await wait(100); fixture.detectChanges(); // scroll to top hierarchicalGrid.verticalScrollContainer.scrollTo(0); await wait(100); fixture.detectChanges(); expect((hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent).expanded).toBe(true); }); it('should show header collapse button if grid has data and row island is defined.', () => { fixture.componentInstance.data = [ {ID: 0, ProductName: 'Product: A0'} ]; fixture.detectChanges(); const headerExpanderElem = fixture.debugElement.queryAll(By.css('.igx-grid__hierarchical-expander--header'))[0]; const icon = headerExpanderElem.query(By.css('igx-icon')); expect(icon).toBeDefined(); }); it('should render last cell of rows fully visible when columns does not have width specified and without scrollbar', () => { const firstRowCell: HTMLElement = (hierarchicalGrid.gridAPI.get_row_by_index(0).cells as QueryList).first.nativeElement; const cellLeftOffset = firstRowCell.offsetLeft + firstRowCell.parentElement.offsetLeft + firstRowCell.offsetWidth; const gridWidth = hierarchicalGrid.nativeElement.offsetWidth; expect(cellLeftOffset).not.toBeGreaterThan(gridWidth); const hScroll = hierarchicalGrid.headerContainer.getScroll(); expect((hScroll.firstElementChild as HTMLElement).offsetWidth).not.toBeGreaterThan(hScroll.offsetWidth); }); it('should allow extracting child grids using hgridAPI', () => { // set first row as expanded. const state = new Map(); state.set(fixture.componentInstance.data[0], true); hierarchicalGrid.expansionStates = state; hierarchicalGrid.cdr.detectChanges(); const row1 = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; // verify row is expanded expect(row1.expanded).toBe(true); const childGrid = hierarchicalGrid.gridAPI.getChildGrid([{ rowID: fixture.componentInstance.data[0], rowIslandKey: 'childData' }]); expect(childGrid).not.toBeNull(); const childState = new Map(); childState.set(fixture.componentInstance.data[0].childData[0], true); childGrid.expansionStates = childState; childGrid.cdr.detectChanges(); const grandChildGrid = hierarchicalGrid.gridAPI.getChildGrid([ { rowID: fixture.componentInstance.data[0], rowIslandKey: 'childData' }, { rowID: fixture.componentInstance.data[0].childData[0], rowIslandKey: 'childData' } ]); expect(grandChildGrid).not.toBeNull(); const rowIsland1 = hierarchicalGrid.gridAPI.getChildRowIsland('childData'); const rowIsland2 = hierarchicalGrid.allLayoutList.find(layout => layout.id === 'igx-row-island-childData-childData'); expect(rowIsland1.key).toBe('childData'); expect(rowIsland2.key).toBe('childData'); }); it('should allow setting expandChildren after bound to data', () => { // set first row as expanded. const state = new Map(); state.set(fixture.componentInstance.data[0], true); hierarchicalGrid.expansionStates = state; hierarchicalGrid.cdr.detectChanges(); let row1 = hierarchicalGrid.gridAPI.get_row_by_index(0); // verify row is expanded expect(row1.expanded).toBe(true); hierarchicalGrid.expandChildren = false; hierarchicalGrid.cdr.detectChanges(); row1 = hierarchicalGrid.gridAPI.get_row_by_index(0); expect(row1.expanded).toBe(false); const expandIcons = fixture.debugElement.queryAll(By.css('#igx-icon-15')); expect(expandIcons.length).toBe(0); let rows = hierarchicalGrid.dataRowList.toArray() as IgxHierarchicalRowComponent[]; rows.forEach((r) => { expect(r.expanded).toBe(false); }); hierarchicalGrid.expandChildren = true; hierarchicalGrid.cdr.detectChanges(); rows = hierarchicalGrid.dataRowList.toArray() as IgxHierarchicalRowComponent[]; rows.forEach((r) => { expect(r.expanded).toBe(true); }); row1 = hierarchicalGrid.gridAPI.get_row_by_index(0); expect(row1.expanded).toBe(true); }); it('should correctly expand children on init if parents have hasChild key', () => { hierarchicalGrid.expandChildren = true; hierarchicalGrid.hasChildrenKey = 'hasChild'; fixture.componentInstance.data = [ { ID: 1, ProductName: 'Product: A1', hasChild: false, childData: fixture.componentInstance.generateDataUneven(1, 1) }, { ID: 2, ProductName: 'Product: A2', hasChild: true, childData: fixture.componentInstance.generateDataUneven(1, 1) } ]; fixture.detectChanges(); expect(hierarchicalGrid.gridAPI.get_row_by_index(0)).toBeInstanceOf(IgxHierarchicalRowComponent); expect(hierarchicalGrid.gridAPI.get_row_by_index(1)).toBeInstanceOf(IgxHierarchicalRowComponent); expect(hierarchicalGrid.gridAPI.get_row_by_index(2)).toBeInstanceOf(IgxChildGridRowComponent); const rowElems = fixture.debugElement.queryAll(By.directive(IgxHierarchicalRowComponent)); expect(rowElems[0].query(By.css('igx-icon')).nativeElement.innerText).toEqual(''); expect(rowElems[1].query(By.css('igx-icon')).nativeElement.innerText).toEqual('expand_more'); }); it('should allow setting expandChildren after bound to data to rowIsland', () => { // set first row as expanded. const state = new Map(); state.set(fixture.componentInstance.data[0], true); hierarchicalGrid.expansionStates = state; hierarchicalGrid.cdr.detectChanges(); const row1 = hierarchicalGrid.gridAPI.get_row_by_index(0); // verify row is expanded expect(row1.expanded).toBe(true); // expand children for the rowIsland should be false by default expect(fixture.componentInstance.rowIsland.expandChildren).toBeFalsy(); fixture.componentInstance.rowIsland.expandChildren = true; fixture.detectChanges(); const childGrid = hierarchicalGrid.gridAPI.getChildGrid([{ rowID: fixture.componentInstance.data[0], rowIslandKey: 'childData' }]); const childRow = childGrid.getRowByIndex(0); expect(childRow.expanded).toBe(true); let rows = childGrid.dataRowList.toArray(); rows.forEach((r) => { expect(r.expanded).toBe(true); }); fixture.componentInstance.rowIsland.expandChildren = false; fixture.detectChanges(); rows = childGrid.dataRowList.toArray(); rows.forEach((r) => { expect(r.expanded).toBe(false); }); }); it('should be able to prevent exiting of edit mode when a row is toggled #10634', async () => { hierarchicalGrid.primaryKey = 'ID'; hierarchicalGrid.rowEditable = true; hierarchicalGrid.rowToggle.subscribe((e) => { e.cancel = true; }); fixture.detectChanges(); wait(); const masterGridFirstRow = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; expect(masterGridFirstRow.expanded).toBe(false); const masterGridSecondCell = masterGridFirstRow.cells.find((c: IgxGridCellComponent) => c.columnIndex === 1); expect(masterGridSecondCell.editMode).toBe(false); masterGridSecondCell.setEditMode(true); fixture.detectChanges(); wait(); expect(masterGridSecondCell.editMode).toBe(true); UIInteractions.simulateClickAndSelectEvent(masterGridFirstRow.expander); fixture.detectChanges(); wait(); expect(masterGridFirstRow.expanded).toBe(false); expect(masterGridSecondCell.editMode).toBe(true); hierarchicalGrid.rowToggle.subscribe((e) => { e.cancel = false; }); UIInteractions.simulateClickAndSelectEvent(masterGridFirstRow.expander); fixture.detectChanges(); wait(); expect(masterGridFirstRow.expanded).toBe(true); expect(masterGridSecondCell.editMode).toBe(true); const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0] as IgxHierarchicalGridComponent; expect(childGrid).toBeDefined(); childGrid.primaryKey = 'ID'; childGrid.rowEditable = true; childGrid.rowToggle.subscribe((e) => { e.cancel = true; }); fixture.detectChanges(); wait(); childGrid.columns.find(c => c.index === 1).editable = true; const childGridSecondRow = childGrid.gridAPI.get_row_by_index(1) as IgxHierarchicalRowComponent; expect(childGridSecondRow.expanded).toBe(false); const childGridSecondCell = childGridSecondRow.cells.find((c: IgxGridCellComponent) => c.columnIndex === 1); expect(childGridSecondCell.editMode).toBe(false); childGridSecondCell.setEditMode(true); fixture.detectChanges(); wait(); expect(childGridSecondCell.editMode).toBe(true); UIInteractions.simulateClickAndSelectEvent(childGridSecondRow.expander); fixture.detectChanges(); wait(); expect(childGrid.gridAPI.crudService.cellInEditMode).toBe(true); expect(childGridSecondRow.inEditMode).toBe(true); }); it('should render correctly when display density is changed', () => { const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const childGrids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); const childGrid = childGrids[0].query(By.css('igx-hierarchical-grid')).componentInstance; expect(hierarchicalGrid.displayDensity).toEqual(DisplayDensity.comfortable); expect(hierarchicalGrid.nativeElement.classList).toEqual(jasmine.arrayWithExactContents(['igx-grid'])); hierarchicalGrid.displayDensity = DisplayDensity.cosy; fixture.detectChanges(); expect(hierarchicalGrid.nativeElement.classList).toEqual(jasmine.arrayWithExactContents(['igx-grid--cosy'])); expect(childGrid.displayDensity).toBe(DisplayDensity.cosy); hierarchicalGrid.displayDensity = DisplayDensity.compact; fixture.detectChanges(); expect(hierarchicalGrid.nativeElement.classList).toEqual(jasmine.arrayWithExactContents(['igx-grid--compact'])); expect(childGrid.displayDensity).toBe(DisplayDensity.compact); }); it('should update child grid data when root grid data is changed.', () => { const newData1 = [ { ID: 0, ChildLevels: 0, ProductName: 'Product: A', childData: [ { ID: 1, ProductName: 'Product: Child A' } ] }, { ID: 1, ChildLevels: 0, ProductName: 'Product: A1', childData: [ { ID: 2, ProductName: 'Product: Child A' } ] }, { ID: 2, ChildLevels: 0, ProductName: 'Product: A2', childData: [ { ID: 3, ProductName: 'Product: Child A' } ] } ]; fixture.componentInstance.data = newData1; fixture.detectChanges(); let row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); let childGrids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); let childGrid = childGrids[0].query(By.css('igx-hierarchical-grid')).componentInstance; expect(childGrid.data).toBe(newData1[0].childData); const newData2 = [ { ID: 0, ChildLevels: 0, ProductName: 'Product: A', childData: [ { ID: 10, ProductName: 'Product: New Child A' } ] }, { ID: 1, ChildLevels: 0, ProductName: 'Product: A1', childData: [ { ID: 20, ProductName: 'Product: New Child A' } ] }, { ID: 2, ChildLevels: 0, ProductName: 'Product: A2', childData: [ { ID: 30, ProductName: 'Product: New Child A' } ] } ]; fixture.componentInstance.data = newData2; fixture.detectChanges(); row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); childGrids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); childGrid = childGrids[0].query(By.css('igx-hierarchical-grid')).componentInstance; expect(childGrid.data).toBe(newData2[0].childData); }); it('when child width is in percents its width should be update if parent width changes while parent row is collapsed. ', async () => { const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const childGrids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); const childGrid = childGrids[0].query(By.css('igx-hierarchical-grid')).componentInstance; expect(childGrid.calcWidth - 370).toBeLessThan(3); UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); fixture.componentInstance.width = '300px'; fixture.detectChanges(); UIInteractions.simulateClickAndSelectEvent(row.expander); await wait(); fixture.detectChanges(); expect(childGrid.calcWidth - 170).toBeLessThan(3); }); it('should exit edit mode on row expand/collapse through the UI', () => { hierarchicalGrid.primaryKey = 'ID'; hierarchicalGrid.rowEditable = true; fixture.detectChanges(); const masterGridFirstRow = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; expect(masterGridFirstRow.expanded).toBe(false); const masterGridSecondCell = masterGridFirstRow.cells.find((c: IgxGridCellComponent) => c.columnIndex === 1); expect(masterGridSecondCell.editMode).toBe(false); masterGridSecondCell.setEditMode(true); fixture.detectChanges(); expect(masterGridSecondCell.editMode).toBe(true); UIInteractions.simulateClickAndSelectEvent(masterGridFirstRow.expander); fixture.detectChanges(); expect(masterGridFirstRow.expanded).toBe(true); expect(masterGridSecondCell.editMode).toBe(true); const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0] as IgxHierarchicalGridComponent; expect(childGrid).toBeDefined(); childGrid.columns.find(c => c.index === 1).editable = true; const childGridSecondRow = childGrid.gridAPI.get_row_by_index(1) as IgxHierarchicalRowComponent; expect(childGridSecondRow.expanded).toBe(false); const childGridSecondCell = childGridSecondRow.cells.find((c: IgxGridCellComponent) => c.columnIndex === 1); expect(childGridSecondCell.editMode).toBe(false); childGridSecondCell.setEditMode(true); fixture.detectChanges(); expect(childGridSecondCell.editMode).toBe(true); UIInteractions.simulateClickAndSelectEvent(masterGridFirstRow.expander); fixture.detectChanges(); expect(childGrid.gridAPI.crudService.cellInEditMode).toBe(true); expect(childGridSecondRow.inEditMode).toBe(false); }); it('child grid width should be recalculated if parent no longer shows scrollbar.', fakeAsync(() => { hierarchicalGrid.height = '1000px'; fixture.detectChanges(); hierarchicalGrid.filter('ProductName', 'A0', IgxStringFilteringOperand.instance().condition('contains'), true); fixture.detectChanges(); const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); const childGrids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); const childGrid = childGrids[0].query(By.css('igx-hierarchical-grid')).componentInstance; expect(childGrid.calcWidth - 370 - childGrid.scrollSize).toBeLessThanOrEqual(5); hierarchicalGrid.clearFilter(); // Required to recalculate and reflect child grid size tick(); fixture.detectChanges(); expect(childGrid.calcWidth - 370).toBeLessThan(3); })); it('should not expand children when hasChildrenKey is false for the row', () => { hierarchicalGrid.hasChildrenKey = 'hasChild'; fixture.componentInstance.data = [ {ID: 1, ProductName: 'Product: A1', hasChild: false, childData: fixture.componentInstance.generateDataUneven(1, 1)}, {ID: 2, ProductName: 'Product: A2', hasChild: true, childData: fixture.componentInstance.generateDataUneven(1, 1)} ]; fixture.detectChanges(); const row1 = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; const rowElems = fixture.debugElement.queryAll(By.directive(IgxHierarchicalRowComponent)); expect(rowElems[0].query(By.css('igx-icon')).nativeElement.innerText).toEqual(''); const row2 = hierarchicalGrid.gridAPI.get_row_by_index(1) as IgxHierarchicalRowComponent; expect(rowElems[1].query(By.css('igx-icon')).nativeElement.innerText).toEqual('chevron_right'); hierarchicalGrid.expandRow(row1.data); hierarchicalGrid.expandRow(row2.data); expect(row1.expanded).toBe(false); expect(row2.expanded).toBe(true); hierarchicalGrid.expandAll(); expect(row1.expanded).toBe(false); }); it('should not expand children when hasChildrenKey is false for the row and there is primaryKey', () => { hierarchicalGrid.hasChildrenKey = 'hasChild'; hierarchicalGrid.primaryKey = 'ID'; fixture.componentInstance.data = [ {ID: 1, ProductName: 'Product: A1', hasChild: false, childData: fixture.componentInstance.generateDataUneven(1, 1)}, {ID: 2, ProductName: 'Product: A2', hasChild: true, childData: fixture.componentInstance.generateDataUneven(1, 1)} ]; fixture.detectChanges(); const row1 = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; const rowElems = fixture.debugElement.queryAll(By.directive(IgxHierarchicalRowComponent)); expect(rowElems[0].query(By.css('igx-icon')).nativeElement.innerText).toEqual(''); const row2 = hierarchicalGrid.gridAPI.get_row_by_index(1) as IgxHierarchicalRowComponent; expect(rowElems[1].query(By.css('igx-icon')).nativeElement.innerText).toEqual('chevron_right'); hierarchicalGrid.expandRow(1); hierarchicalGrid.expandRow(2); expect(row1.expanded).toBe(false); expect(row2.expanded).toBe(true); }); it('should update aria-activeDescendants when navigating around', () => { hierarchicalGrid.cellSelection = 'single'; expect(hierarchicalGrid.tbody.nativeElement.attributes['aria-activedescendant'].value).toEqual(hierarchicalGrid.id); let cellElem = (hierarchicalGrid.gridAPI.get_row_by_index(0).cells as QueryList).toArray()[1]; UIInteractions.simulatePointerOverElementEvent('pointerdown', cellElem.nativeElement); fixture.detectChanges(); expect(hierarchicalGrid.tbody.nativeElement.attributes['aria-activedescendant'].value).toEqual(`${hierarchicalGrid.id}_0_1`); const row1 = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row1.expander); fixture.detectChanges(); const childGrid = hierarchicalGrid.getChildGrids()[0]; expect(childGrid.tbody.nativeElement.attributes['aria-activedescendant'].value).toEqual(childGrid.id); cellElem = (childGrid.gridAPI.get_row_by_index(0).cells as QueryList).toArray()[1]; UIInteractions.simulatePointerOverElementEvent('pointerdown', cellElem.nativeElement); fixture.detectChanges(); expect(hierarchicalGrid.tbody.nativeElement.attributes['aria-activedescendant'].value).toEqual(hierarchicalGrid.id); expect(childGrid.tbody.nativeElement.attributes['aria-activedescendant'].value).toEqual(`${childGrid.id}_0_1`); }); }); describe('IgxHierarchicalGrid Row Islands #hGrid', () => { let fixture; let hierarchicalGrid: IgxHierarchicalGridComponent; beforeEach(() => { fixture = TestBed.createComponent(IgxHierarchicalGridMultiLayoutComponent); fixture.detectChanges(); hierarchicalGrid = fixture.componentInstance.hgrid; }); it('should allow defining row islands on the same level', () => { const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const childGrids = hierarchicalGrid.gridAPI.getChildGrids(false); const childRows = fixture.debugElement.queryAll(By.directive(IgxChildGridRowComponent)); expect(childGrids.length).toBe(2); expect(childRows.length).toBe(2); const ri1 = fixture.componentInstance.rowIsland1; const ri2 = fixture.componentInstance.rowIsland2; expect(childRows[0].componentInstance.layout).toBe(ri1); expect(childRows[1].componentInstance.layout).toBe(ri2); }); it('should display correct data for sibling row islands', () => { const uniqueData = [ { ID: 1, ProductName : 'Parent Name', childData: [ { ID: 11, ProductName : 'Child1 Name' } ], childData2: [ { ID: 12, Col1: 'Child2 Col1', Col2: 'Child2 Col2', Col3: 'Child2 Col3' } ] } ]; fixture.componentInstance.data = uniqueData; fixture.detectChanges(); const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const childGrids = hierarchicalGrid.gridAPI.getChildGrids(false); // check if data for each is correct const child1 = childGrids[0] as IgxHierarchicalGridComponent; const child2 = childGrids[1] as IgxHierarchicalGridComponent; expect(child1.data).toBe(fixture.componentInstance.data[0].childData); expect(child2.data).toBe(fixture.componentInstance.data[0].childData2); expect(child1.getCellByColumn(0, 'ID').value).toBe(11); expect(child1.getColumnByVisibleIndex(0).field).toBe('ID'); expect(child1.getCellByColumn(0, 'ProductName').value).toBe('Child1 Name'); expect(child2.getCellByColumn(0, 'Col1').value).toBe('Child2 Col1'); expect(child2.getCellByColumn(0, 'Col2').value).toBe('Child2 Col2'); expect(child2.getCellByColumn(0, 'Col3').value).toBe('Child2 Col3'); }); it('should apply the set options on the row island to all of its related child grids.', () => { fixture.componentInstance.height = '200px'; fixture.detectChanges(); const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const childGrids = hierarchicalGrid.gridAPI.getChildGrids(false) as IgxHierarchicalGridComponent []; expect(childGrids[0].height).toBe('200px'); expect(childGrids[1].height).toBe('200px'); }); it('Should apply runtime option changes to all related child grids (both existing and not yet initialized).', () => { const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const ri1 = fixture.componentInstance.rowIsland1; ri1.rowSelection = GridSelectionMode.multiple; fixture.detectChanges(); // check rendered grid let childGrids = hierarchicalGrid.gridAPI.getChildGrids(false); expect(childGrids[0].rowSelection).toBe( GridSelectionMode.multiple); expect(childGrids[1].rowSelection).toBe(GridSelectionMode.none); // expand new row and check newly generated grid const row2 = hierarchicalGrid.gridAPI.get_row_by_index(3) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row2.expander); fixture.detectChanges(); childGrids = hierarchicalGrid.gridAPI.getChildGrids(false); expect(childGrids[0].rowSelection).toBe( GridSelectionMode.multiple); expect(childGrids[1].rowSelection).toBe( GridSelectionMode.multiple); expect(childGrids[2].rowSelection).toBe(GridSelectionMode.none); expect(childGrids[3].rowSelection).toBe(GridSelectionMode.none); }); it('should apply column settings applied to the row island to all related child grids.', () => { const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const ri1 = fixture.componentInstance.rowIsland1 as IgxRowIslandComponent; const ri2 = fixture.componentInstance.rowIsland2 as IgxRowIslandComponent; const childGrids = hierarchicalGrid.gridAPI.getChildGrids(false); const child1Cols = childGrids[0].columns; const riCols = ri1.columns; expect(child1Cols.length).toEqual(riCols.length); for (const column of riCols) { const col = child1Cols.find((c) => c.field === column.field); expect(col).not.toBeNull(); } const child2Cols = childGrids[1].columns; const ri2Cols = ri2.columns; expect(child2Cols.length).toEqual(ri2Cols.length); for (let j = 0; j < riCols.length; j++) { const col = child2Cols.find((c) => c.field === ri2Cols[j].field); expect(col).not.toBeNull(); } }); it('should allow setting different height/width in px/percent for row islands and grids should be rendered correctly.', () => { const ri1 = fixture.componentInstance.rowIsland1; // test px ri1.height = '200px'; ri1.width = '200px'; fixture.detectChanges(); let row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); let childGrids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); let childGrid = childGrids[0].query(By.css('igx-hierarchical-grid')).componentInstance; // check sizes are applied expect(childGrid.width).toBe(ri1.width); expect(childGrid.height).toBe(ri1.height); expect(childGrid.nativeElement.style.height).toBe(ri1.height); expect(childGrid.nativeElement.style.width).toBe(ri1.width); // check virtualization state expect(childGrid.verticalScrollContainer.state.chunkSize).toBe(4); expect(childGrid.verticalScrollContainer.getScroll().scrollHeight).toBe(357); let hVirt = childGrid.gridAPI.get_row_by_index(0).virtDirRow; expect(hVirt.state.chunkSize).toBe(2); expect(hVirt.getScroll().scrollWidth).toBe(272); // collapse row UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); // test % ri1.height = '50%'; ri1.width = '50%'; fixture.detectChanges(); row = hierarchicalGrid.gridAPI.get_row_by_index(1) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); childGrids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); childGrid = childGrids[0].query(By.css('igx-hierarchical-grid')).componentInstance; // check sizes are applied expect(childGrid.width).toBe(ri1.width); expect(childGrid.height).toBe(ri1.height); expect(childGrid.nativeElement.style.height).toBe(ri1.height); expect(childGrid.nativeElement.style.width).toBe(ri1.width); // check virtualization state expect(childGrid.verticalScrollContainer.state.chunkSize).toBe(11); expect(childGrid.verticalScrollContainer.getScroll().scrollHeight).toBe(714); hVirt = childGrid.gridAPI.get_row_by_index(0).virtDirRow; expect(hVirt.getScroll().scrollWidth).toBe(272); }); it('should destroy cached instances of child grids when root grid is destroyed', async () => { const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const children = hierarchicalGrid.gridAPI.getChildGrids(true); expect(children.length).toBe(2); const child1 = children[0] as IgxHierarchicalGridComponent; const child2 = children[1] as IgxHierarchicalGridComponent; expect(child1._destroyed).toBeFalsy(); expect(child2._destroyed).toBeFalsy(); hierarchicalGrid.verticalScrollContainer.scrollTo(hierarchicalGrid.dataView.length - 1); await wait(); fixture.detectChanges(); // check that we have child is not destroyed expect(child1._destroyed).toBeFalsy(); expect(child2._destroyed).toBeFalsy(); // destroy hgrid fixture.destroy(); expect(child1._destroyed).toBeTruthy(); expect(child2._destroyed).toBeTruthy(); }); it('should emit child grid events with the related child grid instance as an event arg.', () => { hierarchicalGrid.cellSelection = 'single'; fixture.detectChanges(); const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const childGrids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); const childGrid = childGrids[0].query(By.css('igx-hierarchical-grid')).componentInstance; const cellElem = childGrid.gridAPI.get_row_by_index(0).cells.toArray()[0]; const cell = childGrid.getRowByIndex(0).cells[0] as CellType; const ri1 = fixture.componentInstance.rowIsland1; expect(cell.active).toBeFalse(); expect(cell.selected).toBeFalse(); spyOn(ri1.cellClick, 'emit').and.callThrough(); const event = new Event('click'); cellElem.nativeElement.dispatchEvent(event); const args: IGridCellEventArgs = { cell, event, owner: childGrid }; fixture.detectChanges(); expect(ri1.cellClick.emit).toHaveBeenCalledTimes(1); expect(ri1.cellClick.emit).toHaveBeenCalledWith(args); cell.selected = true; fixture.detectChanges(); expect(cell.selected).toBeTrue(); expect(childGrid.selectedCells[0].row.index).toEqual(cell.row.index); expect(childGrid.selectedCells[0].column.field).toEqual(cell.column.field); }); it('should filter correctly on row island', () => { const uniqueData = [ { ID: 1, ProductName : 'Parent Name', childData: [ { ID: 11, ProductName : 'Child11 ProductName' }, { ID: 12, ProductName : 'Child12 ProductName' } ], childData2: [ { ID: 21, Col1: 'Child21 Col1', Col2: 'Child21 Col2', Col3: 'Child21 Col3' }, { ID: 22, Col1: 'Child22 Col1', Col2: 'Child22 Col2', Col3: 'Child22 Col3' } ] } ]; fixture.componentInstance.data = uniqueData; fixture.detectChanges(); const rowIsland1 = fixture.componentInstance.rowIsland1 as IgxRowIslandComponent; rowIsland1.filter('ProductName', 'Child12', IgxStringFilteringOperand.instance().condition('contains'), true); const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const childGrids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); const childGrid1 = childGrids[0].query(By.css('igx-hierarchical-grid')).componentInstance; expect(childGrid1.data.length).toEqual(2); expect(childGrid1.filteredData.length).toEqual(1); expect(childGrid1.rowList.length).toEqual(1); expect(childGrid1.gridAPI.get_cell_by_index(0, 'ProductName').nativeElement.innerText).toEqual('Child12 ProductName'); }); it('should allow binding to complex object.', () => { const rowIsland1 = fixture.componentInstance.rowIsland1 as IgxRowIslandComponent; const rowIsland2 = fixture.componentInstance.rowIsland2 as IgxRowIslandComponent; rowIsland1.key = 'childData.Records'; rowIsland2.key = 'childData2.Records'; hierarchicalGrid.childLayoutKeys = ['childData.Records', 'childData2.Records']; const complexObjData = [ { ID: 1, ProductName : 'Parent Name', childData: { Records: [ { ID: 11, ProductName : 'Child11 ProductName' }, { ID: 12, ProductName : 'Child12 ProductName' } ] }, childData2: { Records: [ { ID: 21, Col1: 'Child21 Col1', Col2: 'Child21 Col2', Col3: 'Child21 Col3' }, { ID: 22, Col1: 'Child22 Col1', Col2: 'Child22 Col2', Col3: 'Child22 Col3' } ] } } ]; fixture.componentInstance.data = complexObjData; fixture.detectChanges(); const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const childGrids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); const childGrid1 = childGrids[0].query(By.css('igx-hierarchical-grid')).componentInstance; const childGrid2 = childGrids[1].query(By.css('igx-hierarchical-grid')).componentInstance; expect(childGrid1.data.length).toEqual(2); expect(childGrid2.data.length).toEqual(2); expect(childGrid1.data[0].ID).toBe(11); expect(childGrid2.data[0].ID).toBe(21); }); }); describe('IgxHierarchicalGrid Children Sizing #hGrid', () => { let fixture: ComponentFixture; let hierarchicalGrid: IgxHierarchicalGridComponent; const TBODY_CLASS = '.igx-grid__tbody-content'; beforeEach(() => { fixture = TestBed.createComponent(IgxHierarchicalGridSizingComponent); fixture.detectChanges(); hierarchicalGrid = fixture.componentInstance.hgrid; }); it('should create a child grid with null height when its data is unset then set to a number under 10', () => { fixture.detectChanges(); // expansion const row = hierarchicalGrid.rowList.first as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const childGrids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); const childGrid = childGrids[0].query(By.css('igx-hierarchical-grid')).componentInstance; let defaultHeight = childGrids[0].query(By.css(TBODY_CLASS)).styles.height; expect(defaultHeight).toBeFalsy(); expect(childGrid.calcHeight).toBeNull(); childGrid.data = fixture.componentInstance.data; fixture.detectChanges(); defaultHeight = childGrids[0].query(By.css(TBODY_CLASS)).styles.height; expect(defaultHeight).toBeFalsy(); expect(childGrid.calcHeight).toBeNull(); expect(childGrid.data.length).toEqual(1); expect(childGrid.rowList.length).toEqual(1); }); it('should create a child grid with auto-size when its data is unset then set to a number above 10', () => { fixture.detectChanges(); // expansion const row = hierarchicalGrid.rowList.first as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const childGrids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); const childGrid = childGrids[0].query(By.css('igx-hierarchical-grid')).componentInstance; let defaultHeight = childGrids[0].query(By.css(TBODY_CLASS)).styles.height; expect(defaultHeight).toBeFalsy(); expect(childGrid.calcHeight).toBeNull(); childGrid.data = fixture.componentInstance.fullData; fixture.detectChanges(); defaultHeight = childGrids[0].query(By.css(TBODY_CLASS)).styles.height; expect(defaultHeight).toBe('510px'); expect(childGrid.calcHeight).toBe(510); expect(childGrid.data.length).toEqual(100000); expect(childGrid.rowList.length).toEqual(11); }); it('should create a child grid with auto-size when its data is unset then set to a number above 10 and height is 50%', () => { fixture.componentInstance.childHeight = '50%'; fixture.detectChanges(); // expansion const row = hierarchicalGrid.rowList.first as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const childGrids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); const childGrid = childGrids[0].query(By.css('igx-hierarchical-grid')).componentInstance; let defaultHeight = childGrids[0].query(By.css(TBODY_CLASS)).styles.height; expect(defaultHeight).toBeFalsy(); expect(childGrid.calcHeight).toBeNull(); childGrid.data = fixture.componentInstance.fullData; fixture.detectChanges(); defaultHeight = childGrids[0].query(By.css(TBODY_CLASS)).styles.height; expect(defaultHeight).toBe('510px'); expect(childGrid.calcHeight).toBe(510); expect(childGrid.data.length).toEqual(100000); expect(childGrid.rowList.length).toEqual(11); }); it('should create a child grid fixed size when height is set to px', () => { fixture.componentInstance.childHeight = '600px'; fixture.detectChanges(); // expansion const row = hierarchicalGrid.rowList.first as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const childGrids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); const childGrid = childGrids[0].query(By.css('igx-hierarchical-grid')).componentInstance; let defaultHeight = childGrids[0].query(By.css(TBODY_CLASS)).styles.height; let defaultHeightNum = parseInt(defaultHeight, 10); expect(defaultHeightNum).toBeGreaterThan(500); expect(defaultHeightNum).toBeLessThan(600); expect(childGrid.calcHeight).toBeGreaterThan(500); expect(childGrid.calcHeight).toBeLessThan(600); expect(childGrid.rowList.length).toEqual(0); childGrid.data = fixture.componentInstance.fullData; fixture.detectChanges(); defaultHeight = childGrids[0].query(By.css(TBODY_CLASS)).styles.height; defaultHeightNum = parseInt(defaultHeight, 10); expect(defaultHeightNum).toBeGreaterThan(500); expect(defaultHeightNum).toBeLessThan(600); expect(childGrid.calcHeight).toBeGreaterThan(500); expect(childGrid.calcHeight).toBeLessThan(600); expect(childGrid.data.length).toEqual(100000); expect(childGrid.rowList.length).toEqual(12); }); it('should create a child grid null height regardless of data when height is set to null', () => { fixture.componentInstance.childHeight = null; fixture.detectChanges(); // expansion const row = hierarchicalGrid.rowList.first as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const childGrids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); const childGrid = childGrids[0].query(By.css('igx-hierarchical-grid')).componentInstance; let defaultHeight = childGrids[0].query(By.css(TBODY_CLASS)).styles.height; expect(defaultHeight).toBeFalsy(); expect(childGrid.calcHeight).toBeNull(); childGrid.data = fixture.componentInstance.semiData; fixture.detectChanges(); defaultHeight = childGrids[0].query(By.css(TBODY_CLASS)).styles.height; expect(defaultHeight).toBeFalsy(); expect(childGrid.calcHeight).toBeNull(); expect(childGrid.data.length).toEqual(15); expect(childGrid.rowList.length).toEqual(15); }); }); describe('IgxHierarchicalGrid Remote Scenarios #hGrid', () => { let fixture: ComponentFixture; const TBODY_CLASS = '.igx-grid__tbody-content'; const THEAD_CLASS = '.igx-grid-thead'; beforeEach(() => { fixture = TestBed.createComponent(IgxHGridRemoteOnDemandComponent); fixture.detectChanges(); }); // To investigate why it times out it('should render loading indicator when loading and autoGenerate are enabled', fakeAsync(() => { fixture.detectChanges(); 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')); 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 grid.shouldGenerate = true; fixture.componentInstance.databind(); fixture.detectChanges(); loadingIndicator = gridBody.query(By.css('.igx-grid__loading')); colHeaders = gridHead.queryAll(By.css('igx-grid-header')); expect(colHeaders.length).toBeGreaterThan(0); expect(loadingIndicator).toBeNull(); const row = grid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); // Tick is required for height to be recalculated and rerendered tick(); fixture.detectChanges(); const rowIslandDOM = fixture.debugElement.query(By.css('.igx-grid__hierarchical-indent .igx-grid')); const rowIslandBody = rowIslandDOM.query(By.css('.igx-grid__tbody-content')); expect(parseInt(window.getComputedStyle(rowIslandBody.nativeElement).height, 10)).toBe(255); })); it('should render disabled collapse all icon for child grid even when it has no data but with child row island', () => { const hierarchicalGrid = fixture.componentInstance.instance; fixture.componentInstance.databind(); fixture.detectChanges(); const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const gridHead = fixture.debugElement.queryAll(By.css(THEAD_CLASS))[1]; const headerExpanderElem = gridHead.queryAll(By.css('.igx-grid__hierarchical-expander--header'))[0]; const icon = headerExpanderElem.query(By.css('igx-icon')).componentInstance; const iconTxt = headerExpanderElem.query(By.css('igx-icon')).nativeElement.textContent.toLowerCase(); expect(iconTxt).toBe('unfold_less'); expect(icon.getActive).toBe(false); }); }); describe('IgxHierarchicalGrid Template Changing Scenarios #hGrid', () => { const THEAD_CLASS = '.igx-grid-thead'; let fixture: ComponentFixture; let hierarchicalGrid: IgxHierarchicalGridComponent; beforeEach(() => { fixture = TestBed.createComponent(IgxHierarchicalGridColumnsUpdateComponent); fixture.detectChanges(); hierarchicalGrid = fixture.componentInstance.hgrid; }); it('should render correct columns when setting columns for child in AfterViewInit using ngFor', () => { const gridHead = fixture.debugElement.query(By.css(THEAD_CLASS)); const colHeaders = gridHead.queryAll(By.css('igx-grid-header')); expect(colHeaders.length).toEqual(2); expect(colHeaders[0].nativeElement.innerText).toEqual('ID'); expect(colHeaders[1].nativeElement.innerText).toEqual('ProductName'); const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const child1Grids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); const child1Grid = child1Grids[0].query(By.css('igx-hierarchical-grid')); const child1Headers = child1Grid.queryAll(By.css('igx-grid-header')); expect(child1Headers.length).toEqual(5); expect(child1Headers[0].nativeElement.innerText).toEqual('ID'); expect(child1Headers[1].nativeElement.innerText).toEqual('ProductName'); expect(child1Headers[2].nativeElement.innerText).toEqual('Col1'); expect(child1Headers[3].nativeElement.innerText).toEqual('Col2'); expect(child1Headers[4].nativeElement.innerText).toEqual('Col3'); const row1 = child1Grid.componentInstance.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row1.expander); fixture.detectChanges(); const child2Grids = child1Grid.queryAll(By.css('igx-child-grid-row')); const child2Grid = child2Grids[0].query(By.css('igx-hierarchical-grid')); const child2Headers = child2Grid.queryAll(By.css('igx-grid-header')); expect(child2Headers.length).toEqual(3); expect(child2Headers[0].nativeElement.innerText).toEqual('ID'); expect(child2Headers[1].nativeElement.innerText).toEqual('ProductName'); expect(child2Headers[2].nativeElement.innerText).toEqual('Col1'); }); it('should render correct columns when setting columns for parent and child post init using ngFor', fakeAsync(() => { const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const child1Grids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); const child1Grid = child1Grids[0].query(By.css('igx-hierarchical-grid')).componentInstance; fixture.componentInstance.parentCols = ['Col1', 'Col2']; fixture.componentInstance.islandCols1 = ['ID', 'ProductName', 'Col1']; fixture.detectChanges(); tick(); // check parent cols expect(hierarchicalGrid.columns.length).toBe(4); expect(hierarchicalGrid.columns[0].field).toBe('ID'); expect(hierarchicalGrid.columns[1].field).toBe('ProductName'); expect(hierarchicalGrid.columns[2].field).toBe('Col1'); expect(hierarchicalGrid.columns[3].field).toBe('Col2'); // check child cols expect(child1Grid.columns.length).toBe(3); expect(child1Grid.columns[0].field).toBe('ID'); expect(child1Grid.columns[1].field).toBe('ProductName'); expect(child1Grid.columns[2].field).toBe('Col1'); })); it('should update columns for expanded child when adding column to row island', fakeAsync(() => { const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const child1Grids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); const child1Grid = child1Grids[0].query(By.css('igx-hierarchical-grid')); const row1 = child1Grid.componentInstance.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row1.expander); fixture.detectChanges(); const child2Grids = child1Grid.queryAll(By.css('igx-child-grid-row')); const child2Grid = child2Grids[0].query(By.css('igx-hierarchical-grid')); let child2Headers = child2Grid.queryAll(By.css('igx-grid-header')); expect(child2Headers.length).toEqual(3); expect(child2Headers[0].nativeElement.innerText).toEqual('ID'); expect(child2Headers[1].nativeElement.innerText).toEqual('ProductName'); expect(child2Headers[2].nativeElement.innerText).toEqual('Col1'); fixture.componentInstance.islandCols2.push('Col2'); fixture.detectChanges(); tick(); fixture.detectChanges(); child2Headers = child2Grid.queryAll(By.css('igx-grid-header')); expect(child2Headers.length).toEqual(4); expect(child2Headers[0].nativeElement.innerText).toEqual('ID'); expect(child2Headers[1].nativeElement.innerText).toEqual('ProductName'); expect(child2Headers[2].nativeElement.innerText).toEqual('Col1'); expect(child2Headers[3].nativeElement.innerText).toEqual('Col2'); const child1Headers = child1Grid.query(By.css(THEAD_CLASS)).queryAll(By.css('igx-grid-header')); expect(child1Headers.length).toEqual(5); expect(child1Headers[0].nativeElement.innerText).toEqual('ID'); expect(child1Headers[1].nativeElement.innerText).toEqual('ProductName'); expect(child1Headers[2].nativeElement.innerText).toEqual('Col1'); expect(child1Headers[3].nativeElement.innerText).toEqual('Col2'); expect(child1Headers[4].nativeElement.innerText).toEqual('Col3'); const gridHead = fixture.debugElement.query(By.css(THEAD_CLASS)); const colHeaders = gridHead.queryAll(By.css('igx-grid-header')); expect(colHeaders.length).toEqual(2); expect(colHeaders[0].nativeElement.innerText).toEqual('ID'); expect(colHeaders[1].nativeElement.innerText).toEqual('ProductName'); })); it('should update columns for rendered child that is collapsed when adding column to row island', fakeAsync(() => { const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const child1Grids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); const child1Grid = child1Grids[0].query(By.css('igx-hierarchical-grid')); const row1 = child1Grid.componentInstance.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row1.expander); fixture.detectChanges(); const child2Grids = child1Grid.queryAll(By.css('igx-child-grid-row')); const child2Grid = child2Grids[0].query(By.css('igx-hierarchical-grid')); let child2Headers = child2Grid.queryAll(By.css('igx-grid-header')); expect(child2Headers.length).toEqual(3); expect(child2Headers[0].nativeElement.innerText).toEqual('ID'); expect(child2Headers[1].nativeElement.innerText).toEqual('ProductName'); expect(child2Headers[2].nativeElement.innerText).toEqual('Col1'); UIInteractions.simulateClickAndSelectEvent(row1.expander); fixture.detectChanges(); fixture.componentInstance.islandCols2.push('Col2'); fixture.detectChanges(); tick(); fixture.detectChanges(); UIInteractions.simulateClickAndSelectEvent(row1.expander); fixture.detectChanges(); child2Headers = child2Grid.queryAll(By.css('igx-grid-header')); expect(child2Headers.length).toEqual(4); expect(child2Headers[0].nativeElement.innerText).toEqual('ID'); expect(child2Headers[1].nativeElement.innerText).toEqual('ProductName'); expect(child2Headers[2].nativeElement.innerText).toEqual('Col1'); expect(child2Headers[3].nativeElement.innerText).toEqual('Col2'); })); it('test getRowByIndex API methods', () => { const nonExistingRow = hierarchicalGrid.getRowByKey('nonexisting'); expect(nonExistingRow).toBeUndefined(); const nonExistingRow2 = hierarchicalGrid.getRowByIndex(-1); expect(nonExistingRow2).toBeUndefined(); const cell00 = hierarchicalGrid.getCellByColumn(0, 'ID'); expect(cell00.row.index).toBe(0); expect(cell00.column.visibleIndex).toBe(0); const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const child1Grids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); const child1Grid = child1Grids[0].query(By.css('igx-hierarchical-grid')); const firstRow = child1Grid.componentInstance.getRowByIndex(0); firstRow.expanded = true; expect(firstRow.hasChildren).toBe(true); expect(firstRow.children).toBeUndefined(); expect(firstRow.viewIndex).toEqual(0); expect(firstRow.key).toBeDefined(); expect(firstRow.data.ID).toEqual('00'); expect(firstRow.pinned).toBe(false); expect(firstRow.selected).toBe(false); expect(firstRow.expanded).toBe(true); expect(firstRow.deleted).toBe(false); expect(firstRow.inEditMode).toBe(false); // Toggle expanded state firstRow.expanded = false; expect(firstRow.expanded).toBe(false); }); }); describe('IgxHierarchicalGrid hide child columns', () => { let fixture: ComponentFixture; let hierarchicalGrid: IgxHierarchicalGridComponent; beforeEach(() => { fixture = TestBed.createComponent(IgxHierarchicalGridHidingPinningColumnsComponent); fixture.detectChanges(); hierarchicalGrid = fixture.componentInstance.hgrid; }); it('should fire hiddenChange and pinnedChange events for child grid.', () => { const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); const child1Grids = fixture.debugElement.queryAll(By.css('igx-child-grid-row')); const child1Grid = child1Grids[0].query(By.css('igx-hierarchical-grid')); // Pinning const childHeader1 = GridFunctions.getColumnHeaders(fixture)[2]; const firstHeaderIcon = childHeader1.query(By.css('.igx-icon')); spyOn(child1Grid.componentInstance.columns[0].pinnedChange, 'emit').and.callThrough(); expect(GridFunctions.isHeaderPinned(childHeader1.parent)).toBeFalsy(); expect(child1Grid.componentInstance.columns[0].pinned).toBeFalsy(); expect(firstHeaderIcon).toBeDefined(); UIInteractions.simulateClickAndSelectEvent(firstHeaderIcon); fixture.detectChanges(); expect(child1Grid.componentInstance.columns[0].pinnedChange.emit).toHaveBeenCalledTimes(1); expect(child1Grid.componentInstance.columns[0].pinned).toBeTruthy(); // Hiding const childHeader2 = GridFunctions.getColumnHeaders(fixture)[4]; const secondHeaderIcon = childHeader2.query(By.css('.igx-icon')); const lastIndex = child1Grid.componentInstance.columns.length - 1; spyOn(child1Grid.componentInstance.columns[lastIndex].hiddenChange, 'emit').and.callThrough(); expect(child1Grid.componentInstance.columns[lastIndex].hidden).toBeFalsy(); expect(secondHeaderIcon).toBeDefined(); UIInteractions.simulateClickAndSelectEvent(secondHeaderIcon); fixture.detectChanges(); expect(child1Grid.componentInstance.columns[lastIndex].hiddenChange.emit).toHaveBeenCalledTimes(1); expect(child1Grid.componentInstance.columns[lastIndex].hidden).toBeTruthy(); }); }); describe('IgxHierarchicalGrid Runtime Row Island change Scenarios #hGrid', () => { let fixture: ComponentFixture; let hierarchicalGrid: IgxHierarchicalGridComponent; beforeEach(() => { fixture = TestBed.createComponent(IgxHierarchicalGridToggleRIComponent); fixture.detectChanges(); hierarchicalGrid = fixture.componentInstance.hgrid; }); it('should allow changing row islands runtime in root grid.', () => { let row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); let hGrids = fixture.debugElement.queryAll(By.css('igx-hierarchical-grid')); let childGrids = hierarchicalGrid.gridAPI.getChildGrids(); expect(childGrids.length).toBe(1); expect(hGrids.length).toBe(2); fixture.componentInstance.toggleRI = false; fixture.detectChanges(); hGrids = fixture.debugElement.queryAll(By.css('igx-hierarchical-grid')); childGrids = hierarchicalGrid.gridAPI.getChildGrids(); expect(childGrids.length).toBe(0); expect(hGrids.length).toBe(1); expect(row.expander).toBe(undefined); fixture.componentInstance.toggleRI = true; fixture.detectChanges(); hGrids = fixture.debugElement.queryAll(By.css('igx-hierarchical-grid')); childGrids = hierarchicalGrid.gridAPI.getChildGrids(); row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; expect(childGrids.length).toBe(1); expect(hGrids.length).toBe(2); expect(row.expander).not.toBe(undefined); }); it('should allow changing row islands runtime in child grid.', () => { const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); let childGrid = hierarchicalGrid.gridAPI.getChildGrids()[0]; const childRow = childGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(childRow.expander); fixture.detectChanges(); let hGrids = fixture.debugElement.queryAll(By.css('igx-hierarchical-grid')); expect(hGrids.length).toBe(3); expect(childGrid.gridAPI.getChildGrids().length).toBe(1); fixture.componentInstance.toggleChildRI = false; fixture.detectChanges(); hGrids = fixture.debugElement.queryAll(By.css('igx-hierarchical-grid')); childGrid = hierarchicalGrid.gridAPI.getChildGrids()[0]; expect(hGrids.length).toBe(2); expect(childGrid.gridAPI.getChildGrids().length).toBe(0); fixture.componentInstance.toggleChildRI = true; fixture.detectChanges(); hGrids = fixture.debugElement.queryAll(By.css('igx-hierarchical-grid')); childGrid = hierarchicalGrid.gridAPI.getChildGrids()[0]; expect(hGrids.length).toBe(3); expect(childGrid.gridAPI.getChildGrids().length).toBe(1); }); it(`Should apply template to both parent and child grids`, () => { const customFixture = TestBed.createComponent(IgxHierarchicalGridCustomRowEditOverlayComponent); customFixture.detectChanges(); hierarchicalGrid = customFixture.componentInstance.hgrid; hierarchicalGrid.primaryKey = 'ID'; hierarchicalGrid.rowEditable = true; let cellElem = hierarchicalGrid.gridAPI.get_cell_by_index(0, 'ProductName'); let row = hierarchicalGrid.gridAPI.get_row_by_index(0); UIInteractions.simulateDoubleClickAndSelectEvent(cellElem); customFixture.detectChanges(); expect(row.inEditMode).toBe(true); const mainGridOverlay = GridFunctions.getRowEditingOverlay(customFixture); expect(mainGridOverlay).not.toBeNull(); const mainGridOverlayTextContent = mainGridOverlay.querySelector('.igx-banner__text').textContent; const mainGridOverlayActionsContent = mainGridOverlay.querySelector('.igx-banner__actions').textContent; expect(mainGridOverlayTextContent).toBe(' You have 0 changes in this row and 0 hidden columns\n'); expect(mainGridOverlayActionsContent).toBe('CancelDone'); hierarchicalGrid.expandRow(hierarchicalGrid.getRowByIndex(0).key); customFixture.detectChanges(); const secondLevelGrid = hierarchicalGrid.gridAPI.getChildGrids()[0]; expect(secondLevelGrid).not.toBeNull(); secondLevelGrid.primaryKey = 'ID'; customFixture.detectChanges(); expect(GridFunctions.getRowEditingOverlay(customFixture)).toBeDefined(); cellElem = secondLevelGrid.gridAPI.get_cell_by_index(0, 'ProductName'); row = secondLevelGrid.gridAPI.get_row_by_index(0); UIInteractions.simulateDoubleClickAndSelectEvent(cellElem); customFixture.detectChanges(); expect(row.inEditMode).toBe(true); const nestedGridOverlay = GridFunctions.getRowEditingOverlay(customFixture); expect(nestedGridOverlay).not.toBeNull(); const nestedGridOverlayTextContent = nestedGridOverlay.querySelector('.igx-banner__text').textContent; const nestedGridOverlayActionsContent = nestedGridOverlay.querySelector('.igx-banner__actions').textContent; expect(nestedGridOverlayTextContent).toBe('Row Edit Text'); expect(nestedGridOverlayActionsContent).toBe('Row Edit Actions'); }); it(`Should set ID column's width property to auto on init`, () => { const customFixture = TestBed.createComponent(IgxHierarchicalGridAutoSizeColumnsComponent); hierarchicalGrid.primaryKey = 'ID'; hierarchicalGrid = customFixture.componentInstance.hgrid; customFixture.detectChanges(); expect(hierarchicalGrid).not.toBeNull(); expect(hierarchicalGrid).not.toBeUndefined(); }); it(`Should keep the overlay when scrolling an igxHierarchicalGrid with an opened row island with <= 2 data records`, async () => { hierarchicalGrid.primaryKey = 'ID'; hierarchicalGrid.rowEditable = true; hierarchicalGrid.getRowByIndex(0).expanded = true; fixture.detectChanges(); const secondLevelGrid = hierarchicalGrid.gridAPI.getChildGrids()[0]; expect(secondLevelGrid).not.toBeNull(); secondLevelGrid.getRowByIndex(0).expanded = true; fixture.detectChanges(); const thirdLevelGrid = secondLevelGrid.gridAPI.getChildGrids()[0]; thirdLevelGrid.primaryKey = 'ID'; thirdLevelGrid.rowEditable = true; fixture.detectChanges(); expect(thirdLevelGrid).not.toBeNull(); expect(thirdLevelGrid.data.length).toBe(2); const cellElem = thirdLevelGrid.gridAPI.get_cell_by_index(0, 'ChildLevels'); const row = thirdLevelGrid.gridAPI.get_row_by_index(0); UIInteractions.simulateDoubleClickAndSelectEvent(cellElem); fixture.detectChanges(); expect(row.inEditMode).toBe(true); fixture.detectChanges(); let overlay = GridFunctions.getRowEditingOverlay(fixture); expect(overlay).not.toBeNull(); await hierarchicalGrid.dragScroll({ left: 0, top: 10 }); fixture.detectChanges(); await wait(30); overlay = GridFunctions.getRowEditingOverlay(fixture); expect(overlay).not.toBeNull(); }); }); describe('Columns and row islands runtime change', () => { let fixture: ComponentFixture; let hierarchicalGrid: IgxHierarchicalGridComponent; it('should allow changing columns runtime in root grid when there are no row islands.', fakeAsync(() => { fixture = TestBed.createComponent(IgxHierarchicalGridToggleRIAndColsComponent); fixture.detectChanges(); hierarchicalGrid = fixture.componentInstance.hgrid; expect(hierarchicalGrid.childLayoutList.length).toBe(0); expect(hierarchicalGrid.columns.length).toBe(0); fixture.componentInstance.toggleColumns = true; fixture.detectChanges(); tick(); expect(hierarchicalGrid.columns.length).toBe(2); fixture.componentInstance.toggleRI = true; fixture.detectChanges(); expect(hierarchicalGrid.childLayoutList.length).toBe(1); })); }); describe('IgxHierarchicalGrid custom template #hGrid', () => { it('should allow setting custom template for expand/collapse icons', () => { const fixture = TestBed.createComponent(IgxHierarchicalGridCustomTemplateComponent); fixture.detectChanges(); const hierarchicalGrid = fixture.componentInstance.hgrid; let rows = hierarchicalGrid.dataRowList.toArray(); for (const row of rows) { const expander = row.nativeElement.querySelector('.igx-grid__hierarchical-expander'); expect((expander as HTMLElement).innerText).toBe('COLLAPSED'); } hierarchicalGrid.expandChildren = true; fixture.detectChanges(); rows = hierarchicalGrid.dataRowList.toArray(); for (const row of rows) { const expander = row.nativeElement.querySelector('.igx-grid__hierarchical-expander'); expect((expander as HTMLElement).innerText).toBe('EXPANDED'); } const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0]; const childRows = childGrid.dataRowList.toArray(); for (const row of childRows) { const expander = row.nativeElement.querySelector('.igx-grid__hierarchical-expander'); expect((expander as HTMLElement).innerText).toBe('COLLAPSED'); } expect((hierarchicalGrid as any).headerHierarchyExpander.nativeElement.innerText).toBe('EXPANDED'); expect((childGrid as any).headerHierarchyExpander.nativeElement.innerText).toBe('COLLAPSED'); childRows[0].toggle(); fixture.detectChanges(); expect((childGrid as any).headerHierarchyExpander.nativeElement.innerText).toBe('EXPANDED'); hierarchicalGrid.expandChildren = false; fixture.detectChanges(); expect((hierarchicalGrid as any).headerHierarchyExpander.nativeElement.innerText).toBe('COLLAPSED'); }); it('should allow setting custom template for excel style filtering on row island.', () => { const fixture = TestBed.createComponent(IgxHierarchicalGridCustomFilteringTemplateComponent); fixture.detectChanges(); const hierarchicalGrid = fixture.componentInstance.hgrid; const ri = fixture.componentInstance.rowIsland; const firstRow = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(firstRow.expander); fixture.detectChanges(); const childGrid = hierarchicalGrid.gridAPI.getChildGrids()[0] as IgxHierarchicalGridComponent; expect(childGrid.excelStyleFilteringComponent).toBe(ri.excelStyleFilteringComponent); }); it('should correctly filter templated row island in hierarchical grid', fakeAsync(() => { const fixture = TestBed.createComponent(IgxHierarchicalGridCustomFilteringTemplateComponent); fixture.detectChanges(); const hierarchicalGrid = fixture.componentInstance.hgrid; const firstRow = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent; UIInteractions.simulateClickAndSelectEvent(firstRow.expander); fixture.detectChanges(); GridFunctions.clickExcelFilterIconFromCode(fixture, hierarchicalGrid, 'ProductName'); const searchComponent = GridFunctions.getExcelStyleSearchComponent(fixture, null, 'igx-hierarchical-grid'); const inputNativeElement = GridFunctions.getExcelStyleSearchComponentInput(fixture, searchComponent, 'igx-hierarchical-grid'); UIInteractions.clickAndSendInputElementValue(inputNativeElement, 'A4', fixture); GridFunctions.clickApplyExcelStyleFiltering(fixture, null, 'igx-hierarchical-grid'); fixture.detectChanges(); const gridCellValues = GridFunctions.getColumnCells(fixture, 'ProductName', 'igx-hierarchical-grid-cell') .map(c => c.nativeElement.innerText) .sort(); expect(gridCellValues.length).toBe(1); })); }); }); @Component({ template: ` `, standalone: true, imports: [IgxHierarchicalGridComponent, IgxColumnComponent, IgxRowIslandComponent] }) export class IgxHierarchicalGridTestBaseComponent { @ViewChild('hierarchicalGrid', { read: IgxHierarchicalGridComponent, static: true }) public hgrid: IgxHierarchicalGridComponent; @ViewChild('rowIsland', { read: IgxRowIslandComponent, static: true }) public rowIsland: IgxRowIslandComponent; @ViewChild('rowIsland2', { read: IgxRowIslandComponent, static: true }) public rowIsland2: IgxRowIslandComponent; public data; public width = '500px'; constructor() { // 3 level hierarchy this.data = this.generateDataUneven(20, 3); } public generateDataUneven(count: number, level: number, parentID: string = null) { const prods = []; const currLevel = level; let children; for (let i = 0; i < count; i++) { const rowID = parentID ? parentID + i : i.toString(); if (level > 0 ) { // Have child grids for row with even id less rows by not multiplying by 2 children = this.generateDataUneven((i % 2 + 1) * Math.round(count / 3) , currLevel - 1, rowID); } prods.push({ ID: rowID, ChildLevels: currLevel, ProductName: 'Product: A' + i, Col1: i, Col2: i, Col3: i, childData: children, childData2: children }); } return prods; } public clearData(){ this.data = []; } } @Component({ template: ` `, standalone: true, imports: [IgxHierarchicalGridComponent, IgxColumnComponent, IgxRowIslandComponent] }) export class IgxHierarchicalGridMultiLayoutComponent extends IgxHierarchicalGridTestBaseComponent { @ViewChild('rowIsland1', { read: IgxRowIslandComponent, static: true }) public rowIsland1: IgxRowIslandComponent; @ViewChild('rowIsland2', { read: IgxRowIslandComponent, static: true }) public override rowIsland2: IgxRowIslandComponent; public height = '100px'; } @Component({ template: ` `, standalone: true, imports: [IgxHierarchicalGridComponent, IgxColumnComponent, IgxRowIslandComponent] }) export class IgxHGridRemoteOnDemandComponent { @ViewChild(IgxHierarchicalGridComponent, { read: IgxHierarchicalGridComponent, static: true }) public instance: IgxHierarchicalGridComponent; @ViewChild('rowIsland1', { read: IgxRowIslandComponent, static: true }) public rowIsland: IgxRowIslandComponent; @ViewChild('rowIsland2', { read: IgxRowIslandComponent, static: true }) public rowIsland2: IgxRowIslandComponent; public data; constructor(public cdr: ChangeDetectorRef) { } public generateDataUneven(count: number, level: number, parendID: string = null) { const prods = []; const currLevel = level; for (let i = 0; i < count; i++) { const rowID = parendID ? parendID + i : i.toString(); prods.push({ ID: rowID, ChildLevels: currLevel, ProductName: 'Product: A' + i, Col1: i, Col2: i, Col3: i }); } return prods; } public databind() { this.data = this.generateDataUneven(20, 3); } public generateRowIslandData(count: number) { const prods = []; for (let i = 0; i < count; i++) { prods.push({ ID: i, ProductName: 'Product: A' + i }); } return prods; } public gridCreated(event: IGridCreatedEventArgs, _rowIsland: IgxRowIslandComponent) { setTimeout(() => { event.grid.data = this.generateRowIslandData(5); event.grid.cdr.detectChanges(); }); } } @Component({ template: ` `, standalone: true, imports: [IgxHierarchicalGridComponent, IgxColumnComponent, IgxRowIslandComponent, NgFor] }) export class IgxHierarchicalGridColumnsUpdateComponent extends IgxHierarchicalGridTestBaseComponent implements AfterViewInit { public cols1 = ['ID', 'ProductName', 'Col1', 'Col2', 'Col3']; public cols2 = ['ID', 'ProductName', 'Col1']; public parentCols = []; public islandCols1 = []; public islandCols2 = []; constructor(public cdr: ChangeDetectorRef) { super(); } public ngAfterViewInit() { this.islandCols1 = this.cols1; this.islandCols2 = this.cols2; this.cdr.detectChanges(); } } @Component({ template: ` `, standalone: true, imports: [IgxHierarchicalGridComponent, IgxColumnComponent, IgxRowIslandComponent] }) export class IgxHierarchicalGridSizingComponent { @ViewChild('hierarchicalGrid', { read: IgxHierarchicalGridComponent, static: true }) public hgrid: IgxHierarchicalGridComponent; @ViewChild('rowIsland', { read: IgxRowIslandComponent, static: true }) public rowIsland: IgxRowIslandComponent; public childHeight = '100%'; public data = [ { ID: 1, ProductName: 'Car' } ]; public fullData = Array.from({ length: 100000 }, (_, i) => ({ ID: i, ProductName: 'PN' + i })); public semiData = Array.from({ length: 15 }, (_, i) => ({ ID: i, ProductName: 'PN' + i })); } @Component({ template: ` `, standalone: true, imports: [IgxHierarchicalGridComponent, IgxColumnComponent, IgxRowIslandComponent, NgIf] }) export class IgxHierarchicalGridToggleRIComponent extends IgxHierarchicalGridTestBaseComponent { public toggleRI = true; public toggleChildRI = true; } @Component({ template: ` `, standalone: true, imports: [IgxHierarchicalGridComponent, IgxColumnComponent, IgxRowIslandComponent, NgIf] }) export class IgxHierarchicalGridToggleRIAndColsComponent extends IgxHierarchicalGridToggleRIComponent { public override toggleRI = false; public toggleColumns = false; } @Component({ template: ` EXPANDED COLLAPSED COLLAPSED EXPANDED `, standalone: true, imports: [IgxHierarchicalGridComponent, IgxColumnComponent, IgxRowIslandComponent, IgxRowExpandedIndicatorDirective, IgxRowCollapsedIndicatorDirective, IgxHeaderExpandedIndicatorDirective, IgxHeaderCollapsedIndicatorDirective] }) export class IgxHierarchicalGridCustomTemplateComponent extends IgxHierarchicalGridTestBaseComponent {} @Component({ template: ` filter_alt EXPANDED COLLAPSED COLLAPSED EXPANDED `, standalone: true, imports: [ IgxHierarchicalGridComponent, IgxColumnComponent, IgxRowIslandComponent, IgxIconComponent, IgxGridExcelStyleFilteringComponent, IgxExcelStyleColumnOperationsTemplateDirective, IgxExcelStyleHeaderComponent, IgxExcelStyleSortingComponent, IgxExcelStyleFilterOperationsTemplateDirective, IgxExcelStyleSearchComponent, IgxRowExpandedIndicatorDirective, IgxRowCollapsedIndicatorDirective, IgxHeaderExpandedIndicatorDirective, IgxHeaderCollapsedIndicatorDirective ] }) export class IgxHierarchicalGridCustomFilteringTemplateComponent extends IgxHierarchicalGridTestBaseComponent {} @Component({ template: `
{{column.header || column.field}} lock
{{column.header || column.field}} hide_source
`, standalone: true, imports: [IgxHierarchicalGridComponent, IgxColumnComponent, IgxRowIslandComponent, IgxIconComponent, IgxCellHeaderTemplateDirective] }) export class IgxHierarchicalGridHidingPinningColumnsComponent extends IgxHierarchicalGridTestBaseComponent { constructor(public cdr: ChangeDetectorRef) { super(); } public pinColumn(col: ColumnType) { col.pin(); } public hideColumn(col: ColumnType){ col.hidden = true; } } @Component({ template: ` Row Edit Text Row Edit Actions `, standalone: true, imports: [IgxHierarchicalGridComponent, IgxColumnComponent, IgxRowIslandComponent, IgxRowEditTextDirective, IgxRowEditActionsDirective] }) export class IgxHierarchicalGridCustomRowEditOverlayComponent extends IgxHierarchicalGridTestBaseComponent{} @Component({ template: ` Row Edit Text Row Edit Actions `, standalone: true, imports: [IgxHierarchicalGridComponent, IgxColumnComponent, IgxRowIslandComponent, IgxRowEditTextDirective, IgxRowEditActionsDirective] }) export class IgxHierarchicalGridAutoSizeColumnsComponent extends IgxHierarchicalGridTestBaseComponent {}