From 8c9a5728dcd3ad769b33af3de127ae44ddee2fdc Mon Sep 17 00:00:00 2001 From: skrustev Date: Thu, 16 Apr 2020 12:07:29 +0300 Subject: [PATCH 1/5] feat(igxGrid): Update navigation to include ghost rows. #6640 --- .../src/lib/grids/grid-base.directive.ts | 60 ++++++++++++++----- .../src/lib/grids/grid/grid.component.ts | 6 +- .../src/lib/grids/grid/grid.pipes.ts | 1 + .../src/lib/grids/grid/row-pinning.spec.ts | 6 +- .../hierarchical-grid.component.html | 2 +- .../hierarchical-grid.component.ts | 2 +- 6 files changed, 53 insertions(+), 24 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index 69172ff81b4..60e73464aad 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -2441,6 +2441,11 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements */ public columnWidthSetByUser = false; + /** + * @hidden @internal + */ + public pinnedRecords: any[]; + /** * @hidden @internal */ @@ -2657,10 +2662,11 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements /** * @hidden + * Returns the row index of a row that takes into account the full view data like pinning. */ - public getRowIndex(rowIndex, pinned) { + public getDataViewIndex(rowIndex, pinned) { if (pinned && !this.isRowPinningToTop) { - rowIndex = rowIndex + this.dataView.length; + rowIndex = rowIndex + this.unpinnedRecords.length; } else if (!pinned && this.isRowPinningToTop) { rowIndex = rowIndex + this.pinnedRecordsCount; } @@ -2719,6 +2725,15 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements return this._pinnedRecordIDs.indexOf(id) !== -1; } + /** + * @hidden + * @internal + */ + public isRecordPinnedByIndex(rowIndex: number) { + return this.hasPinnedRecords && (this.isRowPinningToTop && rowIndex < this.pinnedRecordsCount) || + (!this.isRowPinningToTop && rowIndex >= this.unpinnedRecords.length); + } + /** * @hidden * @internal @@ -5154,25 +5169,38 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements } /** - * Returns the currently transformed paged/filtered/sorted/grouped data, displayed in the grid. + * Returns the currently transformed paged/filtered/sorted/grouped pinned row data, displayed in the grid. * @example * ```typescript - * const dataView = this.grid.dataView; + * const pinnedDataView = this.grid.pinnedDataView; * ``` */ - get dataView(): any[] { - return this.unpinnedRecords ? this.unpinnedRecords : this.verticalScrollContainer.igxForOf; + get pinnedDataView(): any[] { + return this.pinnedRecords ? this.pinnedRecords : []; } /** - * Returns the currently transformed paged/filtered/sorted/grouped pinned data, displayed in the grid. + * Returns currently transformed paged/filtered/sorted/grouped unpinned row data, displayed in the grid. * @example * ```typescript * const pinnedDataView = this.grid.pinnedDataView; * ``` */ - get pinnedDataView(): any[] { - return this.pinnedRows.map(row => row.rowData); + get unpinnedDataView(): any[] { + return this.unpinnedRecords ? this.unpinnedRecords : this.verticalScrollContainer.igxForOf; + } + + /** + * Returns the currently transformed paged/filtered/sorted/grouped row data, displayed in the grid. + * @example + * ```typescript + * const dataView = this.grid.dataView; + * ``` + */ + get dataView(): any[] { + return this.isRowPinningToTop ? + [...this.pinnedDataView, ...this.unpinnedDataView] : + [...this.unpinnedDataView, ...this.pinnedDataView]; } /** @@ -5415,7 +5443,7 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements * If `headers` is enabled, it will use the column header (if any) instead of the column field. */ public getSelectedData(formatters = false, headers = false) { - const source = this.isRowPinningToTop ? [...this.pinnedDataView, ...this.dataView] : [...this.dataView, ...this.pinnedDataView]; + const source = this.dataView; return this.extractDataFromSelection(source, formatters, headers); } @@ -5619,10 +5647,14 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements if (this.dataView.slice(rowIndex, rowIndex + 1).find(rec => rec.expression || rec.childGridsData)) { visibleColIndex = -1; } - const shouldScrollVertically = this.navigation.shouldPerformVerticalScroll(rowIndex, visibleColIndex); + // If the target row is pinned no need to scroll as well. + const shouldScrollVertically = + !this.isRecordPinnedByIndex(rowIndex) && this.navigation.shouldPerformVerticalScroll(rowIndex, visibleColIndex); const shouldScrollHorizontally = this.navigation.shouldPerformHorizontalScroll(visibleColIndex, rowIndex); if (shouldScrollVertically) { - this.navigation.performVerticalScrollToCell(rowIndex, visibleColIndex, + // Only for top pinning we need to subtract pinned count because virtualization indexing doesn't count pinned rows. + const scrollRowIndex = this.isRowPinningToTop ? rowIndex - this.pinnedRecordsCount : rowIndex; + this.navigation.performVerticalScrollToCell(scrollRowIndex, visibleColIndex, () => { this.navigateTo(rowIndex, visibleColIndex, cb); }); } else if (shouldScrollHorizontally) { this.navigation.performHorizontalScrollToCell(visibleColIndex, () => { this.navigateTo(rowIndex, visibleColIndex, cb); }); @@ -5905,11 +5937,11 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements if (delayScrolling) { this.verticalScrollContainer.onDataChanged.pipe(first()).subscribe(() => { this.scrollDirective(this.verticalScrollContainer, - typeof (row) === 'number' ? row : this.dataView.indexOf(row)); + typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row)); }); } else { this.scrollDirective(this.verticalScrollContainer, - typeof (row) === 'number' ? row : this.dataView.indexOf(row)); + typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row)); } this.scrollToHorizontally(column); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.component.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.component.ts index 6384314c6b8..69e8ede2450 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.component.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.component.ts @@ -769,10 +769,6 @@ export class IgxGridComponent extends IgxGridBaseDirective implements GridType, * @hidden @internal */ public getContext(rowData: any, rowIndex: number, pinned?: boolean): any { - if (pinned && !this.isRowPinningToTop) { - rowIndex = rowIndex + this.dataView.length; - } - rowIndex = !pinned && this.isRowPinningToTop ? rowIndex + this._pinnedRecordIDs.length : rowIndex; if (this.isDetailRecord(rowData)) { const cachedData = this.childDetailTemplates.get(rowData.detailsData); const rowID = this.primaryKey ? rowData.detailsData[this.primaryKey] : this.data.indexOf(rowData.detailsData); @@ -797,7 +793,7 @@ export class IgxGridComponent extends IgxGridBaseDirective implements GridType, } return { $implicit: this.isGhostRecord(rowData) ? rowData.recordRef : rowData, - index: rowIndex, + index: this.getDataViewIndex(rowIndex, pinned), templateID: this.isGroupByRecord(rowData) ? 'groupRow' : this.isSummaryRow(rowData) ? 'summaryRow' : 'dataRow', disabled: this.isGhostRecord(rowData) }; diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts index 00353668c1f..9a32e63cbb8 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts @@ -173,6 +173,7 @@ export class IgxGridRowPinningPipe implements PipeTransform { if (grid.hasPinnedRecords && isPinned) { const result = collection.filter(rec => grid.isRecordPinned(rec)); result.sort((rec1, rec2) => grid.pinRecordIndex(rec1) - grid.pinRecordIndex(rec2)); + grid.pinnedRecords = result; return result; } diff --git a/projects/igniteui-angular/src/lib/grids/grid/row-pinning.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/row-pinning.spec.ts index e365fa7bd8c..2d02cbf3385 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/row-pinning.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/row-pinning.spec.ts @@ -416,7 +416,7 @@ describe('Row Pinning #grid', () => { expect(grid.pinnedRows.length).toBe(1); let pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER)); expect(pinRowContainer.length).toBe(1); - expect(grid.dataView.length).toBe(5); + expect(grid.dataView.length).toBe(6); expect(paginator.componentInstance.totalPages).toEqual(6); grid.getRowByIndex(3).pin(); @@ -425,7 +425,7 @@ describe('Row Pinning #grid', () => { expect(grid.pinnedRows.length).toBe(2); pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER)); expect(pinRowContainer.length).toBe(1); - expect(grid.dataView.length).toBe(5); + expect(grid.dataView.length).toBe(7); expect(paginator.componentInstance.totalPages).toEqual(6); // unpin @@ -439,7 +439,7 @@ describe('Row Pinning #grid', () => { pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER)); expect(pinRowContainer.length).toBe(0); - expect(grid.dataView.length).toBe(5); + expect(grid.dataView.length).toBe(6); expect(paginator.componentInstance.totalPages).toEqual(6); }); diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.html b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.html index 39501087509..6c359d230b5 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.html +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.html @@ -140,7 +140,7 @@ 'igx-grid__tr-container': true, 'igx-grid__tr--highlighted':isRowHighlighted(rowData) }"> - diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts index a6ec522dd26..b2ed56e066e 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts @@ -547,7 +547,7 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti return { $implicit: this.isGhostRecord(rowData) ? rowData.recordRef : rowData, templateID: 'dataRow', - index: this.getRowIndex(rowIndex, pinned), + index: this.getDataViewIndex(rowIndex, pinned), disabled: this.isGhostRecord(rowData) }; } From 58669a8ee08493ef5d1056a126be40c4a968b5ba Mon Sep 17 00:00:00 2001 From: skrustev Date: Wed, 22 Apr 2020 14:40:20 +0300 Subject: [PATCH 2/5] fix(igxGrid): Fixes for row pinning and navigation. Adding automation. --- .../src/lib/grids/grid-base.directive.ts | 16 +- .../src/lib/grids/grid-navigation.service.ts | 10 +- .../src/lib/grids/grid/grid.pipes.ts | 10 +- .../src/lib/grids/grid/row-pinning.spec.ts | 237 +++++++++++++++++- .../hierarchical-grid-navigation.service.ts | 6 +- .../hierarchical-grid.component.html | 2 +- 6 files changed, 259 insertions(+), 22 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index 60e73464aad..236bec2a660 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -2666,9 +2666,9 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements */ public getDataViewIndex(rowIndex, pinned) { if (pinned && !this.isRowPinningToTop) { - rowIndex = rowIndex + this.unpinnedRecords.length; + rowIndex = rowIndex + this.unpinnedDataView.length; } else if (!pinned && this.isRowPinningToTop) { - rowIndex = rowIndex + this.pinnedRecordsCount; + rowIndex = rowIndex + this.pinnedDataView.length; } return rowIndex; } @@ -2730,8 +2730,8 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements * @internal */ public isRecordPinnedByIndex(rowIndex: number) { - return this.hasPinnedRecords && (this.isRowPinningToTop && rowIndex < this.pinnedRecordsCount) || - (!this.isRowPinningToTop && rowIndex >= this.unpinnedRecords.length); + return this.hasPinnedRecords && (this.isRowPinningToTop && rowIndex < this.pinnedDataView.length) || + (!this.isRowPinningToTop && rowIndex >= this.unpinnedDataView.length); } /** @@ -3004,6 +3004,7 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements public setFilteredSortedData(data, pinned: boolean) { if (this._pinnedRecordIDs.length > 0 && pinned) { this._filteredSortedPinnedData = data; + this.pinnedRecords = data; this.filteredSortedData = this.isRowPinningToTop ? [... this._filteredSortedPinnedData, ... this._filteredSortedUnpinnedData] : [... this._filteredSortedUnpinnedData, ... this._filteredSortedPinnedData]; } else if (this._pinnedRecordIDs.length > 0 && !pinned) { @@ -5648,13 +5649,10 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements visibleColIndex = -1; } // If the target row is pinned no need to scroll as well. - const shouldScrollVertically = - !this.isRecordPinnedByIndex(rowIndex) && this.navigation.shouldPerformVerticalScroll(rowIndex, visibleColIndex); + const shouldScrollVertically = this.navigation.shouldPerformVerticalScroll(rowIndex, visibleColIndex); const shouldScrollHorizontally = this.navigation.shouldPerformHorizontalScroll(visibleColIndex, rowIndex); if (shouldScrollVertically) { - // Only for top pinning we need to subtract pinned count because virtualization indexing doesn't count pinned rows. - const scrollRowIndex = this.isRowPinningToTop ? rowIndex - this.pinnedRecordsCount : rowIndex; - this.navigation.performVerticalScrollToCell(scrollRowIndex, visibleColIndex, + this.navigation.performVerticalScrollToCell(rowIndex, visibleColIndex, () => { this.navigateTo(rowIndex, visibleColIndex, cb); }); } else if (shouldScrollHorizontally) { this.navigation.performHorizontalScrollToCell(visibleColIndex, () => { this.navigateTo(rowIndex, visibleColIndex, cb); }); diff --git a/projects/igniteui-angular/src/lib/grids/grid-navigation.service.ts b/projects/igniteui-angular/src/lib/grids/grid-navigation.service.ts index add01e722fe..6f2303b2436 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-navigation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-navigation.service.ts @@ -314,8 +314,11 @@ export class IgxGridNavigationService { } public shouldPerformVerticalScroll(targetRowIndex: number, visibleColIndex: number): boolean { + if (this.grid.isRecordPinnedByIndex(targetRowIndex)) { return false; } + const scrollRowIndex = this.grid.hasPinnedRecords && this.grid.isRowPinningToTop ? + targetRowIndex - this.grid.pinnedDataView.length : targetRowIndex; const targetRow = this.getRowElementByIndex(targetRowIndex); - const rowHeight = this.grid.verticalScrollContainer.getSizeAt(targetRowIndex); + const rowHeight = this.grid.verticalScrollContainer.getSizeAt(scrollRowIndex); const containerHeight = this.grid.calcHeight ? Math.ceil(this.grid.calcHeight) : 0; const endTopOffset = targetRow ? targetRow.offsetTop + rowHeight + this.containerTopOffset : containerHeight + rowHeight; return !targetRow || targetRow.offsetTop < Math.abs(this.containerTopOffset) @@ -328,7 +331,10 @@ export class IgxGridNavigationService { } public performVerticalScrollToCell(rowIndex: number, visibleColIndex = -1, cb?: () => void) { - this.grid.verticalScrollContainer.scrollTo(rowIndex); + // Only for top pinning we need to subtract pinned count because virtualization indexing doesn't count pinned rows. + const scrollRowIndex = this.grid.hasPinnedRecords && this.grid.isRowPinningToTop ? + rowIndex - this.grid.pinnedDataView.length : rowIndex; + this.grid.verticalScrollContainer.scrollTo(scrollRowIndex); this.grid.verticalScrollContainer.onChunkLoad .pipe(first()).subscribe(() => { if (cb) { cb(); } diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts index 9a32e63cbb8..a4fd914dbc5 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts @@ -166,18 +166,18 @@ export class IgxGridRowPinningPipe implements PipeTransform { public transform(collection: any[] , id: string, isPinned = false, pipeTrigger: number) { const grid = this.gridAPI.grid; - if (!grid.hasPinnedRecords) { - return isPinned ? [] : collection; - } - if (grid.hasPinnedRecords && isPinned) { const result = collection.filter(rec => grid.isRecordPinned(rec)); result.sort((rec1, rec2) => grid.pinRecordIndex(rec1) - grid.pinRecordIndex(rec2)); - grid.pinnedRecords = result; return result; } grid.unpinnedRecords = collection; + if (!grid.hasPinnedRecords) { + grid.pinnedRecords = []; + return isPinned ? [] : collection; + } + return collection.map((rec) => { return grid.isRecordPinned(rec) ? { recordRef: rec, ghostRecord: true} : rec; }); diff --git a/projects/igniteui-angular/src/lib/grids/grid/row-pinning.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/row-pinning.spec.ts index 2d02cbf3385..8fa9d8d453a 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/row-pinning.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/row-pinning.spec.ts @@ -8,7 +8,11 @@ import { configureTestSuite } from '../../test-utils/configure-suite'; import { ColumnPinningPosition, RowPinningPosition } from '../common/enums'; import { IPinningConfig } from '../common/grid.interface'; import { SampleTestData } from '../../test-utils/sample-test-data.spec'; -import { verifyLayoutHeadersAreAligned, verifyDOMMatchesLayoutSettings } from '../../test-utils/helper-utils.spec'; +import { + verifyLayoutHeadersAreAligned, + verifyDOMMatchesLayoutSettings, + setupGridScrollDetection +} from '../../test-utils/helper-utils.spec'; import { GridFunctions } from '../../test-utils/grid-functions.spec'; import { SortingDirection } from '../../data-operations/sorting-expression.interface'; import { IgxGridTransaction } from '../tree-grid'; @@ -16,7 +20,7 @@ import { IgxTransactionService } from '../../services'; import { GridSummaryFunctions } from '../../test-utils/grid-functions.spec'; import { IgxStringFilteringOperand } from '../../data-operations/filtering-condition'; import { IgxPaginatorComponent } from '../../paginator/paginator.component'; -import { wait } from '../../test-utils/ui-interactions.spec'; +import { wait, UIInteractions } from '../../test-utils/ui-interactions.spec'; describe('Row Pinning #grid', () => { const FIXED_ROW_CONTAINER = '.igx-grid__tr--pinned '; @@ -439,7 +443,7 @@ describe('Row Pinning #grid', () => { pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER)); expect(pinRowContainer.length).toBe(0); - expect(grid.dataView.length).toBe(6); + expect(grid.dataView.length).toBe(5); expect(paginator.componentInstance.totalPages).toEqual(6); }); @@ -774,6 +778,233 @@ describe('Row Pinning #grid', () => { expect(grid.calcHeight - expectedHeight).toBeLessThanOrEqual(1); }); }); + + describe(' Navigation', () => { + let gridContent: DebugElement; + + beforeEach(() => { + fix = TestBed.createComponent(GridRowPinningComponent); + fix.detectChanges(); + grid = fix.componentInstance.instance; + setupGridScrollDetection(fix, grid); + gridContent = GridFunctions.getGridContent(fix); + }); + + it('should navigate to bottom from top pinned row using Ctrl+ArrowDown', async() => { + grid.getRowByIndex(5).pin(); + fix.detectChanges(); + + const firstRowCell = grid.getRowByIndex(0).cells.toArray()[1]; + UIInteractions.simulateClickAndSelectCellEvent(firstRowCell); + 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.toArray()[1]; + const selectedCell = fix.componentInstance.instance.selectedCells[0]; + expect(selectedCell).toBe(lastRowCell); + expect(selectedCell.rowIndex).toBe(27); + }); + + it('should navigate and scroll to first unpinned row from top pinned row using ArrowDown', async() => { + grid.getRowByIndex(5).pin(); + fix.detectChanges(); + + grid.navigateTo(10); + await wait(DEBOUNCE_TIME); + fix.detectChanges(); + + const firstRowCell = grid.getRowByIndex(0).cells.toArray()[1]; + UIInteractions.simulateClickAndSelectCellEvent(firstRowCell); + fix.detectChanges(); + + UIInteractions.triggerEventHandlerKeyDown('ArrowDown', gridContent); + await wait(DEBOUNCE_TIME); + fix.detectChanges(); + await wait(DEBOUNCE_TIME); + fix.detectChanges(); + + const secondRowCell = grid.getRowByIndex(1).cells.toArray()[1]; + const selectedCell = fix.componentInstance.instance.selectedCells[0]; + expect(selectedCell).toBe(secondRowCell); + expect(selectedCell.rowIndex).toBe(1); + }); + + it('should navigate to top pinned row from bottom unpinned row without scrolling using Ctrl+ArrowUp', async() => { + grid.getRowByIndex(5).pin(); + fix.detectChanges(); + + grid.navigateTo(27); + await wait(DEBOUNCE_TIME); + fix.detectChanges(); + + expect(grid.verticalScrollContainer.getScroll().scrollTop).not.toEqual(0); + + const lastRowCell = grid.getRowByIndex(27).cells.toArray()[1]; + UIInteractions.simulateClickAndSelectCellEvent(lastRowCell); + fix.detectChanges(); + + UIInteractions.triggerEventHandlerKeyDown('ArrowUp', gridContent, false, false, true); + await wait(DEBOUNCE_TIME); + fix.detectChanges(); + await wait(DEBOUNCE_TIME); + fix.detectChanges(); + + const firstRowCell = grid.getRowByIndex(0).cells.toArray()[1]; + const selectedCell = fix.componentInstance.instance.selectedCells[0]; + expect(selectedCell).toBe(firstRowCell); + expect(selectedCell.rowIndex).toBe(0); + expect(grid.verticalScrollContainer.getScroll().scrollTop).not.toEqual(0); + }); + + it('should navigate to top pinned row from first unpinned row using ArrowUp', async() => { + grid.getRowByIndex(5).pin(); + grid.getRowByIndex(1).pin(); + fix.detectChanges(); + + const thirdRowCell = grid.getRowByIndex(2).cells.toArray()[1]; + UIInteractions.simulateClickAndSelectCellEvent(thirdRowCell); + fix.detectChanges(); + + expect(grid.navigation.activeNode.row).toBe(2); + expect(grid.navigation.activeNode.column).toBe(1); + + UIInteractions.triggerEventHandlerKeyDown('ArrowUp', gridContent); + await wait(DEBOUNCE_TIME); + fix.detectChanges(); + + const secondRowCell = grid.getRowByIndex(1).cells.toArray()[1]; + const selectedCell = fix.componentInstance.instance.selectedCells[0]; + expect(selectedCell).toBe(secondRowCell); + expect(selectedCell.rowIndex).toBe(1); + }); + + it('should navigate and scroll to top from bottom pinned row using Ctrl+ArrowUp', async() => { + fix.componentInstance.pinningConfig = { columns: ColumnPinningPosition.Start, rows: RowPinningPosition.Bottom }; + grid.getRowByIndex(5).pin(); + fix.detectChanges(); + + grid.navigateTo(26); + await wait(DEBOUNCE_TIME); + fix.detectChanges(); + + const lastRowCell = grid.getRowByIndex(27).cells.toArray()[1]; + UIInteractions.simulateClickAndSelectCellEvent(lastRowCell); + fix.detectChanges(); + + expect(grid.navigation.activeNode.row).toBe(27); + expect(grid.navigation.activeNode.column).toBe(1); + + UIInteractions.triggerEventHandlerKeyDown('ArrowUp', gridContent, false, false, true); + await wait(DEBOUNCE_TIME); + fix.detectChanges(); + await wait(DEBOUNCE_TIME); + fix.detectChanges(); + + const firstRowCell = grid.getRowByIndex(0).cells.toArray()[1]; + const selectedCell = fix.componentInstance.instance.selectedCells[0]; + expect(selectedCell).toBe(firstRowCell); + expect(selectedCell.rowIndex).toBe(0); + }); + + it('should navigate to last unpinned row from bottom pinned row using ArrowUp', async() => { + fix.componentInstance.pinningConfig = { columns: ColumnPinningPosition.Start, rows: RowPinningPosition.Bottom }; + grid.getRowByIndex(5).pin(); + fix.detectChanges(); + + const firstRowCell = grid.getRowByIndex(27).cells.toArray()[1]; + UIInteractions.simulateClickAndSelectCellEvent(firstRowCell); + fix.detectChanges(); + + UIInteractions.triggerEventHandlerKeyDown('ArrowUp', gridContent); + await wait(DEBOUNCE_TIME); + fix.detectChanges(); + await wait(DEBOUNCE_TIME); + fix.detectChanges(); + + const lastUnpinnedRowCell = grid.getRowByIndex(26).cells.toArray()[1]; + const selectedCell = fix.componentInstance.instance.selectedCells[0]; + expect(selectedCell).toBe(lastUnpinnedRowCell); + expect(selectedCell.rowIndex).toBe(26); + }); + + it('should navigate to bottom pinned row from top unpinned row without scrolling using Ctrl+ArrowDown', async() => { + fix.componentInstance.pinningConfig = { columns: ColumnPinningPosition.Start, rows: RowPinningPosition.Bottom }; + grid.getRowByIndex(5).pin(); + fix.detectChanges(); + + expect(grid.verticalScrollContainer.getScroll().scrollTop).toEqual(0); + + const firstRowCell = grid.getRowByIndex(0).cells.toArray()[1]; + UIInteractions.simulateClickAndSelectCellEvent(firstRowCell); + 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.toArray()[1]; + const selectedCell = fix.componentInstance.instance.selectedCells[0]; + expect(selectedCell).toBe(lastRowCell); + expect(selectedCell.rowIndex).toBe(27); + expect(grid.verticalScrollContainer.getScroll().scrollTop).toEqual(0); + }); + + it('should navigate to bottom pinned row from last unpinned row using ArrowDown', async() => { + fix.componentInstance.pinningConfig = { columns: ColumnPinningPosition.Start, rows: RowPinningPosition.Bottom }; + grid.getRowByIndex(5).pin(); + grid.getRowByIndex(1).pin(); + fix.detectChanges(); + + grid.navigateTo(26); + await wait(DEBOUNCE_TIME); + fix.detectChanges(); + + const firstRowCell = grid.getRowByIndex(26).cells.toArray()[1]; + UIInteractions.simulateClickAndSelectCellEvent(firstRowCell); + fix.detectChanges(); + + expect(grid.navigation.activeNode.row).toBe(26); + expect(grid.navigation.activeNode.column).toBe(1); + + UIInteractions.triggerEventHandlerKeyDown('ArrowDown', gridContent); + await wait(DEBOUNCE_TIME); + fix.detectChanges(); + + const lastRowCell = grid.getRowByIndex(27).cells.toArray()[1]; + const selectedCell = fix.componentInstance.instance.selectedCells[0]; + expect(selectedCell).toBe(lastRowCell); + expect(selectedCell.rowIndex).toBe(27); + }); + + it('should navigate down from pinned to unpinned row when there are filtered out pinned rows', async() => { + grid.getRowByIndex(5).pin(); + grid.getRowByIndex(1).pin(); + fix.detectChanges(); + + grid.filter('ID', 'B', IgxStringFilteringOperand.instance().condition('contains'), false); + fix.detectChanges(); + + const firstRowCell = grid.getRowByIndex(0).cells.toArray()[1]; + UIInteractions.simulateClickAndSelectCellEvent(firstRowCell); + fix.detectChanges(); + + UIInteractions.triggerEventHandlerKeyDown('ArrowDown', gridContent); + await wait(DEBOUNCE_TIME); + fix.detectChanges(); + + const lastRowCell = grid.getRowByIndex(1).cells.toArray()[1]; + const selectedCell = fix.componentInstance.instance.selectedCells[0]; + expect(selectedCell).toBe(lastRowCell); + expect(selectedCell.rowIndex).toBe(1); + }); + }); }); @Component({ diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-navigation.service.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-navigation.service.ts index d32955220af..cf23d3272c0 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-navigation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-navigation.service.ts @@ -318,7 +318,8 @@ export class IgxHierarchicalGridNavigationService extends IgxGridNavigationServi let top = currGrid.tbody.nativeElement.getBoundingClientRect().top; while (currGrid.parent) { currGrid = currGrid.parent; - top = Math.max(top, currGrid.tbody.nativeElement.getBoundingClientRect().top); + const pinnedRowsHeight = currGrid.hasPinnedRecords && currGrid.isRowPinningToTop ? currGrid.pinnedRowHeight : 0; + top = Math.max(top, currGrid.tbody.nativeElement.getBoundingClientRect().top + pinnedRowsHeight); } return top; } @@ -332,7 +333,8 @@ export class IgxHierarchicalGridNavigationService extends IgxGridNavigationServi let bottom = currGrid.tbody.nativeElement.getBoundingClientRect().bottom; while (currGrid.parent) { currGrid = currGrid.parent; - bottom = Math.min(bottom, currGrid.tbody.nativeElement.getBoundingClientRect().bottom); + const pinnedRowsHeight = currGrid.hasPinnedRecords && !currGrid.isRowPinningToTop ? currGrid.pinnedRowHeight : 0; + bottom = Math.min(bottom, currGrid.tbody.nativeElement.getBoundingClientRect().bottom - pinnedRowsHeight); } return bottom; } diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.html b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.html index 6c359d230b5..f7fdc932245 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.html +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.html @@ -99,7 +99,7 @@ | visibleColumns:hasVisibleColumns | gridRowPinning:id:true:pipeTrigger | gridFiltering:filteringExpressionsTree:filterStrategy:advancedFilteringExpressionsTree:id:pipeTrigger:filteringPipeTrigger:true - | gridSort:sortingExpressions:sortStrategy:id:pipeTrigger as pinnedData"> + | gridSort:sortingExpressions:sortStrategy:id:pipeTrigger:true as pinnedData">
From 46fcd557a9336e8d3324558ce229c34075e9ce1f Mon Sep 17 00:00:00 2001 From: skrustev Date: Wed, 22 Apr 2020 16:17:26 +0300 Subject: [PATCH 3/5] fix(igxTreeGrid): Additional fixes for tree grid row pining and update test. --- .../igniteui-angular/src/lib/grids/grid-base.directive.ts | 4 ++-- .../src/lib/grids/tree-grid/tree-grid-integration.spec.ts | 4 ++-- .../src/lib/grids/tree-grid/tree-grid.component.html | 4 ++-- .../src/lib/grids/tree-grid/tree-grid.component.ts | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index b37603a204d..057a2324bc6 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -5194,7 +5194,7 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements } /** - * Returns the currently transformed paged/filtered/sorted/grouped row data, displayed in the grid. + * Returns the currently transformed paged/filtered/sorted/grouped/pinned/unpinned row data, displayed in the grid. * @example * ```typescript * const dataView = this.grid.dataView; @@ -5446,7 +5446,7 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements * If `headers` is enabled, it will use the column header (if any) instead of the column field. */ public getSelectedData(formatters = false, headers = false) { - const source = this.dataView; + const source = this.filteredSortedData; return this.extractDataFromSelection(source, formatters, headers); } diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-integration.spec.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-integration.spec.ts index 1fd54bbf5b7..03be324c0f6 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-integration.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-integration.spec.ts @@ -1565,12 +1565,12 @@ describe('IgxTreeGrid - Integration #tGrid', () => { treeGrid.perPage = 5; fix.detectChanges(); - expect(treeGrid.dataView.length).toBe(5); + expect(treeGrid.dataView.length).toBe(6); treeGrid.perPage = 10; fix.detectChanges(); - expect(treeGrid.dataView.length).toBe(10); + expect(treeGrid.dataView.length).toBe(11); }); it('should correctly apply paging state for grid and paginator when there are pinned rows.', fakeAsync(() => { diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.html b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.html index 9fcec4c9701..22ce357aaf6 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.html +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.html @@ -79,9 +79,9 @@ | treeGridTransaction:id:pipeTrigger | visibleColumns:hasVisibleColumns | treeGridNormalizeRecord:pipeTrigger + | gridRowPinning:id:true:pipeTrigger | treeGridFiltering:filteringExpressionsTree:filterStrategy:advancedFilteringExpressionsTree:id:pipeTrigger:filteringPipeTrigger:true - | treeGridSorting:sortingExpressions:sortStrategy:id:pipeTrigger:true - | gridRowPinning:id:true:pipeTrigger as pinnedData'> + | treeGridSorting:sortingExpressions:sortStrategy:id:pipeTrigger:true as pinnedData'>
- @@ -60,7 +60,7 @@ - + {{cell.row.pinned ? 'lock' : 'lock_open'}} @@ -87,7 +87,7 @@ > - + {{cell.row.pinned ? 'lock' : 'lock_open'}} From 2db77fef91b0501d2eb9da391e9395f38f032849 Mon Sep 17 00:00:00 2001 From: skrustev Date: Mon, 27 Apr 2020 17:57:56 +0300 Subject: [PATCH 5/5] chore(*): Remove extra empty spaces. --- .../src/lib/grids/grid/grid-row-pinning.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-row-pinning.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-row-pinning.spec.ts index bde1c62a958..1d46fc1028c 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-row-pinning.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-row-pinning.spec.ts @@ -881,7 +881,7 @@ describe('Row Pinning #grid', () => { grid.navigateTo(10); await wait(DEBOUNCE_TIME); fix.detectChanges(); - + const firstRowCell = grid.getRowByIndex(0).cells.toArray()[1]; UIInteractions.simulateClickAndSelectEvent(firstRowCell); await wait(DEBOUNCE_TIME); @@ -908,9 +908,9 @@ describe('Row Pinning #grid', () => { fix.detectChanges(); await wait(DEBOUNCE_TIME); fix.detectChanges(); - + expect(grid.verticalScrollContainer.getScroll().scrollTop).not.toEqual(0); - + const lastRowCell = grid.getRowByIndex(27).cells.toArray()[1]; UIInteractions.simulateClickAndSelectEvent(lastRowCell); await wait(DEBOUNCE_TIME); @@ -962,7 +962,7 @@ describe('Row Pinning #grid', () => { fix.detectChanges(); await wait(DEBOUNCE_TIME); fix.detectChanges(); - + const lastRowCell = grid.getRowByIndex(27).cells.toArray()[1]; UIInteractions.simulateClickAndSelectEvent(lastRowCell); await wait(DEBOUNCE_TIME);