UNPKG

igniteui-angular-sovn

Version:

Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps

962 lines (820 loc) 105 kB
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<any, boolean>(); 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<CellType>).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<any, boolean>(); 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<any, boolean>(); 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<any, boolean>(); 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<any, boolean>(); 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<CellType>).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<CellType>).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(childGri