igniteui-angular-sovn
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
1,039 lines (838 loc) • 50.8 kB
text/typescript
import { configureTestSuite } from '../../test-utils/configure-suite';
import { TestBed, waitForAsync } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { Component, ViewChild, DebugElement} from '@angular/core';
import { IgxChildGridRowComponent, IgxHierarchicalGridComponent } from './hierarchical-grid.component';
import { wait, UIInteractions, waitForSelectionChange } from '../../test-utils/ui-interactions.spec';
import { IgxRowIslandComponent } from './row-island.component';
import { By } from '@angular/platform-browser';
import { IgxHierarchicalRowComponent } from './hierarchical-row.component';
import { clearGridSubs, setupHierarchicalGridScrollDetection } from '../../test-utils/helper-utils.spec';
import { GridFunctions } from '../../test-utils/grid-functions.spec';
import { IgxGridCellComponent } from '../cell.component';
import { IGridCellEventArgs, IgxColumnComponent } from '../public_api';
const DEBOUNCE_TIME = 30;
const GRID_CONTENT_CLASS = '.igx-grid__tbody-content';
const GRID_FOOTER_CLASS = '.igx-grid__tfoot';
describe('IgxHierarchicalGrid Navigation', () => {
let fixture;
let hierarchicalGrid: IgxHierarchicalGridComponent;
let baseHGridContent: DebugElement;
configureTestSuite();
beforeAll(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
IgxHierarchicalGridTestBaseComponent,
IgxHierarchicalGridTestComplexComponent,
IgxHierarchicalGridMultiLayoutComponent,
IgxHierarchicalGridSmallerChildComponent
]
}).compileComponents();
}));
describe('IgxHierarchicalGrid Basic Navigation #hGrid', () => {
beforeEach(waitForAsync(() => {
fixture = TestBed.createComponent(IgxHierarchicalGridTestBaseComponent);
fixture.detectChanges();
hierarchicalGrid = fixture.componentInstance.hgrid;
setupHierarchicalGridScrollDetection(fixture, hierarchicalGrid);
baseHGridContent = GridFunctions.getGridContent(fixture);
GridFunctions.focusFirstCell(fixture, hierarchicalGrid);
}));
afterEach(() => {
clearGridSubs();
});
// simple tests
it('should allow navigating down from parent row into child grid.', async () => {
hierarchicalGrid.expandChildren = false;
hierarchicalGrid.height = '600px';
hierarchicalGrid.width = '800px';
fixture.componentInstance.rowIsland.height = '350px';
fixture.detectChanges();
await wait();
// expand row
const row1 = hierarchicalGrid.dataRowList.first as IgxHierarchicalRowComponent;
UIInteractions.simulateClickAndSelectEvent(row1.expander);
fixture.detectChanges();
await wait();
// activate cell
const fCell = hierarchicalGrid.dataRowList.first.cells.first;
GridFunctions.focusCell(fixture, fCell);
// arrow down
UIInteractions.triggerEventHandlerKeyDown('arrowdown', baseHGridContent, false, false, false);
fixture.detectChanges();
// verify selection in child.
const selectedCell = fixture.componentInstance.selectedCell;
expect(selectedCell.value).toEqual(0);
expect(selectedCell.column.field).toMatch('ID');
});
it('should allow navigating up from child row into parent grid.', () => {
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const childFirstCell = childGrid.dataRowList.first.cells.first;
GridFunctions.focusCell(fixture, childFirstCell);
// arrow up in child
const childGridContent = fixture.debugElement.queryAll(By.css(GRID_CONTENT_CLASS))[1];
UIInteractions.triggerEventHandlerKeyDown('arrowup', childGridContent, false, false, false);
fixture.detectChanges();
// verify selection in parent.
const selectedCell = fixture.componentInstance.selectedCell;
expect(selectedCell.value).toEqual(0);
expect(selectedCell.column.field).toMatch('ID');
});
it('should allow navigating down in child grid when child grid selected cell moves outside the parent view port.', async () => {
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const childCell = childGrid.dataRowList.toArray()[3].cells.first;
GridFunctions.focusCell(fixture, childCell);
// arrow down in child
const childGridContent = fixture.debugElement.queryAll(By.css(GRID_CONTENT_CLASS))[1];
UIInteractions.triggerEventHandlerKeyDown('arrowdown', childGridContent, false, false, false);
fixture.detectChanges();
await wait();
// parent should scroll down so that cell in child is in view.
const selectedCell = fixture.componentInstance.selectedCell;
const selectedCellElem = childGrid.gridAPI.get_cell_by_index(selectedCell.row.index, selectedCell.column.field) as IgxGridCellComponent;
const gridOffsets = hierarchicalGrid.tbody.nativeElement.getBoundingClientRect();
const rowOffsets = selectedCellElem.intRow.nativeElement.getBoundingClientRect();
expect(rowOffsets.top >= gridOffsets.top && rowOffsets.bottom <= gridOffsets.bottom).toBeTruthy();
});
it('should allow navigating up in child grid when child grid selected cell moves outside the parent view port.', async () => {
hierarchicalGrid.verticalScrollContainer.scrollTo(2);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const childCell = childGrid.dataRowList.toArray()[4].cells.first;
GridFunctions.focusCell(fixture, childCell);
const prevScrTop = hierarchicalGrid.verticalScrollContainer.getScroll().scrollTop;
const childGridContent = fixture.debugElement.queryAll(By.css(GRID_CONTENT_CLASS))[1];
UIInteractions.triggerEventHandlerKeyDown('arrowup', childGridContent, false, false, false);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
// parent should scroll up so that cell in child is in view.
const currScrTop = hierarchicalGrid.verticalScrollContainer.getScroll().scrollTop;
expect(prevScrTop - currScrTop).toBeGreaterThanOrEqual(childGrid.rowHeight);
});
it('should allow navigating to end in child grid when child grid target row moves outside the parent view port.', async () => {
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const childCell = childGrid.dataRowList.toArray()[0].cells.toArray()[0];
GridFunctions.focusCell(fixture, childCell);
const childGridContent = fixture.debugElement.queryAll(By.css(GRID_CONTENT_CLASS))[1];
UIInteractions.triggerEventHandlerKeyDown('end', childGridContent, false, false, true);
fixture.detectChanges();
await wait();
// verify selection in child.
const selectedCell = fixture.componentInstance.selectedCell;
expect(selectedCell.row.index).toEqual(9);
expect(selectedCell.column.field).toMatch('childData2');
// parent should be scrolled down
const currScrTop = hierarchicalGrid.verticalScrollContainer.getScroll().scrollTop;
expect(currScrTop).toBeGreaterThanOrEqual(childGrid.rowHeight * 5);
});
it('should allow navigating to start in child grid when child grid target row moves outside the parent view port.', async () => {
hierarchicalGrid.verticalScrollContainer.scrollTo(2);
fixture.detectChanges();
await wait();
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const horizontalScrDir = childGrid.dataRowList.toArray()[0].virtDirRow;
horizontalScrDir.scrollTo(6);
fixture.detectChanges();
await wait();
const childLastCell = childGrid.dataRowList.toArray()[9].cells.toArray()[3];
GridFunctions.focusCell(fixture, childLastCell);
const childGridContent = fixture.debugElement.queryAll(By.css(GRID_CONTENT_CLASS))[1];
UIInteractions.triggerEventHandlerKeyDown('home', childGridContent, false, false, true);
await wait(DEBOUNCE_TIME * 3);
fixture.detectChanges();
const selectedCell = fixture.componentInstance.selectedCell;
expect(selectedCell.value).toEqual(0);
expect(selectedCell.column.index).toBe(0);
expect(selectedCell.row.index).toBe(0);
// check if child row is in view of parent.
const gridOffsets = hierarchicalGrid.tbody.nativeElement.getBoundingClientRect();
const rowElem = childGrid.gridAPI.get_row_by_index(selectedCell.row.index);
const rowOffsets = rowElem.nativeElement.getBoundingClientRect();
expect(rowOffsets.top).toBeGreaterThanOrEqual(gridOffsets.top);
expect(rowOffsets.bottom).toBeLessThanOrEqual(gridOffsets.bottom);
});
it('should allow navigating to bottom in child grid when child grid target row moves outside the parent view port.', async () => {
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const childCell = childGrid.dataRowList.first.cells.first;
GridFunctions.focusCell(fixture, childCell);
const childGridContent = fixture.debugElement.queryAll(By.css(GRID_CONTENT_CLASS))[1];
UIInteractions.triggerEventHandlerKeyDown('arrowdown', childGridContent, false, false, true);
// wait for parent grid to complete scroll to child cell.
await wait();
fixture.detectChanges();
const selectedCell = fixture.componentInstance.selectedCell;
expect(selectedCell.value).toBe(9);
expect(selectedCell.column.index).toBe(0);
expect(selectedCell.row.index).toBe(9);
const currScrTop = hierarchicalGrid.verticalScrollContainer.getScroll().scrollTop;
expect(currScrTop).toBeGreaterThanOrEqual(childGrid.rowHeight * 5);
});
it('should not lose activation when pressing Ctrl+ArrowDown is pressed at the bottom row(expended) in a child grid.', async () => {
hierarchicalGrid.height = '600px';
hierarchicalGrid.width = '800px';
fixture.componentInstance.rowIsland.height = '400px';
fixture.detectChanges();
await wait();
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
childGrid.data = childGrid.data.slice(0, 5);
fixture.detectChanges();
childGrid.dataRowList.toArray()[4].expander.nativeElement.click();
fixture.detectChanges();
await wait();
const childCell = childGrid.dataRowList.toArray()[4].cells.toArray()[0];
GridFunctions.focusCell(fixture, childCell);
const childGridContent = fixture.debugElement.queryAll(By.css(GRID_CONTENT_CLASS))[1];
UIInteractions.triggerEventHandlerKeyDown('arrowdown', childGridContent, false, false, true);
await wait();
fixture.detectChanges();
const childLastRowCell = childGrid.dataRowList.toArray()[4].cells.toArray()[0];
const selectedCell = fixture.componentInstance.selectedCell;
expect(selectedCell.row.index).toBe(childLastRowCell.row.index);
expect(selectedCell.column.visibleIndex).toBe(childLastRowCell.column.visibleIndex);
expect(selectedCell.column.index).toBe(childLastRowCell.column.index);
expect(selectedCell.value).toBe(childLastRowCell.value);
const currScrTop = hierarchicalGrid.verticalScrollContainer.getScroll().scrollTop;
expect(currScrTop).toEqual(0);
});
it('should allow navigating to top in child grid when child grid target row moves outside the parent view port.', async () => {
hierarchicalGrid.verticalScrollContainer.scrollTo(2);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const childLastRowCell = childGrid.dataRowList.toArray()[9].cells.first;
const childGridContent = fixture.debugElement.queryAll(By.css(GRID_CONTENT_CLASS))[1];
GridFunctions.focusCell(fixture, childLastRowCell);
UIInteractions.triggerEventHandlerKeyDown('arrowup', childGridContent, false, false, true);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
const childFirstRowCell = childGrid.dataRowList.first.cells.first;
const selectedCell = fixture.componentInstance.selectedCell;
expect(selectedCell.row.index).toBe(childFirstRowCell.row.index);
expect(selectedCell.column.visibleIndex).toBe(childFirstRowCell.column.visibleIndex);
expect(selectedCell.column.index).toBe(childFirstRowCell.column.index);
expect(selectedCell.value).toBe(childFirstRowCell.value);
// check if child row is in view of parent.
const gridOffsets = hierarchicalGrid.tbody.nativeElement.getBoundingClientRect();
const selectedCellElem = childGrid.gridAPI.get_cell_by_index(selectedCell.row.index, selectedCell.column.field) as IgxGridCellComponent;
const rowOffsets = selectedCellElem.intRow.nativeElement.getBoundingClientRect();
expect(rowOffsets.top).toBeGreaterThanOrEqual(gridOffsets.top);
expect(rowOffsets.bottom).toBeLessThanOrEqual(gridOffsets.bottom);
});
it('should scroll top of child grid into view when pressing Ctrl + Arrow Up when cell is selected in it.', async () => {
hierarchicalGrid.verticalScrollContainer.scrollTo(2);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const childLastRowCell = childGrid.dataRowList.toArray()[9].cells.first;
const childGridContent = fixture.debugElement.queryAll(By.css(GRID_CONTENT_CLASS))[1];
GridFunctions.focusCell(fixture, childLastRowCell);
UIInteractions.triggerEventHandlerKeyDown('arrowup', childGridContent, false, false, true);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
const childFirstRowCell = childGrid.dataRowList.first.cells.first;
const selectedCell = fixture.componentInstance.selectedCell;
expect(selectedCell.row.index).toBe(childFirstRowCell.row.index);
expect(selectedCell.column.visibleIndex).toBe(childFirstRowCell.column.visibleIndex);
expect(selectedCell.column.index).toBe(childFirstRowCell.column.index);
expect(selectedCell.value).toBe(childFirstRowCell.value);
// check if child row is in view of parent.
const gridOffsets = hierarchicalGrid.tbody.nativeElement.getBoundingClientRect();
const rowElem = childGrid.gridAPI.get_row_by_index(selectedCell.row.index);
const rowOffsets = rowElem.nativeElement.getBoundingClientRect();
expect(rowOffsets.top).toBeGreaterThanOrEqual(gridOffsets.top);
expect(rowOffsets.bottom).toBeLessThanOrEqual(gridOffsets.bottom);
});
it('when navigating down from parent into child should scroll child grid to top and start navigation from first row.', async () => {
const ri = fixture.componentInstance.rowIsland;
ri.height = '200px';
fixture.detectChanges();
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
childGrid.verticalScrollContainer.scrollTo(9);
await wait(DEBOUNCE_TIME);
fixture.detectChanges();
let currScrTop = childGrid.verticalScrollContainer.getScroll().scrollTop;
expect(currScrTop).toBeGreaterThan(0);
const fCell = hierarchicalGrid.dataRowList.toArray()[0].cells.toArray()[0];
GridFunctions.focusCell(fixture, fCell);
UIInteractions.triggerEventHandlerKeyDown('arrowdown', baseHGridContent, false, false, false);
await wait(DEBOUNCE_TIME);
fixture.detectChanges();
const childFirstCell = childGrid.dataRowList.toArray()[0].cells.toArray()[0];
const selectedCell = fixture.componentInstance.selectedCell;
expect(selectedCell.row.index).toBe(childFirstCell.row.index);
expect(selectedCell.column.index).toBe(childFirstCell.column.index);
currScrTop = childGrid.verticalScrollContainer.getScroll().scrollTop;
expect(currScrTop).toBe(0);
});
it('when navigating up from parent into child should scroll child grid to bottom and start navigation from last row.', async () => {
const ri = fixture.componentInstance.rowIsland;
ri.height = '200px';
fixture.detectChanges();
await wait();
hierarchicalGrid.verticalScrollContainer.scrollTo(2);
fixture.detectChanges();
await wait();
const parentCell = hierarchicalGrid.gridAPI.get_cell_by_key(1, 'ID');
GridFunctions.focusCell(fixture, parentCell);
UIInteractions.triggerEventHandlerKeyDown('arrowup', baseHGridContent, false, false, false);
fixture.detectChanges();
await wait();
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const vertScr = childGrid.verticalScrollContainer.getScroll();
const currScrTop = vertScr.scrollTop;
// should be scrolled to bottom
expect(currScrTop).toBe(vertScr.scrollHeight - vertScr.clientHeight);
});
it('should move activation to last data cell in grid when ctrl+end is used.', async () => {
const parentCell = hierarchicalGrid.dataRowList.first.cells.first;
GridFunctions.focusCell(fixture, parentCell);
UIInteractions.triggerEventHandlerKeyDown('end', baseHGridContent, false, false, true);
fixture.detectChanges();
await waitForSelectionChange(hierarchicalGrid);
const lastDataCell = hierarchicalGrid.getCellByKey(19, 'childData2');
const selectedCell = fixture.componentInstance.selectedCell;
expect(selectedCell.row.index).toBe(lastDataCell.row.index);
expect(selectedCell.column.index).toBe(lastDataCell.column.index);
});
it('if next child cell is not in view should scroll parent so that it is in view.', async () => {
hierarchicalGrid.verticalScrollContainer.scrollTo(4);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
const parentCell = hierarchicalGrid.dataRowList.toArray()[0].cells.toArray()[0];
GridFunctions.focusCell(fixture, parentCell);
const prevScroll = hierarchicalGrid.verticalScrollContainer.getScroll().scrollTop;
UIInteractions.triggerEventHandlerKeyDown('arrowdown', baseHGridContent, false, false, false);
fixture.detectChanges();
await wait();
// check if selected row is in view of parent.
const gridOffsets = hierarchicalGrid.tbody.nativeElement.getBoundingClientRect();
const rowElem = hierarchicalGrid.gridAPI.get_row_by_index(parentCell.row.index);
const rowOffsets = rowElem.nativeElement.getBoundingClientRect();
expect(rowOffsets.top >= gridOffsets.top && rowOffsets.bottom <= gridOffsets.bottom).toBeTruthy();
expect(hierarchicalGrid.verticalScrollContainer.getScroll().scrollTop - prevScroll).toBeGreaterThanOrEqual(100);
});
it('should expand/collapse hierarchical row using ALT+Arrow Right/ALT+Arrow Left.', async () => {
const parentRow = hierarchicalGrid.dataRowList.first as IgxHierarchicalRowComponent;
expect(parentRow.expanded).toBe(true);
const parentCell = parentRow.cells.first;
GridFunctions.focusCell(fixture, parentCell);
// collapse
UIInteractions.triggerEventHandlerKeyDown('arrowleft', baseHGridContent, true, false, false);
fixture.detectChanges();
await wait();
expect(parentRow.expanded).toBe(false);
// expand
UIInteractions.triggerEventHandlerKeyDown('arrowright', baseHGridContent, true, false, false);
await wait();
fixture.detectChanges();
expect(parentRow.expanded).toBe(true);
});
it('should retain active cell when expand/collapse hierarchical row using ALT+Arrow Right/ALT+Arrow Left.', async () => {
// scroll to last row
const lastDataIndex = hierarchicalGrid.dataView.length - 2;
hierarchicalGrid.verticalScrollContainer.scrollTo(lastDataIndex);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
hierarchicalGrid.verticalScrollContainer.scrollTo(lastDataIndex);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
let parentCell = hierarchicalGrid.gridAPI.get_cell_by_index(38, 'ID');
GridFunctions.focusCell(fixture, parentCell);
// collapse
UIInteractions.triggerEventHandlerKeyDown('arrowleft', baseHGridContent, true, false, false);
fixture.detectChanges();
await wait();
parentCell = hierarchicalGrid.gridAPI.get_cell_by_index(38, 'ID');
expect(parentCell.active).toBeTruthy();
// expand
UIInteractions.triggerEventHandlerKeyDown('arrowright', baseHGridContent, true, false, false);
fixture.detectChanges();
await wait();
parentCell = hierarchicalGrid.gridAPI.get_cell_by_index(38, 'ID');
expect(parentCell.active).toBeTruthy();
});
it('should expand/collapse hierarchical row using ALT+Arrow Down/ALT+Arrow Up.', async () => {
const parentRow = hierarchicalGrid.dataRowList.first as IgxHierarchicalRowComponent;
expect(parentRow.expanded).toBe(true);
let parentCell = parentRow.cells.first;
GridFunctions.focusCell(fixture, parentCell);
// collapse
UIInteractions.triggerEventHandlerKeyDown('arrowup', baseHGridContent, true, false, false);
fixture.detectChanges();
await wait();
expect(parentRow.expanded).toBe(false);
// expand
parentCell = parentRow.cells.first;
UIInteractions.triggerEventHandlerKeyDown('arrowdown', baseHGridContent, true, false, false);
fixture.detectChanges();
await wait();
expect(parentRow.expanded).toBe(true);
});
it('should skip child grids that have no data when navigating up/down', async () => {
// set first child to not have data
const child1 = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
child1.data = [];
fixture.detectChanges();
await wait();
const parentRow = hierarchicalGrid.dataRowList.first;
const parentCell = parentRow.cells.first;
GridFunctions.focusCell(fixture, parentCell);
UIInteractions.triggerEventHandlerKeyDown('arrowdown', baseHGridContent, false, false, false);
fixture.detectChanges();
await wait();
// second data row in parent should be focused
const parentRow2 = hierarchicalGrid.getRowByIndex(2);
const parentCell2 = parentRow2.cells[0];
let selectedCell = fixture.componentInstance.selectedCell;
expect(selectedCell.row.index).toBe(parentCell2.row.index);
expect(selectedCell.column.index).toBe(parentCell2.column.index);
UIInteractions.triggerEventHandlerKeyDown('arrowup', baseHGridContent, false, false, false);
fixture.detectChanges();
await wait();
// first data row in parent should be selected
selectedCell = fixture.componentInstance.selectedCell;
expect(selectedCell.row.index).toBe(parentCell.row.index);
expect(parentCell.selected).toBeTruthy();
});
it('should skip nested child grids that have no data when navigating up/down', async () => {
const child1 = hierarchicalGrid.gridAPI.getChildGrids(false)[0] as IgxHierarchicalGridComponent;
child1.height = '150px';
await wait();
fixture.detectChanges();
const row = child1.dataRowList.first as IgxHierarchicalRowComponent;
row.toggle();
await wait();
fixture.detectChanges();
// set nested child to not have data
const subChild = child1.gridAPI.getChildGrids(false)[0];
subChild.data = [];
subChild.cdr.detectChanges();
fixture.detectChanges();
await wait();
const fchildRowCell = row.cells.first;
GridFunctions.focusCell(fixture, fchildRowCell);
const childGridContent = fixture.debugElement.queryAll(By.css(GRID_CONTENT_CLASS))[1];
UIInteractions.triggerEventHandlerKeyDown('arrowdown', childGridContent, false, false, false);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
// second child row should be in view
const sChildRowCell = child1.getRowByIndex(2).cells[0];
let selectedCell = fixture.componentInstance.selectedCell;
expect(selectedCell.value).toBe(sChildRowCell.value);
expect(child1.verticalScrollContainer.getScroll().scrollTop).toBeGreaterThanOrEqual(150);
UIInteractions.triggerEventHandlerKeyDown('arrowup', childGridContent, false, false, false);
await wait(DEBOUNCE_TIME);
fixture.detectChanges();
selectedCell = fixture.componentInstance.selectedCell;
expect(selectedCell.row.index).toBe(0);
expect(child1.verticalScrollContainer.getScroll().scrollTop).toBe(0);
});
it('should navigate inside summary row with Ctrl + Arrow Right/ Ctrl + Arrow Left', async () => {
const col = hierarchicalGrid.getColumnByName('ID');
col.hasSummary = true;
fixture.detectChanges();
let summaryCells = hierarchicalGrid.summariesRowList.toArray()[0].summaryCells.toArray();
const firstCell = summaryCells[0];
GridFunctions.focusCell(fixture, firstCell);
const footerContent = fixture.debugElement.queryAll(By.css(GRID_FOOTER_CLASS))[2].children[0];
UIInteractions.triggerEventHandlerKeyDown('arrowright', footerContent, false, false, true);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
summaryCells = hierarchicalGrid.summariesRowList.toArray()[0].summaryCells.toArray();
const lastCell = summaryCells.find((s) => s.column.field === 'childData2');
expect(lastCell.active).toBeTruthy();
UIInteractions.triggerEventHandlerKeyDown('arrowleft', footerContent, false, false, true);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
summaryCells = hierarchicalGrid.summariesRowList.toArray()[0].summaryCells.toArray();
const fCell = summaryCells.find((s) => s.column.field === 'ID');
expect(fCell.active).toBeTruthy();
});
it('should navigate to Cancel button when there is row in edit mode', async () => {
hierarchicalGrid.columnList.forEach((c) => {
if (c.field !== hierarchicalGrid.primaryKey) {
c.editable = true;
}
});
hierarchicalGrid.rowEditable = true;
fixture.detectChanges();
await wait();
const cellElem = hierarchicalGrid.gridAPI.get_cell_by_index(0, 'ID');
GridFunctions.focusCell(fixture, cellElem);
UIInteractions.triggerEventHandlerKeyDown('end', baseHGridContent, false, false, false);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
const cell = hierarchicalGrid.gridAPI.get_cell_by_index(0, 'childData2');
UIInteractions.simulateDoubleClickAndSelectEvent(cell);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
UIInteractions.triggerKeyDownEvtUponElem('tab', cell.nativeElement, true);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
const activeEl = document.activeElement;
expect(activeEl.innerHTML).toEqual('Cancel');
UIInteractions.triggerKeyDownEvtUponElem('tab', activeEl, true, false, true);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
expect(document.activeElement.tagName.toLowerCase()).toBe('input');
});
it('should navigate to row edit button "Done" on shift + tab', async () => {
hierarchicalGrid.columnList.forEach((c) => {
if (c.field !== hierarchicalGrid.primaryKey) {
c.editable = true;
}
});
hierarchicalGrid.rowEditable = true;
fixture.detectChanges();
await wait();
hierarchicalGrid.getColumnByName('ID').hidden = true;
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
hierarchicalGrid.navigateTo(2);
await wait();
const cell = hierarchicalGrid.gridAPI.get_cell_by_index(2, 'ChildLevels');
UIInteractions.simulateDoubleClickAndSelectEvent(cell);
fixture.detectChanges();
await wait();
UIInteractions.triggerKeyDownEvtUponElem('tab', cell.nativeElement, true, false, true);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
const activeEl = document.activeElement;
expect(activeEl.innerHTML).toEqual('Done');
UIInteractions.triggerKeyDownEvtUponElem('tab', activeEl, true);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
expect(document.activeElement.tagName.toLowerCase()).toBe('input');
});
});
describe('IgxHierarchicalGrid Complex Navigation #hGrid', () => {
beforeEach(waitForAsync(() => {
fixture = TestBed.createComponent(IgxHierarchicalGridTestComplexComponent);
fixture.detectChanges();
hierarchicalGrid = fixture.componentInstance.hgrid;
setupHierarchicalGridScrollDetection(fixture, hierarchicalGrid);
baseHGridContent = GridFunctions.getGridContent(fixture);
GridFunctions.focusFirstCell(fixture, hierarchicalGrid);
}));
afterEach(() => {
clearGridSubs();
});
// complex tests
it('in case prev cell is not in view port should scroll the closest scrollable parent so that cell comes in view.', async () => {
// scroll parent so that child top is not in view
hierarchicalGrid.verticalScrollContainer.addScrollTop(300);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
const child = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const nestedChild = child.gridAPI.getChildGrids(false)[0];
const nestedChildCell = nestedChild.dataRowList.toArray()[1].cells.toArray()[0];
GridFunctions.focusCell(fixture, nestedChildCell);
let oldScrTop = hierarchicalGrid.verticalScrollContainer.getScroll().scrollTop;
fixture.detectChanges();
// navigate up
const nestedChildGridContent = fixture.debugElement.queryAll(By.css(GRID_CONTENT_CLASS))[2];
UIInteractions.triggerEventHandlerKeyDown('arrowup', nestedChildGridContent, false, false, false);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
let nextCell = nestedChild.dataRowList.toArray()[0].cells.toArray()[0];
let currScrTop = hierarchicalGrid.verticalScrollContainer.getScroll().scrollTop;
const elemHeight = nestedChildCell.intRow.nativeElement.clientHeight;
// check if parent of parent has been scroll up so that the focused cell is in view
expect(oldScrTop - currScrTop).toEqual(elemHeight);
oldScrTop = currScrTop;
expect(nextCell.selected).toBe(true);
expect(nextCell.active).toBe(true);
expect(nextCell.rowIndex).toBe(0);
// navigate up into parent
UIInteractions.triggerEventHandlerKeyDown('arrowup', nestedChildGridContent, false, false, false);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
nextCell = child.dataRowList.toArray()[0].cells.toArray()[0];
currScrTop = hierarchicalGrid.verticalScrollContainer.getScroll().scrollTop;
expect(oldScrTop - currScrTop).toBeGreaterThanOrEqual(100);
expect(nextCell.selected).toBe(true);
expect(nextCell.active).toBe(true);
expect(nextCell.rowIndex).toBe(0);
});
it('in case next cell is not in view port should scroll the closest scrollable parent so that cell comes in view.', async () => {
const child = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const nestedChild = child.gridAPI.getChildGrids(false)[0];
const nestedChildCell = nestedChild.dataRowList.toArray()[1].cells.toArray()[0];
// navigate down in nested child
GridFunctions.focusCell(fixture, nestedChildCell);
const nestedChildGridContent = fixture.debugElement.queryAll(By.css(GRID_CONTENT_CLASS))[2];
UIInteractions.triggerEventHandlerKeyDown('arrowdown', nestedChildGridContent, false, false, false);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
// check if parent has scrolled down to show focused cell.
expect(child.verticalScrollContainer.getScroll().scrollTop).toBe(nestedChildCell.intRow.nativeElement.clientHeight);
const nextCell = nestedChild.dataRowList.toArray()[2].cells.toArray()[0];
expect(nextCell.selected).toBe(true);
expect(nextCell.active).toBe(true);
expect(nextCell.rowIndex).toBe(2);
});
it('should allow navigating up from parent into nested child grid', async () => {
hierarchicalGrid.verticalScrollContainer.scrollTo(2);
await wait();
fixture.detectChanges();
const child = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const lastIndex = child.dataView.length - 1;
child.verticalScrollContainer.scrollTo(lastIndex);
await wait();
fixture.detectChanges();
child.verticalScrollContainer.scrollTo(lastIndex);
await wait();
fixture.detectChanges();
const parentCell = hierarchicalGrid.gridAPI.get_cell_by_index(2, 'ID');
GridFunctions.focusCell(fixture, parentCell);
UIInteractions.triggerEventHandlerKeyDown('arrowup', baseHGridContent , false, false, false);
await wait(DEBOUNCE_TIME);
fixture.detectChanges();
const nestedChild = child.gridAPI.getChildGrids(false)[5];
const lastCell = nestedChild.gridAPI.get_cell_by_index(4, 'ID');
expect(lastCell.selected).toBe(true);
expect(lastCell.active).toBe(true);
expect(lastCell.row.index).toBe(4);
});
});
describe('IgxHierarchicalGrid sibling row islands Navigation #hGrid', () => {
beforeEach(waitForAsync(() => {
fixture = TestBed.createComponent(IgxHierarchicalGridMultiLayoutComponent);
fixture.detectChanges();
hierarchicalGrid = fixture.componentInstance.hgrid;
setupHierarchicalGridScrollDetection(fixture, hierarchicalGrid);
baseHGridContent = GridFunctions.getGridContent(fixture);
GridFunctions.focusFirstCell(fixture, hierarchicalGrid);
}));
afterEach(() => {
clearGridSubs();
});
it('should allow navigating up between sibling child grids.', async () => {
hierarchicalGrid.verticalScrollContainer.scrollTo(2);
fixture.detectChanges();
await wait();
const child1 = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const child2 = hierarchicalGrid.gridAPI.getChildGrids(false)[5];
const child2Cell = child2.dataRowList.first.cells.first;
GridFunctions.focusCell(fixture, child2Cell);
const childGridContent = fixture.debugElement.queryAll(By.css(GRID_CONTENT_CLASS))[2];
UIInteractions.triggerEventHandlerKeyDown('arrowup', childGridContent, false, false, false);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
const lastCellPrevRI = child1.dataRowList.last.cells.first;
expect(lastCellPrevRI.active).toBe(true);
expect(lastCellPrevRI.selected).toBe(true);
expect(lastCellPrevRI.rowIndex).toBe(9);
});
it('should allow navigating down between sibling child grids.', async () => {
hierarchicalGrid.verticalScrollContainer.scrollTo(2);
await wait(DEBOUNCE_TIME);
fixture.detectChanges();
const child1 = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const child2 = hierarchicalGrid.gridAPI.getChildGrids(false)[5];
child1.verticalScrollContainer.scrollTo(child1.dataView.length - 1);
await wait(DEBOUNCE_TIME);
fixture.detectChanges();
const child2Cell = child2.dataRowList.toArray()[0].cells.toArray()[0];
const lastCellPrevRI = child1.dataRowList.last.cells.toArray()[0];
GridFunctions.focusCell(fixture, lastCellPrevRI);
const childGridContent = fixture.debugElement.queryAll(By.css(GRID_CONTENT_CLASS))[1];
UIInteractions.triggerEventHandlerKeyDown('arrowdown', childGridContent, false, false, false);
await wait(DEBOUNCE_TIME);
fixture.detectChanges();
expect(child2Cell.selected).toBe(true);
expect(child2Cell.active).toBe(true);
});
it('should navigate up from parent row to the correct child sibling.', async () => {
const parentCell = hierarchicalGrid.dataRowList.toArray()[1].cells.first;
GridFunctions.focusCell(fixture, parentCell);
// Arrow Up into prev child grid
UIInteractions.triggerEventHandlerKeyDown('arrowup', baseHGridContent, false, false, false);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
const child2 = hierarchicalGrid.gridAPI.getChildGrids(false)[5];
const child2Cell = child2.dataRowList.last.cells.first;
expect(child2Cell.selected).toBe(true);
expect(child2Cell.active).toBe(true);
expect(child2Cell.rowIndex).toBe(9);
});
it('should navigate down from parent row to the correct child sibling.', async () => {
const parentCell = hierarchicalGrid.dataRowList.first.cells.first;
GridFunctions.focusCell(fixture, parentCell);
// Arrow down into next child grid
UIInteractions.triggerEventHandlerKeyDown('arrowdown', baseHGridContent, false, false, false);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
const child1 = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const child1Cell = child1.dataRowList.toArray()[0].cells.toArray()[0];
expect(child1Cell.selected).toBe(true);
expect(child1Cell.active).toBe(true);
expect(child1Cell.rowIndex).toBe(0);
});
it('should navigate to last cell in previous child using Arrow Up from last cell of sibling with more columns', async () => {
const childGrid2 = hierarchicalGrid.gridAPI.getChildGrids(false)[5];
childGrid2.dataRowList.first.virtDirRow.scrollTo(7);
fixture.detectChanges();
await wait();
const child2LastCell = childGrid2.dataRowList.first.cells.first;
GridFunctions.focusCell(fixture, child2LastCell);
const childGridContent = fixture.debugElement.queryAll(By.css(GRID_CONTENT_CLASS))[2];
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
let childLastCell = childGrid.selectedCells;
expect(childLastCell.length).toBe(0);
UIInteractions.triggerEventHandlerKeyDown('arrowup', childGridContent, false, false, false);
fixture.detectChanges();
await wait(DEBOUNCE_TIME * 2);
childLastCell = childGrid.selectedCells;
expect(childLastCell.length).toBe(1);
expect(childLastCell[0].active).toBeTruthy();
});
});
describe('IgxHierarchicalGrid Smaller Child Navigation #hGrid', () => {
beforeEach(waitForAsync(() => {
fixture = TestBed.createComponent(IgxHierarchicalGridSmallerChildComponent);
fixture.detectChanges();
hierarchicalGrid = fixture.componentInstance.hgrid;
setupHierarchicalGridScrollDetection(fixture, hierarchicalGrid);
baseHGridContent = GridFunctions.getGridContent(fixture);
GridFunctions.focusFirstCell(fixture, hierarchicalGrid);
}));
afterEach(() => {
clearGridSubs();
});
it('should navigate to last cell in next row for child grid using Arrow Down from last cell of parent with more columns', async () => {
const parentCell = hierarchicalGrid.gridAPI.get_cell_by_index(0, 'Col2');
GridFunctions.focusCell(fixture, parentCell);
UIInteractions.triggerEventHandlerKeyDown('arrowdown', baseHGridContent, false, false, false);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
// last cell in child should be focused
const childGrid = hierarchicalGrid.gridAPI.getChildGrids(false)[0];
const childLastCell = childGrid.gridAPI.get_cell_by_index(0, 'Col1');
expect(childLastCell.selected).toBe(true);
expect(childLastCell.active).toBe(true);
});
it('should navigate to last cell in next row for child grid using Arrow Up from last cell of parent with more columns', async () => {
hierarchicalGrid.verticalScrollContainer.scrollTo(2);
fixture.detectChanges();
await wait();
const parentCell = hierarchicalGrid.gridAPI.get_cell_by_index(2, 'Col2');
GridFunctions.focusCell(fixture, parentCell);
UIInteractions.triggerEventHandlerKeyDown('arrowup', baseHGridContent, false, false, false);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
// last cell in child should be focused
const childGrids = fixture.debugElement.queryAll(By.directive(IgxChildGridRowComponent));
const childGrid = childGrids[1].query(By.directive(IgxHierarchicalGridComponent)).componentInstance;
const childLastCell = childGrid.gridAPI.get_cell_by_index(9, 'ProductName');
expect(childLastCell.selected).toBe(true);
expect(childLastCell.active).toBe(true);
});
it('should navigate to last cell in next child using Arrow Down from last cell of previous child with more columns', async () => {
const childGrids = fixture.debugElement.queryAll(By.directive(IgxChildGridRowComponent));
const firstChildGrid = childGrids[0].query(By.directive(IgxHierarchicalGridComponent)).componentInstance;
const secondChildGrid = childGrids[1].query(By.directive(IgxHierarchicalGridComponent)).componentInstance;
firstChildGrid.verticalScrollContainer.scrollTo(9);
fixture.detectChanges();
await wait();
const firstChildCell = firstChildGrid.gridAPI.get_cell_by_index(9, 'Col1');
GridFunctions.focusCell(fixture, firstChildCell);
const childGridContent = fixture.debugElement.queryAll(By.css(GRID_CONTENT_CLASS))[1];
UIInteractions.triggerEventHandlerKeyDown('arrowdown', childGridContent, false, false, false);
fixture.detectChanges();
await wait(DEBOUNCE_TIME);
const secondChildCell = secondChildGrid.gridAPI.get_cell_by_index(0, 'ProductName');
expect(secondChildCell.selected).toBe(true);
expect(secondChildCell.active).toBe(true);
});
});
});
@Component({
template: `
<igx-hierarchical-grid #grid1 [data]="data" (selected)="selected($event)"
[autoGenerate]="true" [height]="'400px'" [width]="'500px'" #hierarchicalGrid primaryKey="ID" [expandChildren]="true">
<igx-row-island (selected)="selected($event)" [key]="'childData'" [autoGenerate]="true" [height]="null" #rowIsland>
<igx-row-island (selected)="selected($event)" [key]="'childData'" [autoGenerate]="true" [height]="null" #rowIsland2 >
</igx-row-island>
</igx-row-island>
</igx-hierarchical-grid>`,
standalone: true,
imports: [IgxHierarchicalGridComponent, 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 selectedCell;
constructor() {
// 3 level hierarchy
this.data = this.generateData(20, 3);
}
public selected(event: IGridCellEventArgs) {
this.selectedCell = event.cell;
}
public generateData(count: number, level: number) {
const prods = [];
const currLevel = level;
let children;
for (let i = 0; i < count; i++) {
if (level > 0 ) {
children = this.generateData(count / 2 , currLevel - 1);
}
prods.push({
ID: i, ChildLevels: currLevel, ProductName: 'Product: A' + i, Col1: i,
Col2: i, Col3: i, childData: children, childData2: children });
}
return prods;
}
}
@Component({
template: `
<igx-hierarchical-grid #grid1 [height]="'400px'" [width]="'500px'" [data]="data" [autoGenerate]="true"
[expandChildren]='true' #hierarchicalGrid>
<igx-row-island [key]="'childData'" [autoGenerate]="true" [expandChildren]='true' [height]="'300px'" #rowIsland>
<igx-row-island [key]="'childData'" [autoGenerate]="true" #rowIsland2 [height]="'200px'" >
</igx-row-island>
</igx-row-island>
</igx-hierarchical-grid>`,
standalone: true,
imports: [IgxHierarchicalGridComponent, IgxRowIslandComponent]
})
export class IgxHierarchicalGridTestComplexComponent extends IgxHierarchicalGridTestBaseComponent {
constructor() {
super();
// 3 level hierarchy
this.data = this.generateData(20, 3);
}
}
@Component({
template: `
<igx-hierarchical-grid #grid1 [data]="data" [autoGenerate]="true" [height]="'500px'" [width]="'500px'"
[expandChildren]='true' #hierarchicalGrid>
<igx-row-island [key]="'childData'" [autoGenerate]="true" [height]="'150px'">
<igx-row-island [key]="'childData2'" [autoGenerate]="true" [height]="'100px'">
</igx-row-island>
</igx-row-island>
<igx-row-island [key]="'childData2'" [autoGenerate]="true" [height]="'150px'">
</igx-row-island>
</igx-hierarchical-grid>`,
standalone: true,
imports: [IgxHierarchicalGridComponent, IgxRowIslandComponent]
})
export class IgxHierarchicalGridMultiLayoutComponent extends IgxHierarchicalGridTestBaseComponent {}
@Component({
template: `
<igx-hierarchical-grid #grid1 [data]="data" [autoGenerate]="false" [height]="'500px'" [width]="'800px'"
[expandChildren]='true' #hierarchicalGrid>
<igx-column field="ID"></igx-column>
<igx-column field="ChildLevels"></igx-column>
<igx-column field="ProductName"></igx-column>
<igx-column field="Col1"></igx-column>
<igx-column field="Col2"></igx-column>
<igx-row-island [key]="'childData'" [autoGenerate]="false" [height]="'200px'">
<igx-column field="ID"></igx-column>
<igx-column field="ChildLevels"></igx-column>
<igx-column field="ProductName"></igx-col