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
text/typescript
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