Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion demos/aurelia/test/cypress/e2e/example32.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ describe('Example 32 - Columns Resize by Content', () => {
cy.get('#filter-checkbox-selectall-container input[type=checkbox]').click({ force: true });

cy.window().then((win) => {
expect(win.console.log).to.have.callCount(3);
const selectedIdsCalls = (win.console.log as any).getCalls().filter((call: any) => call.args[0] === 'Selected Ids:');
expect(win.console.log).to.be.calledWith('Selected Ids:', expectedRowIds);
expect(selectedIdsCalls.some((call: any) => call.args[1]?.length === 401)).to.be.true;
});
});

Expand Down
3 changes: 2 additions & 1 deletion demos/react/test/cypress/e2e/example32.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ describe('Example 32 - Columns Resize by Content', () => {
cy.get('#filter-checkbox-selectall-container input[type=checkbox]').click({ force: true });

cy.window().then((win) => {
expect(win.console.log).to.have.callCount(3);
const selectedIdsCalls = (win.console.log as any).getCalls().filter((call: any) => call.args[0] === 'Selected Ids:');
expect(win.console.log).to.be.calledWith('Selected Ids:', expectedRowIds);
expect(selectedIdsCalls.some((call: any) => call.args[1]?.length === 401)).to.be.true;
});
});

