igniteui-angular-sovn
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
1,151 lines (920 loc) • 65.6 kB
text/typescript
import { ViewChild, Component, DebugElement, OnInit, QueryList } from '@angular/core';
import { NgFor, NgIf } from '@angular/common';
import { TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { IgxGridComponent } from './grid.component';
import { IgxGridDetailTemplateDirective } from './public_api';
import { configureTestSuite } from '../../test-utils/configure-suite';
import { ColumnPinningPosition, RowPinningPosition } from '../common/enums';
import { SampleTestData } from '../../test-utils/sample-test-data.spec';
import { GridFunctions } from '../../test-utils/grid-functions.spec';
import { GridSummaryFunctions } from '../../test-utils/grid-functions.spec';
import { IgxStringFilteringOperand } from '../../data-operations/filtering-condition';
import { IgxPaginatorComponent } from '../../paginator/paginator.component';
import { wait, UIInteractions } from '../../test-utils/ui-interactions.spec';
import { clearGridSubs, setupGridScrollDetection } from '../../test-utils/helper-utils.spec';
import { GridRowConditionalStylingComponent } from '../../test-utils/grid-base-components.spec';
import { SortingDirection } from '../../data-operations/sorting-strategy';
import { IgxColumnLayoutComponent } from '../columns/column-layout.component';
import { CellType, IPinRowEventArgs, IPinningConfig, IgxColumnComponent } from '../public_api';
describe('Row Pinning #grid', () => {
const FIXED_ROW_CONTAINER = '.igx-grid__tr--pinned ';
const CELL_CSS_CLASS = '.igx-grid__td';
const DEBOUNCE_TIME = 60;
let fix;
let grid: IgxGridComponent;
configureTestSuite((() => {
return TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
GridRowConditionalStylingComponent,
GridRowPinningComponent,
GridRowPinningWithMRLComponent,
GridRowPinningWithMDVComponent,
GridRowPinningWithTransactionsComponent,
GridRowPinningWithInitialPinningComponent
]
});
}));
describe('', () => {
beforeEach(() => {
fix = TestBed.createComponent(GridRowPinningComponent);
grid = fix.componentInstance.instance;
fix.detectChanges();
});
it('should pin rows to top.', () => {
// pin 2nd data row
grid.pinRow(fix.componentInstance.data[1]);
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(1);
let pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer.length).toBe(1);
expect(pinRowContainer[0].children.length).toBe(1);
expect(pinRowContainer[0].children[0].context.key).toBe(fix.componentInstance.data[1]);
expect(pinRowContainer[0].children[0].nativeElement).toBe(grid.gridAPI.get_row_by_index(0).nativeElement);
expect(grid.gridAPI.get_row_by_index(1).key).toBe(fix.componentInstance.data[0]);
expect(grid.gridAPI.get_row_by_index(3).key).toBe(fix.componentInstance.data[2]);
// pin 3rd data row
grid.pinRow(fix.componentInstance.data[2]);
fix.detectChanges();
pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer[0].children.length).toBe(2);
expect(pinRowContainer[0].children[0].context.key).toBe(fix.componentInstance.data[1]);
expect(pinRowContainer[0].children[1].context.key).toBe(fix.componentInstance.data[2]);
expect(grid.gridAPI.get_row_by_index(2).key).toBe(fix.componentInstance.data[0]);
expect(grid.gridAPI.get_row_by_index(5).key).toBe(fix.componentInstance.data[3]);
fix.detectChanges();
// 2 records pinned + 2px border
expect(grid.pinnedRowHeight).toBe(2 * grid.renderedRowHeight + 2);
const expectedHeight = parseInt(grid.height, 10) - grid.pinnedRowHeight - 18 - grid.theadRow.nativeElement.offsetHeight;
expect(grid.calcHeight - expectedHeight).toBeLessThanOrEqual(1);
});
it('should pin rows to bottom.', () => {
fix.componentInstance.pinningConfig = { columns: ColumnPinningPosition.Start, rows: RowPinningPosition.Bottom };
fix.detectChanges();
// pin 2nd
grid.pinRow(fix.componentInstance.data[1]);
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(1);
let pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer.length).toBe(1);
expect(pinRowContainer[0].children.length).toBe(1);
expect(pinRowContainer[0].children[0].context.key).toBe(fix.componentInstance.data[1]);
expect(pinRowContainer[0].children[0].context.index - grid.pinnedRows.length).toBe(fix.componentInstance.data.length - 1);
expect(pinRowContainer[0].children[0].nativeElement)
.toBe(grid.gridAPI.get_row_by_index(fix.componentInstance.data.length).nativeElement);
expect(grid.gridAPI.get_row_by_index(0).key).toBe(fix.componentInstance.data[0]);
expect(grid.gridAPI.get_row_by_index(2).key).toBe(fix.componentInstance.data[2]);
// pin 1st
grid.pinRow(fix.componentInstance.data[0]);
fix.detectChanges();
pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer[0].children.length).toBe(2);
expect(pinRowContainer[0].children[0].context.key).toBe(fix.componentInstance.data[1]);
expect(pinRowContainer[0].children[1].context.key).toBe(fix.componentInstance.data[0]);
fix.detectChanges();
// check last pinned is fully in view
const last = pinRowContainer[0].children[1].context.nativeElement;
expect(last.getBoundingClientRect().bottom - grid.tbody.nativeElement.getBoundingClientRect().bottom).toBe(0);
// 2 records pinned + 2px border
expect(grid.pinnedRowHeight).toBe(2 * grid.renderedRowHeight + 2);
const expectedHeight = parseInt(grid.height, 10) - grid.pinnedRowHeight - 18 - grid.theadRow.nativeElement.offsetHeight;
expect(grid.calcHeight - expectedHeight).toBeLessThanOrEqual(1);
});
it('should allow pinning row at specified index via API.', () => {
grid.pinRow(fix.componentInstance.data[1]);
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(1);
expect(grid.pinnedRows[0].data).toBe(fix.componentInstance.data[1]);
// pin at index 0
grid.pinRow(fix.componentInstance.data[2], 0);
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(2);
expect(grid.pinnedRows[0].data).toBe(fix.componentInstance.data[2]);
expect(grid.pinnedRows[1].data).toBe(fix.componentInstance.data[1]);
// pin at index 1
grid.pinRow(fix.componentInstance.data[3], 1);
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(3);
expect(grid.pinnedRows[0].data).toBe(fix.componentInstance.data[2]);
expect(grid.pinnedRows[1].data).toBe(fix.componentInstance.data[3]);
expect(grid.pinnedRows[2].data).toBe(fix.componentInstance.data[1]);
});
it('should emit rowPinning on pin/unpin.', () => {
spyOn(grid.rowPinning, 'emit').and.callThrough();
let row = grid.getRowByIndex(0);
const rowID = row.key;
row.pin();
// Check pinned state with getRowByIndex after pin action
expect(row.pinned).toBe(true);
expect(grid.rowPinning.emit).toHaveBeenCalledTimes(1);
expect(grid.rowPinning.emit).toHaveBeenCalledWith({
rowID,
insertAtIndex: 0,
isPinned: true,
row,
cancel: false
});
row = grid.getRowByIndex(0);
row.unpin();
// Check pinned state with getRowByIndex after unpin action
expect(row.pinned).toBe(false);
expect(grid.rowPinning.emit).toHaveBeenCalledTimes(2);
});
it('should emit correct rowPinning arguments on pin/unpin.', () => {
spyOn(grid.rowPinning, 'emit').and.callThrough();
const row = grid.getRowByIndex(5);
const rowID = row.key;
row.pin();
expect(grid.rowPinning.emit).toHaveBeenCalledTimes(1);
expect(grid.rowPinning.emit).toHaveBeenCalledWith({
rowID,
insertAtIndex: 0,
isPinned: true,
row,
cancel: false
});
const row2 = grid.getRowByIndex(3);
const rowID2 = row2.key;
row2.pin();
expect(grid.rowPinning.emit).toHaveBeenCalledTimes(2);
expect(grid.rowPinning.emit).toHaveBeenCalledWith({
rowID: rowID2,
insertAtIndex: 1,
isPinned: true,
row: row2,
cancel: false
});
});
it('should be able to set pin possition of row on pin/unpin events.', () => {
const row1 = grid.getRowByIndex(0);
row1.pin();
expect(row1.pinned).toBe(true);
expect(grid.pinnedRecords.length).toBe(1);
expect(grid.pinnedRecords[0]).toEqual(row1.data);
const row2 = grid.getRowByIndex(2);
row2.pin();
grid.pinRow(row2.key);
expect(row2.pinned).toBe(true);
expect(grid.pinnedRecords.length).toBe(2);
expect(grid.pinnedRecords[1]).toEqual(row2.data);
grid.rowPinning.subscribe((e: IPinRowEventArgs) => {
e.insertAtIndex = 0;
});
const row5 = grid.getRowByIndex(5);
row5.pin();
expect(row2.pinned).toBe(true);
expect(grid.pinnedRecords.length).toBe(3);
expect(grid.pinnedRecords[0]).toEqual(row5.data);
});
it('should emit rowPinned on pin/unpin.', () => {
spyOn(grid.rowPinned, 'emit').and.callThrough();
const row = grid.getRowByIndex(0);
const rowID = row.key;
row.pin();
// Check pinned state with getRowByIndex after pin action
expect(row.pinned).toBe(true);
expect(grid.rowPinned.emit).toHaveBeenCalledTimes(1);
expect(grid.rowPinned.emit).toHaveBeenCalledWith({
rowID,
insertAtIndex: 0,
isPinned: true,
row,
cancel: false
});
row.unpin();
// Check pinned state with getRowByIndex after unpin action
expect(row.pinned).toBe(false);
expect(grid.rowPinned.emit).toHaveBeenCalledTimes(2);
});
it(`Should be able to cancel rowPinning on pin/unpin event.`, () => {
spyOn(grid.rowPinning, 'emit').and.callThrough();
let sub = grid.rowPinning.subscribe((e: IPinRowEventArgs) => {
e.cancel = true;
});
const row = grid.getRowByIndex(0);
const rowID = row.key;
expect(row.pinned).toBeFalsy();
row.pin();
fix.detectChanges();
expect(grid.rowPinning.emit).toHaveBeenCalledTimes(1);
expect(grid.rowPinning.emit).toHaveBeenCalledWith({
insertAtIndex: 0,
isPinned: true,
rowID,
row,
cancel: true
});
expect(row.pinned).toBeFalsy();
sub.unsubscribe();
row.pin();
fix.detectChanges();
expect(grid.rowPinning.emit).toHaveBeenCalledTimes(2);
expect(grid.rowPinning.emit).toHaveBeenCalledWith({
insertAtIndex: 0,
isPinned: true,
rowID,
row,
cancel: false
});
expect(row.pinned).toBe(true);
sub = grid.rowPinning.subscribe((e: IPinRowEventArgs) => {
e.cancel = true;
});
row.unpin();
fix.detectChanges();
expect(grid.rowPinning.emit).toHaveBeenCalledTimes(3);
expect(grid.rowPinning.emit).toHaveBeenCalledWith({
isPinned: false,
rowID,
row,
cancel: true
});
expect(row.pinned).toBe(true);
sub.unsubscribe();
});
it('should pin/unpin via grid API methods.', () => {
// pin 2nd row
grid.pinRow(fix.componentInstance.data[1]);
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(1);
let pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer.length).toBe(1);
expect(pinRowContainer[0].children.length).toBe(1);
expect(pinRowContainer[0].children[0].context.key).toBe(fix.componentInstance.data[1]);
expect(pinRowContainer[0].children[0].context.index).toBe(0);
expect(pinRowContainer[0].children[0].nativeElement).toBe(grid.gridAPI.get_row_by_index(0).nativeElement);
expect(grid.gridAPI.get_row_by_index(0).key).toBe(fix.componentInstance.data[1]);
expect(grid.gridAPI.get_row_by_index(1).key).toBe(fix.componentInstance.data[0]);
// unpin 2nd row
grid.unpinRow(fix.componentInstance.data[1]);
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(0);
pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer.length).toBe(0);
expect(grid.gridAPI.get_row_by_index(0).key).toBe(fix.componentInstance.data[0]);
expect(grid.gridAPI.get_row_by_index(1).key).toBe(fix.componentInstance.data[1]);
});
it('should pin/unpin via row API methods.', () => {
// pin 2nd row
let row = grid.gridAPI.get_row_by_index(1);
row.pin();
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(1);
let pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer.length).toBe(1);
expect(pinRowContainer[0].children.length).toBe(1);
expect(pinRowContainer[0].children[0].context.key).toBe(fix.componentInstance.data[1]);
expect(grid.gridAPI.get_row_by_index(0).key).toBe(fix.componentInstance.data[1]);
expect(grid.gridAPI.get_row_by_index(1).key).toBe(fix.componentInstance.data[0]);
// unpin
row = grid.gridAPI.get_row_by_index(0);
row.unpin();
expect(grid.pinnedRows.length).toBe(0);
pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer.length).toBe(0);
expect(grid.gridAPI.get_row_by_index(0).key).toBe(fix.componentInstance.data[0]);
expect(grid.gridAPI.get_row_by_index(1).key).toBe(fix.componentInstance.data[1]);
});
it('should pin/unpin via row pinned setter.', () => {
// pin 2nd row
let row = grid.gridAPI.get_row_by_index(1);
row.pinned = true;
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(1);
let pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer.length).toBe(1);
expect(pinRowContainer[0].children.length).toBe(1);
expect(pinRowContainer[0].children[0].context.key).toBe(fix.componentInstance.data[1]);
expect(grid.gridAPI.get_row_by_index(0).key).toBe(fix.componentInstance.data[1]);
expect(grid.gridAPI.get_row_by_index(1).key).toBe(fix.componentInstance.data[0]);
// unpin
row = grid.gridAPI.get_row_by_index(0);
row.pinned = false;
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(0);
pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer.length).toBe(0);
expect(grid.gridAPI.get_row_by_index(0).key).toBe(fix.componentInstance.data[0]);
expect(grid.gridAPI.get_row_by_index(1).key).toBe(fix.componentInstance.data[1]);
});
it('should search in both pinned and unpinned rows.', () => {
// pin 1st row
let row = grid.gridAPI.get_row_by_index(0);
row.pinned = true;
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(1);
let finds = grid.findNext('mari');
fix.detectChanges();
const fixNativeElement = fix.debugElement.nativeElement;
let spans = fixNativeElement.querySelectorAll('.igx-highlight');
expect(spans.length).toBe(2);
expect(finds).toEqual(3);
finds = grid.findNext('antonio');
fix.detectChanges();
spans = fixNativeElement.querySelectorAll('.igx-highlight');
expect(spans.length).toBe(2);
expect(finds).toEqual(2);
// pin 3rd row
row = grid.gridAPI.get_row_by_index(2);
row.pinned = true;
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(2);
finds = grid.findNext('antonio');
fix.detectChanges();
spans = fixNativeElement.querySelectorAll('.igx-highlight');
expect(spans.length).toBe(2);
expect(finds).toEqual(2);
});
it('should allow pinning onInit', () => {
expect(() => {
fix = TestBed.createComponent(GridRowPinningComponent);
grid = fix.componentInstance.instance;
fix.detectChanges();
grid.pinRow(fix.componentInstance.data[1]);
fix.detectChanges();
}).not.toThrow();
expect(grid.pinnedRows.length).toBe(1);
expect(grid.gridAPI.get_row_by_index(0).key).toBe(fix.componentInstance.data[1]);
});
it('should pin rows when columns are grouped.', () => {
grid.height = '650px';
fix.detectChanges();
// pin 1st and 2nd data row
grid.pinRow(fix.componentInstance.data[0]);
grid.pinRow(fix.componentInstance.data[1]);
fix.detectChanges();
// group by string column
grid.groupBy({
fieldName: 'ContactTitle', dir: SortingDirection.Desc, ignoreCase: false
});
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(2);
// verify rows
const groupRows = grid.groupsRowList.toArray();
const dataRows = grid.dataRowList.toArray();
expect(groupRows.length).toEqual(2);
expect(dataRows.length).toEqual(9);
expect(groupRows[0].groupRow.records[0].ID).toEqual('ALFKI');
expect(groupRows[0].groupRow.records[1].ID).toEqual('AROUT');
// pin 4th data row with ID:AROUT
grid.pinRow(fix.componentInstance.data[3]);
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(3);
// make sure the pinned rows is in the unpinned area as disabled row
expect(groupRows[0].groupRow.records[0].ID).toEqual('ALFKI');
expect(groupRows[0].groupRow.records[1].ID).toEqual('AROUT');
expect(groupRows[0].groupRow.records[2].ID).toEqual('BLAUS');
});
it('should apply filtering to both pinned and unpinned rows.', () => {
grid.gridAPI.get_row_by_index(1).pin();
fix.detectChanges();
grid.gridAPI.get_row_by_index(5).pin();
fix.detectChanges();
let pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer[0].children.length).toBe(2);
expect(pinRowContainer[0].children[0].context.key).toBe(fix.componentInstance.data[1]);
expect(pinRowContainer[0].children[1].context.key).toBe(fix.componentInstance.data[4]);
grid.filter('ID', 'B', IgxStringFilteringOperand.instance().condition('contains'), false);
fix.detectChanges();
pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer[0].children.length).toBe(1);
expect(pinRowContainer[0].children[0].context.key).toBe(fix.componentInstance.data[4]);
});
it('should calculate global summaries correctly when filtering is applied.', () => {
grid.getColumnByName('ID').hasSummary = true;
fix.detectChanges();
grid.filter('ID', 'BERGS', IgxStringFilteringOperand.instance().condition('contains'), false);
fix.detectChanges();
let summaryRow = GridSummaryFunctions.getRootSummaryRow(fix);
GridSummaryFunctions.verifyColumnSummaries(summaryRow, 0, ['Count'], ['1']);
// pin row
grid.gridAPI.get_row_by_index(0).pin();
fix.detectChanges();
summaryRow = GridSummaryFunctions.getRootSummaryRow(fix);
GridSummaryFunctions.verifyColumnSummaries(summaryRow, 0, ['Count'], ['1']);
});
it('should remove pinned container and recalculate sizes when all pinned records are filtered out.', () => {
grid.gridAPI.get_row_by_index(1).pin();
fix.detectChanges();
let pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer.length).toBe(1);
fix.detectChanges();
let expectedHeight = parseInt(grid.height, 10) - grid.pinnedRowHeight - 18 - grid.theadRow.nativeElement.offsetHeight;
expect(grid.calcHeight - expectedHeight).toBeLessThanOrEqual(1);
grid.filter('ID', 'B', IgxStringFilteringOperand.instance().condition('startsWith'), false);
fix.detectChanges();
pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer.length).toBe(0);
fix.detectChanges();
expect(grid.pinnedRowHeight).toBe(0);
expectedHeight = parseInt(grid.height, 10) - grid.pinnedRowHeight - 18 - grid.theadRow.nativeElement.offsetHeight;
expect(grid.calcHeight - expectedHeight).toBeLessThanOrEqual(1);
});
it('should return correct filterData collection.', () => {
grid.gridAPI.get_row_by_index(1).pin();
fix.detectChanges();
grid.gridAPI.get_row_by_index(6).pin();
fix.detectChanges();
grid.filter('ID', 'B', IgxStringFilteringOperand.instance().condition('contains'), false);
fix.detectChanges();
let gridFilterData = grid.filteredData;
expect(gridFilterData.length).toBe(8);
expect(gridFilterData[0].ID).toBe('BLAUS');
expect(gridFilterData[1].ID).toBe('BERGS');
fix.componentInstance.pinningConfig = { columns: ColumnPinningPosition.Start, rows: RowPinningPosition.Bottom };
fix.detectChanges();
gridFilterData = grid.filteredData;
expect(gridFilterData.length).toBe(8);
expect(gridFilterData[0].ID).toBe('BLAUS');
expect(gridFilterData[1].ID).toBe('BERGS');
});
it('should apply sorting to both pinned and unpinned rows.', () => {
grid.gridAPI.get_row_by_index(1).pin();
grid.gridAPI.get_row_by_index(6).pin();
expect(grid.gridAPI.get_row_by_index(0).key).toBe(fix.componentInstance.data[1]);
expect(grid.gridAPI.get_row_by_index(1).key).toBe(fix.componentInstance.data[5]);
grid.sort({ fieldName: 'ID', dir: SortingDirection.Desc, ignoreCase: false });
fix.detectChanges();
// check pinned rows data is sorted
expect(grid.gridAPI.get_row_by_index(0).key).toBe(fix.componentInstance.data[5]);
expect(grid.gridAPI.get_row_by_index(1).key).toBe(fix.componentInstance.data[1]);
// check unpinned rows data is sorted
const lastIndex = fix.componentInstance.data.length - 1;
expect(grid.gridAPI.get_row_by_index(2).key).toBe(fix.componentInstance.data[lastIndex]);
});
});
describe('Row pinning with Master Detail View', () => {
beforeEach(() => {
fix = TestBed.createComponent(GridRowPinningWithMDVComponent);
grid = fix.componentInstance.instance;
fix.detectChanges();
});
it('should be in view when expanded and pinning row to bottom of the grid.', async () => {
fix.componentInstance.pinningConfig = { columns: ColumnPinningPosition.Start, rows: RowPinningPosition.Bottom };
fix.detectChanges();
// pin 1st row
const row = grid.gridAPI.get_row_by_index(0);
row.pinned = true;
fix.detectChanges();
await wait(DEBOUNCE_TIME);
fix.detectChanges();
GridFunctions.toggleMasterRow(fix, grid.pinnedRows[0]);
fix.detectChanges();
await wait(DEBOUNCE_TIME);
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(1);
const firstRowIconName = GridFunctions.getRowExpandIconName(grid.rowList.first);
const pinnedRow = grid.pinnedRows[0];
expect(grid.expansionStates.size).toEqual(1);
expect(grid.expansionStates.has(pinnedRow.key)).toBeTruthy();
expect(grid.expansionStates.get(pinnedRow.key)).toBeTruthy();
// disabled row should have expand icon
expect(firstRowIconName).toEqual('expand_more');
// disabled row should have chip
const cell = (grid.gridAPI.get_row_by_index(0).cells as QueryList<CellType>).toArray()[0];
expect(cell.nativeElement.getElementsByClassName('igx-grid__td--pinned-chip').length).toBe(1);
// pinned row shouldn't have expand icon
const hasIconForPinnedRow = pinnedRow.cells.first.nativeElement.querySelector('igx-icon');
expect(hasIconForPinnedRow).toBeNull();
// check last pinned row is fully in view
expect(pinnedRow.nativeElement.getBoundingClientRect().bottom - grid.tbody.nativeElement.getBoundingClientRect().bottom)
.toBe(0);
});
it('should calculate global summaries with both pinned and unpinned collections', () => {
// enable summaries for each column
grid.columns.forEach(c => {
c.hasSummary = true;
});
fix.detectChanges();
grid.groupBy({
fieldName: 'ContactTitle', dir: SortingDirection.Asc, ignoreCase: false
});
fix.detectChanges();
let row = grid.gridAPI.get_row_by_index(1);
row.pinned = true;
fix.detectChanges();
let summaryRow = GridSummaryFunctions.getRootSummaryRow(fix);
GridSummaryFunctions.verifyColumnSummaries(summaryRow, 0, ['Count'], ['27']);
row = grid.pinnedRows[0];
row.pinned = false;
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(0);
summaryRow = GridSummaryFunctions.getRootSummaryRow(fix);
GridSummaryFunctions.verifyColumnSummaries(summaryRow, 0, ['Count'], ['27']);
});
it('should calculate groupby row summaries only within unpinned collection', () => {
// enable summaries for each column
grid.columns.forEach(c => {
c.hasSummary = true;
});
fix.detectChanges();
grid.groupBy({
fieldName: 'ContactTitle', dir: SortingDirection.Asc, ignoreCase: false
});
fix.detectChanges();
let row = grid.gridAPI.get_row_by_index(1);
row.pinned = true;
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(1);
// get first summary row and make sure that the pinned record is not contained within the calculations
let summaryRow = GridSummaryFunctions.getSummaryRowByDataRowIndex(fix, 4);
GridSummaryFunctions.verifyColumnSummaries(summaryRow, 0, ['Count'], ['2']);
// unpin the row and check if the summary is recalculated
row = grid.pinnedRows[0];
row.pinned = false;
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(0);
summaryRow = GridSummaryFunctions.getSummaryRowByDataRowIndex(fix, 3);
GridSummaryFunctions.verifyColumnSummaries(summaryRow, 0, ['Count'], ['2']);
});
});
describe('Paging', () => {
let paginator: IgxPaginatorComponent;
beforeEach(() => {
fix = TestBed.createComponent(GridRowPinningComponent);
fix.componentInstance.createSimpleData(12);
grid = fix.componentInstance.instance;
fix.componentInstance.paging = true;
fix.detectChanges();
paginator = fix.debugElement.query(By.directive(IgxPaginatorComponent)).componentInstance;
paginator.perPage = 5;
fix.detectChanges();
});
it('should correctly apply paging state for grid and paginator when there are pinned rows.', () => {
// pin the first row
grid.gridAPI.get_row_by_index(0).pin();
expect(grid.rowList.length).toEqual(6);
expect(grid.perPage).toEqual(5);
expect(paginator.perPage).toEqual(5);
expect(paginator.totalRecords).toEqual(12);
expect(paginator.totalPages).toEqual(3);
// pin the second row
grid.gridAPI.get_row_by_index(2).pin();
expect(grid.rowList.length).toEqual(7);
expect(grid.perPage).toEqual(5);
expect(paginator.perPage).toEqual(5);
expect(paginator.totalRecords).toEqual(12);
expect(paginator.totalPages).toEqual(3);
});
it('should have the correct records shown for pages with pinned rows', () => {
grid.gridAPI.get_row_by_index(0).pin();
let rows = grid.rowList.toArray();
[1, 1, 2, 3, 4, 5].forEach((x, index) => expect(rows[index].cells.first.value).toEqual(x));
grid.paginator.paginate(2);
fix.detectChanges();
rows = grid.rowList.toArray();
[1, 11, 12].forEach((x, index) => expect(rows[index].cells.first.value).toEqual(x));
});
});
describe(' Editing ', () => {
beforeEach(() => {
fix = TestBed.createComponent(GridRowPinningWithTransactionsComponent);
grid = fix.componentInstance.instance;
fix.detectChanges();
});
it('should allow pinning edited row.', () => {
grid.updateCell('New value', 'ANTON', 'CompanyName');
fix.detectChanges();
grid.pinRow('ANTON');
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(1);
const pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer.length).toBe(1);
expect(pinRowContainer[0].children.length).toBe(1);
expect(pinRowContainer[0].children[0].context.key).toBe('ANTON');
expect(pinRowContainer[0].children[0].context.data.CompanyName).toBe('New value');
});
it('should allow pinning deleted row.', () => {
grid.deleteRow('ALFKI');
fix.detectChanges();
grid.pinRow('ALFKI');
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(1);
const pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer.length).toBe(1);
expect(pinRowContainer[0].children.length).toBe(1);
expect(pinRowContainer[0].children[0].context.key).toBe('ALFKI');
});
it('should allow pinning added row.', () => {
grid.addRow({ ID: 'Test', CompanyName: 'Test' });
fix.detectChanges();
grid.pinRow('Test');
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(1);
const pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer.length).toBe(1);
expect(pinRowContainer[0].children.length).toBe(1);
expect(pinRowContainer[0].children[0].context.key).toBe('Test');
});
it('should stop editing when edited row is pinned/unpinned.', () => {
grid.getColumnByName('CompanyName').editable = true;
fix.detectChanges();
let cell = grid.getCellByColumn(0, 'CompanyName');
let cellDomNumber = fix.debugElement.queryAll(By.css(CELL_CSS_CLASS))[1];
cellDomNumber.triggerEventHandler('dblclick', {});
fix.detectChanges();
expect(cell.editMode).toBeTruthy();
grid.pinRow(cell.row.key);
fix.detectChanges();
cell = grid.getCellByColumn(0, 'CompanyName');
expect(cell.editMode).toBeFalsy();
cellDomNumber = fix.debugElement.queryAll(By.css(CELL_CSS_CLASS))[1];
cellDomNumber.triggerEventHandler('dblclick', {});
fix.detectChanges();
expect(cell.editMode).toBeTruthy();
grid.unpinRow(cell.row.key);
fix.detectChanges();
cell = grid.getCellByColumn(0, 'CompanyName');
expect(cell.editMode).toBeFalsy();
});
});
describe('Row pinning with MRL', () => {
beforeEach(() => {
fix = TestBed.createComponent(GridRowPinningWithMRLComponent);
grid = fix.componentInstance.instance;
fix.detectChanges();
});
it('should pin/unpin correctly to top', () => {
// pin
grid.pinRow(fix.componentInstance.data[1]);
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(1);
const pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer.length).toBe(1);
expect(pinRowContainer[0].children.length).toBe(1);
expect(pinRowContainer[0].children[0].context.key).toBe(fix.componentInstance.data[1]);
expect(pinRowContainer[0].children[0].nativeElement).toBe(grid.gridAPI.get_row_by_index(0).nativeElement);
expect(grid.gridAPI.get_row_by_index(0).pinned).toBeTruthy();
const gridPinnedRow = grid.pinnedRows[0];
// headers are aligned to cells
GridFunctions.verifyLayoutHeadersAreAligned(grid, gridPinnedRow, true);
GridFunctions.verifyDOMMatchesLayoutSettings(grid, gridPinnedRow, fix.componentInstance.colGroups);
// unpin
const row = grid.pinnedRows[0];
row.pinned = false;
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(0);
expect(row.pinned).toBeFalsy();
const gridUnpinnedRow = grid.gridAPI.get_row_by_index(1);
GridFunctions.verifyLayoutHeadersAreAligned(grid, gridUnpinnedRow);
GridFunctions.verifyDOMMatchesLayoutSettings(grid, gridUnpinnedRow, fix.componentInstance.colGroups);
});
it('should pin/unpin correctly to bottom', () => {
fix.componentInstance.pinningConfig = { columns: ColumnPinningPosition.Start, rows: RowPinningPosition.Bottom };
fix.detectChanges();
// pin
grid.pinRow(fix.componentInstance.data[1]);
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(1);
const pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer.length).toBe(1);
expect(pinRowContainer[0].children.length).toBe(1);
expect(pinRowContainer[0].children[0].context.key).toBe(fix.componentInstance.data[1]);
expect(pinRowContainer[0].children[0].nativeElement)
.toBe(grid.gridAPI.get_row_by_index(fix.componentInstance.data.length).nativeElement);
expect(grid.gridAPI.get_row_by_index(fix.componentInstance.data.length).pinned).toBeTruthy();
const gridPinnedRow = grid.pinnedRows[0];
// headers are aligned to cells
GridFunctions.verifyLayoutHeadersAreAligned(grid, gridPinnedRow, true);
GridFunctions.verifyDOMMatchesLayoutSettings(grid, gridPinnedRow, fix.componentInstance.colGroups);
// unpin
const row = grid.pinnedRows[0];
row.pinned = false;
fix.detectChanges();
expect(grid.pinnedRows.length).toBe(0);
expect(row.pinned).toBeFalsy();
const gridUnpinnedRow = grid.gridAPI.get_row_by_index(1);
GridFunctions.verifyLayoutHeadersAreAligned(grid, gridUnpinnedRow);
GridFunctions.verifyDOMMatchesLayoutSettings(grid, gridUnpinnedRow, fix.componentInstance.colGroups);
});
it('should test getRowByIndex API members.', () => {
fix.componentInstance.pinningConfig = { columns: ColumnPinningPosition.Start, rows: RowPinningPosition.Bottom };
fix.detectChanges();
// pin 1st
grid.pinRow(fix.componentInstance.data[0]);
fix.detectChanges();
const firstRow = grid.getRowByIndex(0);
// Check if the row is pinned to the bottom through the Row pinned API
expect(firstRow.pinned).toBe(true);
// Toggle pin state with row API
firstRow.pinned = false;
expect(firstRow.pinned).toBe(false);
fix.detectChanges();
firstRow.pinned = true;
expect(firstRow.pinned).toBe(true);
fix.detectChanges();
// Check dom existence
expect(grid.pinnedRows.length).toBe(1);
let pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer[0].children[0].nativeElement)
.toBe(grid.gridAPI.get_row_by_index(fix.componentInstance.data.length).nativeElement);
// Pin/Unpin with the methods
firstRow.unpin();
expect(firstRow.pinned).toBe(false);
firstRow.pin();
expect(firstRow.pinned).toBe(true);
// Check again pinned row presence
pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer[0].children[0].nativeElement)
.toBe(grid.gridAPI.get_row_by_index(fix.componentInstance.data.length).nativeElement);
// Check select
firstRow.selected = true;
fix.detectChanges();
expect(firstRow.selected).toBe(true);
// Check pinned row existence after the selection
expect(pinRowContainer[0].children[0].nativeElement.offsetParent).toBeDefined();
expect(pinRowContainer[0].children[0].nativeElement.offsetWidth).toBeGreaterThan(0);
firstRow.selected = false;
fix.detectChanges();
expect(firstRow.selected).toBe(false);
// Delete row
firstRow.delete();
fix.detectChanges();
expect(grid.gridAPI.get_row_by_index(0).data.ID).toEqual('ANATR');
// TO DO Check pinned row existence after the row deletion
expect(pinRowContainer[0].children[0].nativeElement.offsetParent).toBeNull();
expect(pinRowContainer[0].children[0].nativeElement.offsetWidth).toEqual(0);
// Check API methods
expect(firstRow.key).toBeTruthy();
expect(firstRow.data).toBeTruthy();
expect(firstRow.pinned).toBe(true);
});
});
describe(' Hiding', () => {
beforeEach(() => {
fix = TestBed.createComponent(GridRowPinningComponent);
grid = fix.componentInstance.instance;
fix.detectChanges();
});
it('should hide columns in pinned and unpinned area', () => {
// pin 2nd data row
grid.pinRow(fix.componentInstance.data[1]);
const hiddenCol = grid.columns[1];
hiddenCol.hidden = true;
fix.detectChanges();
const pinnedCells = grid.pinnedRows[0].cells;
expect(pinnedCells.filter(cell => cell.column.field === hiddenCol.field).length).toBe(0);
const unpinnedCells = grid.rowList.first.cells;
expect(unpinnedCells.filter(cell => cell.column.field === hiddenCol.field).length).toBe(0);
expect(pinnedCells.length).toBe(unpinnedCells.length);
const headerCells = grid.headerCellList;
expect(headerCells.filter(cell => cell.column.field === hiddenCol.field).length).toBe(0);
expect(grid.pinnedRows.length).toBe(1);
const pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
expect(pinRowContainer.length).toBe(1);
expect(pinRowContainer[0].children.length).toBe(1);
expect(pinRowContainer[0].children[0].context.key).toBe(fix.componentInstance.data[1]);
expect(pinRowContainer[0].children[0].nativeElement).toBe(grid.gridAPI.get_row_by_index(0).nativeElement);
expect(grid.gridAPI.get_row_by_index(0).key).toBe(fix.componentInstance.data[1]);
expect(grid.gridAPI.get_row_by_index(1).key).toBe(fix.componentInstance.data[0]);
expect(grid.gridAPI.get_row_by_index(2).key).toBe(fix.componentInstance.data[1]);
expect(grid.gridAPI.get_row_by_index(3).key).toBe(fix.componentInstance.data[2]);
fix.detectChanges();
// 1 records pinned + 2px border
expect(grid.pinnedRowHeight).toBe(grid.renderedRowHeight + 2);
const expectedHeight = parseInt(grid.height, 10) - grid.pinnedRowHeight - 18 - grid.theadRow.nativeElement.offsetHeight;
expect(grid.calcHeight - expectedHeight).toBeLessThanOrEqual(1);
});
it('should keep the scrollbar sizes correct when partially filtering out pinned records', () => {
grid.gridAPI.get_row_by_index(1).pin();
grid.gridAPI.get_row_by_index(3).pin();
grid.gridAPI.get_row_by_index(5).pin();
grid.gridAPI.get_row_by_index(7).pin();
fix.detectChanges();
// 4 records pinned + 2px border
expect(grid.pinnedRowHeight).toBe(4 * grid.renderedRowHeight + 2);
let expectedHeight = parseInt(grid.height, 10) - grid.pinnedRowHeight - 18 - grid.theadRow.nativeElement.offsetHeight;
expect(grid.calcHeight - expectedHeight).toBeLessThanOrEqual(1);
grid.filter('ContactTitle', 'Owner', IgxStringFilteringOperand.instance().condition('contains'), false);
fix.detectChanges();
// 2 records pinned + 2px border
expect(grid.pinnedRowHeight).toBe(2 * grid.renderedRowHeight + 2);
expectedHeight = parseInt(grid.height, 10) - grid.pinnedRowHeight - 18 - grid.theadRow.nativeElement.offsetHeight;
expect(grid.calcHeight - expectedHeight).toBeLessThanOrEqual(1);
});
});
describe(' Cell Editing', () => {
beforeEach(() => {
fix = TestBed.createComponent(GridRowPinningComponent);
fix.detectChanges();
// enable cell editing for column
grid = fix.componentInstance.instance;
grid.getColumnByName('CompanyName').editable = true;
});
it('should enter edit mode for the next editable cell when tabbing.', () => {
const gridContent = GridFunctions.getGridContent(fix);
grid.gridAPI.get_row_by_index(0).pin();
grid.gridAPI.get_row_by_index(3).pin();
const firstEditable = grid.gridAPI.get_cell_by_index(0, 'CompanyName');
const secondEditable = grid.gridAPI.get_cell_by_index(1, 'CompanyName');
const thirdEditable = grid.gridAPI.get_cell_by_index(3, 'CompanyName');
const fourthEditable = grid.gridAPI.get_cell_by_index(5, 'CompanyName');
// enter edit mode for pinned row
UIInteractions.simulateDoubleClickAndSelectEvent(firstEditable);
fix.detectChanges();
expect(firstEditable.editMode).toBeTruthy();
// press tab on edited cell
UIInteractions.triggerEventHandlerKeyDown('tab', gridContent);
fix.detectChanges();
expect(firstEditable.editMode).toBeFalsy();
expect(secondEditable.editMode).toBeTruthy();
// press tab on edited cell
UIInteractions.triggerEventHandlerKeyDown('tab', gridContent);
fix.detectChanges();
expect(secondEditable.editMode).toBeFalsy();
expect(thirdEditable.editMode).toBeTruthy();
UIInteractions.triggerEventHandlerKeyDown('tab', gridContent);
fix.detectChanges();
expect(thirdEditable.editMode).toBeFalsy();
expect(fourthEditable.editMode).toBeTruthy();
});
it('should enter edit mode for the previous editable cell when shift+tabbing.', () => {
const gridContent = GridFunctions.getGridContent(fix);
grid.gridAPI.get_row_by_index(0).pin();
grid.gridAPI.get_row_by_index(3).pin();
const firstEditable = grid.gridAPI.get_cell_by_index(0, 'CompanyName');
const secondEditable = grid.gridAPI.get_cell_by_index(1, 'CompanyName');
const thirdEditable = grid.gridAPI.get_cell_by_index(3, 'CompanyName');
const fourthEditable = grid.gridAPI.get_cell_by_index(5, 'CompanyName');
// enter edit mode for unpinned row
UIInteractions.simulateDoubleClickAndSelectEvent(fourthEditable);
fix.detectChanges();
expect(fourthEditable.editMode).toBeTruthy();
// press shift+tab on edited cell
UIInteractions.triggerEventHandlerKeyDown('tab', gridContent, false, true);
fix.detectChanges();
expect(fourthEditable.editMode).toBeFalsy();
expect(thirdEditable.editMode).toBeTruthy();
// press shift+tab on edited cell
UIInteractions.triggerEventHandlerKeyDown('tab', gridContent, false, true);
fix.detectChanges();
expect(thirdEditable.editMode).toBeFalsy();
expect(secondEditable.editMode).toBeTruthy();
// press shift+tab on edited cell
UIInteractions.triggerEventHandlerKeyDown('tab', gridContent, false, true);
fix.detectChanges();
expect(secondEditable.editMode).toBeFalsy();
expect(firstEditable.editMode).toBeTruthy();
});
});
describe(' Navigation', () => {
let gridContent: DebugElement;
beforeEach(() => {
fix = TestBed.createComponent(GridRowPinningComponent);
fix.detectChanges();
grid = fix.componentInstance.instance;
setupGridScrollDetection(fix, grid);
gridContent = GridFunctions.getGridContent(fix);
});
afterEach(() => {
clearGridSubs();
});
it('should navigate to bottom from top pinned row using Ctrl+ArrowDown', async () => {
grid.gridAPI.get_row_by_index(5).pin();
const firstRowCell = (grid.gridAPI.get_row_by_index(0).cells as QueryList<CellType>).toArray()[1];
UIInteractions.simulateClickAndSelectEvent(firstRowCell);
await wait(DEBOUNCE_TIME);
fix.detectChanges();
UIInteractions.triggerEventHandlerKeyDown('ArrowDown', gridContent, false, false, true);
await wait(DEBOUNCE_TIME);
fix.detectChanges();
await wait(DEBOUNCE_TIME);
fix.detectChanges();
const lastRowCell = grid.getRowByIndex(27).cells[1];
const selectedCell = fix.componentInstance.instance.selectedCells[0];
// expect(selectedCell).toBe(lastRowCell);
expect(selectedCell.row.index).toBe(lastRowCell.row.index);
expect(selectedCell.column.visibleIndex).toBe(lastRowCell.column.visibleIndex);
});
it('should navigate and scroll to first unpinned row from top pinned row using ArrowDown', async () => {
grid.gridAPI.get_row_by_index(5).pin();
grid.navigateTo(10);
await wait(DEBOUNCE_TIME);
fix.detectChanges();
const firstRowCell = (grid.gridAPI.get_row_by_index(0).cells as QueryList<CellType>).toArray()[1];
UIInteractions.simulateClickAndSelectEvent(firstRowCell);
await wait(DEBOUNCE_TIME);
fix.detectChanges();
UIInteractions.triggerEventHandlerKeyDown('Arro