From 3d935ae7fb97de6a8e6406a0415276d8adb98070 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Wed, 24 Jun 2026 04:29:47 -0700 Subject: [PATCH] Fix IgxGridSelectionService.generateRange crash on drag-select released outside grid --- .../core/src/selection/selection.service.ts | 2 +- .../grid/src/grid-cell-selection.spec.ts | 65 ++++++++++++++++++- 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/grids/core/src/selection/selection.service.ts b/projects/igniteui-angular/grids/core/src/selection/selection.service.ts index db318ff7c91..7e8e4e71f8d 100644 --- a/projects/igniteui-angular/grids/core/src/selection/selection.service.ts +++ b/projects/igniteui-angular/grids/core/src/selection/selection.service.ts @@ -183,7 +183,7 @@ export class IgxGridSelectionService { public generateRange(node: ISelectionNode, state?: SelectionState): GridSelectionRange { this._lastSelectedNode = node; - if (!state) { + if (!state || !state.node) { return { rowStart: node.row, rowEnd: node.row, diff --git a/projects/igniteui-angular/grids/grid/src/grid-cell-selection.spec.ts b/projects/igniteui-angular/grids/grid/src/grid-cell-selection.spec.ts index cb622a8250f..2e3c9c7a915 100644 --- a/projects/igniteui-angular/grids/grid/src/grid-cell-selection.spec.ts +++ b/projects/igniteui-angular/grids/grid/src/grid-cell-selection.spec.ts @@ -11,7 +11,7 @@ import { } from '../../../test-utils/grid-samples.spec'; import { UIInteractions, wait } from '../../../test-utils/ui-interactions.spec'; import { clearGridSubs, setupGridScrollDetection } from '../../../test-utils/helper-utils.spec'; -import { GridSelectionMode } from 'igniteui-angular/grids/core'; +import { GridSelectionMode, GridSelectionRange } from 'igniteui-angular/grids/core'; import { GridSelectionFunctions, GridFunctions } from '../../../test-utils/grid-functions.spec'; import { DebugElement } from '@angular/core'; @@ -45,6 +45,19 @@ describe('IgxGrid - Cell selection #grid', () => { detect = () => grid.cdr.detectChanges(); }); + it('Should collapse generated range to a single node when pointer state has no anchor node', () => { + const node = { row: 2, column: 3 }; + const expectedRange = { rowStart: 2, rowEnd: 2, columnStart: 3, columnEnd: 3 }; + let range: GridSelectionRange | undefined; + + grid.selectionService.initPointerState(); + + expect(() => { + range = grid.selectionService.generateRange(node, grid.selectionService.pointerState); + }).not.toThrow(); + expect(range).toEqual(expectedRange); + }); + it('Should be able to select a range with mouse dragging', () => { const selectionChangeSpy = spyOn(grid.rangeSelected, 'emit').and.callThrough(); const startCell = grid.gridAPI.get_cell_by_index(2, 'ParentID'); @@ -99,6 +112,30 @@ describe('IgxGrid - Cell selection #grid', () => { expect(selectionChangeSpy).toHaveBeenCalledWith(range); }); + it('Should keep standard in-grid drag range selection with a non-null pointer anchor', () => { + const selectionChangeSpy = spyOn(grid.rangeSelected, 'emit').and.callThrough(); + const startCell = grid.gridAPI.get_cell_by_index(0, 'ID'); + const endCell = grid.gridAPI.get_cell_by_index(2, 'Name'); + const range = { rowStart: 0, rowEnd: 2, columnStart: 0, columnEnd: 2 }; + + UIInteractions.simulatePointerOverElementEvent('pointerdown', startCell.nativeElement); + detect(); + + expect(grid.selectionService.pointerState.node).not.toBeNull(); + + UIInteractions.simulatePointerOverElementEvent('pointerenter', endCell.nativeElement); + detect(); + + UIInteractions.simulatePointerOverElementEvent('pointerup', endCell.nativeElement); + detect(); + + GridSelectionFunctions.verifyCellsRegionSelected(grid, 0, 2, 0, 2); + GridSelectionFunctions.verifySelectedRange(grid, 0, 2, 0, 2); + + expect(selectionChangeSpy).toHaveBeenCalledTimes(1); + expect(selectionChangeSpy).toHaveBeenCalledWith(range); + }); + it('Should not lose selection on right clicking', () => { const selectionChangeSpy = spyOn(grid.rangeSelected, 'emit').and.callThrough(); const range = { rowStart: 2, rowEnd: 3, columnStart: 0, columnEnd: 1 }; @@ -954,6 +991,32 @@ describe('IgxGrid - Cell selection #grid', () => { expect(selectionChangeSpy).toHaveBeenCalledWith(range); }); + it('Should not throw and should emit single-cell range when drag is released outside grid after keyboard navigation', () => { + const selectionChangeSpy = spyOn(grid.rangeSelected, 'emit').and.callThrough(); + const startCell = grid.gridAPI.get_cell_by_index(2, 'ParentID'); + + UIInteractions.simulatePointerOverElementEvent('pointerdown', startCell.nativeElement); + detect(); + + UIInteractions.triggerKeyDownEvtUponElem('arrowright', startCell.nativeElement); + detect(); + + const activeNode = grid.navigation.activeNode; + const range = { + rowStart: activeNode.row, + rowEnd: activeNode.row, + columnStart: activeNode.column, + columnEnd: activeNode.column + }; + + expect(grid.selectionService.pointerState.node).toBeNull(); + expect(() => UIInteractions.simulatePointerOverElementEvent('pointerup', document.body)).not.toThrow(); + detect(); + + expect(selectionChangeSpy).toHaveBeenCalledTimes(1); + expect(selectionChangeSpy).toHaveBeenCalledWith(range); + }); + it('Should not throw an error when trying to do a drag selection that is started outside the grid', fakeAsync(() => { const cell = grid.gridAPI.get_cell_by_index(1, 'ParentID');