Expand Down
3 changes: 2 additions & 1 deletion demos/vue/test/cypress/e2e/example32.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ describe('Example 32 - Columns Resize by Content', () => {
cy.get('#filter-checkbox-selectall-container input[type=checkbox]').click({ force: true });

cy.window().then((win) => {
expect(win.console.log).to.have.callCount(3);
const selectedIdsCalls = (win.console.log as any).getCalls().filter((call: any) => call.args[0] === 'Selected Ids:');
expect(win.console.log).to.be.calledWith('Selected Ids:', expectedRowIds);
expect(selectedIdsCalls.some((call: any) => call.args[1]?.length === 401)).to.be.true;
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ describe('Example 32 - Columns Resize by Content', () => {
cy.get('#filter-checkbox-selectall-container input[type=checkbox]').click({ force: true });

cy.window().then((win) => {
expect(win.console.log).to.have.callCount(3);
const selectedIdsCalls = (win.console.log as any).getCalls().filter((call: any) => call.args[0] === 'Selected Ids:');
expect(win.console.log).to.be.calledWith('Selected Ids:', expectedRowIds);
expect(selectedIdsCalls.some((call: any) => call.args[1]?.length === 401)).to.be.true;
});
});

Expand Down
79 changes: 79 additions & 0 deletions packages/common/src/core/__tests__/slickDataView.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2197,6 +2197,85 @@ describe('SlickDatView core file', () => {
]);
});

it('should report all filtered selected IDs when selection changes through the grid', () => {
const columns = [
{ id: 'name', field: 'name', name: 'Name' },
{ id: 'age', field: 'age', name: 'Age' },
];
const gridOptions = { enableCellNavigation: true, multiSelect: true, devMode: { ownerNodeIndex: 0 } } as GridOption;
dv = new SlickDataView({});
const grid = new SlickGrid('#myGrid', dv, columns, gridOptions);
const onSelectedRowIdsSpy = vi.spyOn(dv.onSelectedRowIdsChanged, 'notify');
grid.setSelectionModel(new SlickHybridSelectionModel({ selectActiveRow: false, selectionType: 'row' }));
dv.setItems(items);
dv.setPagingOptions({ dataView: dv, pageNum: 0, pageSize: 4 });
dv.syncGridSelection(grid, false, true);
dv.setSelectedIds([3, 4, 8], { isRowBeingAdded: true, applyRowSelectionToGrid: true });
onSelectedRowIdsSpy.mockClear();

grid.setSelectedRows([0, 1, 2]);

expect(onSelectedRowIdsSpy).toHaveBeenLastCalledWith(expect.objectContaining({ filteredIds: [4, 3, 1, 8] }), expect.anything(), dv);
});

it('should normalize an existing unsorted selection when the same IDs are selected again', () => {
const columns = [
{ id: 'name', field: 'name', name: 'Name' },
{ id: 'age', field: 'age', name: 'Age' },
];
const gridOptions = { enableCellNavigation: true, multiSelect: true, devMode: { ownerNodeIndex: 0 } } as GridOption;
dv = new SlickDataView({});
const grid = new SlickGrid('#myGrid', dv, columns, gridOptions);
grid.setSelectionModel(new SlickHybridSelectionModel({ selectActiveRow: false, selectionType: 'row' }));
dv.setItems(items);
grid.setSelectedRows([0, 1]);
dv.syncGridSelection(grid, false, true);

dv.setSelectedIds([3, 4], { isRowBeingAdded: true, applyRowSelectionToGrid: false });

expect(dv.getAllSelectedIds()).toEqual([3, 4]);
});

it('should reuse pending filtered IDs for bulk grid selection', () => {
const columns = [
{ id: 'name', field: 'name', name: 'Name' },
{ id: 'age', field: 'age', name: 'Age' },
];
const gridOptions = { enableCellNavigation: true, multiSelect: true, devMode: { ownerNodeIndex: 0 } } as GridOption;
dv = new SlickDataView({});
const grid = new SlickGrid('#myGrid', dv, columns, gridOptions);
const onSelectedRowIdsSpy = vi.spyOn(dv.onSelectedRowIdsChanged, 'notify');
grid.setSelectionModel(new SlickHybridSelectionModel({ selectActiveRow: false, selectionType: 'row' }));
dv.setItems(items);
dv.setPagingOptions({ dataView: dv, pageNum: 0, pageSize: 4 });
dv.syncGridSelection(grid, false, true);
dv.setSelectedIds([3, 4, 8], { isRowBeingAdded: true, shouldTriggerEvent: false, applyRowSelectionToGrid: false });

grid.setSelectedRows([0, 1], 'click.selectAll');

expect(onSelectedRowIdsSpy).toHaveBeenLastCalledWith(expect.objectContaining({ filteredIds: [3, 4, 8] }), expect.anything(), dv);
});

it('should ignore pending filtered IDs when bulk grid selection changes the selected IDs', () => {
const columns = [
{ id: 'name', field: 'name', name: 'Name' },
{ id: 'age', field: 'age', name: 'Age' },
];
const gridOptions = { enableCellNavigation: true, multiSelect: true, devMode: { ownerNodeIndex: 0 } } as GridOption;
dv = new SlickDataView({});
const grid = new SlickGrid('#myGrid', dv, columns, gridOptions);
const onSelectedRowIdsSpy = vi.spyOn(dv.onSelectedRowIdsChanged, 'notify');
grid.setSelectionModel(new SlickHybridSelectionModel({ selectActiveRow: false, selectionType: 'row' }));
dv.setItems(items);
dv.setPagingOptions({ dataView: dv, pageNum: 0, pageSize: 4 });
dv.syncGridSelection(grid, false, true);
dv.setSelectedIds([8], { isRowBeingAdded: true, shouldTriggerEvent: false, applyRowSelectionToGrid: false });

grid.setSelectedRows([0, 1], 'click.selectAll');

expect(onSelectedRowIdsSpy).toHaveBeenLastCalledWith(expect.objectContaining({ filteredIds: [4, 3, 8] }), expect.anything(), dv);
});

it('should not expect row selections to be preserved when using "multiSelect:false" and setSelectedIds() even when either preseve is enabled ("preserveHidden" or "preserveHiddenOnSelectionChange")', () => {
const columns = [
{ id: 'name', field: 'name', name: 'Name' },
Expand Down
59 changes: 59 additions & 0 deletions packages/common/src/core/__tests__/slickGrid.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ class TestGrid extends SlickGrid<any, Column> {
public callGetFormatter(row: number, column: Column) {
return this.getFormatter(row, column);
}
public callRowsToRanges(rows: number[], compactRows = false) {
return this.rowsToRanges(rows, compactRows);
}
public callAppendSelectedCellHtml(row: number, cell: number) {
const divRow = document.createElement('div');
this.selectedRanges = [new SlickRange(row, cell)];
this.appendCellHtml(divRow, row, cell, 1, 1, null, this.getDataItem(row));
return divRow.firstElementChild as HTMLDivElement;
}
public setCurrentEditorNull() {
(this as any).currentEditor = null;
}
Expand Down Expand Up @@ -969,6 +978,38 @@ describe('SlickGrid core file', () => {
expect(secondRowItemCell.classList.contains('selected')).toBeTruthy();
});

it('should compact contiguous row ranges when Select All is clicked', () => {
const rowSelectionModel = new SlickHybridSelectionModel({ selectionType: 'row' });
const setRangeSpy = vi.spyOn(rowSelectionModel, 'setSelectedRanges');

grid = new SlickGrid<any, Column>(container, data, columns, defaultOptions);
grid.setSelectionModel(rowSelectionModel);
vi.spyOn(grid.getEditorLock(), 'isActive').mockReturnValueOnce(false);

grid.setSelectedRows([0, 1], 'click.selectAll');

expect(setRangeSpy).toHaveBeenCalledWith([new SlickRange(0, 0, 1, 0)], 'click.selectAll');
});

it('should compact non-contiguous ascending rows and allow an empty selection', () => {
grid = new TestGrid(container, data, columns, defaultOptions);

expect((grid as TestGrid).callRowsToRanges([0, 2], true)).toEqual([new SlickRange(0, 0, 0, 0), new SlickRange(2, 0, 2, 0)]);
expect((grid as TestGrid).callRowsToRanges([], true)).toEqual([]);
});

it('should retain legacy selection order when Select All receives non-ascending rows', () => {
const rowSelectionModel = new SlickHybridSelectionModel({ selectionType: 'row' });

grid = new SlickGrid<any, Column>(container, data, columns, defaultOptions);
grid.setSelectionModel(rowSelectionModel);
vi.spyOn(grid.getEditorLock(), 'isActive').mockReturnValueOnce(false);

grid.setSelectedRows([2, 1], 'click.selectAll');

expect(grid.getSelectedRows()).toEqual([1, 2]);
});

it('should select rows when the last column is hidden', () => {
const columnsWithHiddenLastColumn = [
{ id: 'firstName', field: 'firstName', name: 'First Name' },
Expand All @@ -984,6 +1025,24 @@ describe('SlickGrid core file', () => {
expect(grid.getSelectedRows()).toEqual([1]);
});

it('should add selected CSS when rendering a selected cell without a CSS hash', () => {
grid = new TestGrid(container, data, columns, defaultOptions);

const cell = (grid as TestGrid).callAppendSelectedCellHtml(1, 0);

expect(cell.classList.contains('selected')).toBeTruthy();
});

it('should add selected CSS alongside an existing cell CSS hash', () => {
grid = new TestGrid(container, data, columns, defaultOptions);
grid.setCellCssStyles('highlight', { 1: { firstName: 'highlight' } });

const cell = (grid as TestGrid).callAppendSelectedCellHtml(1, 0);

expect(cell.classList).toContain('highlight');
expect(cell.classList).toContain('selected');
});

it('should call SlickHybridSelectionModel.onDragReplaceCells() when selection mode is REP and range is expanding', () => {
const hybridSelectionModel = new SlickHybridSelectionModel();
hybridSelectionModel.activeSelectionIsRow = true;
Expand Down
31 changes: 28 additions & 3 deletions packages/common/src/core/slickDataView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ export class SlickDataView<TData extends SlickDataItem = any> implements CustomD
protected toggledGroupsByLevel: any[] = [];
protected groupingDelimiter = ':|:';
protected selectedRowIds: DataIdType[] = [];
protected pendingSelectedFilteredIds?: { ids: DataIdType[]; selectedRowIds: DataIdType[] };
protected preSelectedRowIdsChangeFn?: (args?: any) => void;

protected pagesize = 0;
Expand Down Expand Up @@ -1392,8 +1393,21 @@ export class SlickDataView<TData extends SlickDataItem = any> implements CustomD
if (rowIds === false) {
this.selectedRowIds = [];
} else {
if (this.selectedRowIds!.sort().join(',') !== rowIds.sort().join(',')) {
this.selectedRowIds = rowIds;
const sortedRowIds = rowIds.every((id, index) => index === 0 || `${rowIds[index - 1]}` <= `${id}`) ? rowIds : rowIds.slice().sort();
const sortedSelectedRowIds = this.selectedRowIds!.every(
(id, index) => index === 0 || `${this.selectedRowIds![index - 1]}` <= `${id}`
)
? this.selectedRowIds
: this.selectedRowIds!.slice().sort();
let selectedRowIdsChanged = this.selectedRowIds!.length !== sortedRowIds.length;
if (!selectedRowIdsChanged) {
const selectedRowIdsSet = new Set(this.selectedRowIds);
selectedRowIdsChanged = sortedRowIds.some((id) => !selectedRowIdsSet.has(id));
}
if (selectedRowIdsChanged) {
this.selectedRowIds = sortedRowIds;
} else if (sortedSelectedRowIds !== this.selectedRowIds) {
this.selectedRowIds = sortedSelectedRowIds;
}
}
};
Expand Down Expand Up @@ -1435,10 +1449,17 @@ export class SlickDataView<TData extends SlickDataItem = any> implements CustomD
dataView: this,
};
this.preSelectedRowIdsChangeFn!(selectedRowsChangedArgs);
const isBulkSelection = args.caller === 'click.selectAll' || args.caller === 'click.unselectAll';
const pendingSelectedFilteredIds = this.pendingSelectedFilteredIds;
const filteredIds =
isBulkSelection && pendingSelectedFilteredIds?.selectedRowIds === this.selectedRowIds
? pendingSelectedFilteredIds.ids
: (this.getAllSelectedFilteredIds() as DataIdType[]);
this.pendingSelectedFilteredIds = undefined;
this.onSelectedRowIdsChanged.notify(
Object.assign(selectedRowsChangedArgs, {
selectedRowIds: this.selectedRowIds,
filteredIds: this.getAllSelectedFilteredIds() as DataIdType[],
filteredIds,
}),
new SlickEventData(),
this
Expand Down Expand Up @@ -1532,6 +1553,10 @@ export class SlickDataView<TData extends SlickDataItem = any> implements CustomD
};
this.preSelectedRowIdsChangeFn?.(selectedRowsChangedArgs);

if (shouldTriggerEvent === false && applyRowSelectionToGrid === false) {
this.pendingSelectedFilteredIds = { ids: isRowBeingAdded ? selectedIds.slice() : [], selectedRowIds: this.selectedRowIds };
}

if (shouldTriggerEvent !== false) {
this.onSelectedRowIdsChanged.notify(
Object.assign(selectedRowsChangedArgs, {
Expand Down
Loading
Loading