diff --git a/packages/devextreme/js/__internal/ui/selection/m_selection.strategy.standard.ts b/packages/devextreme/js/__internal/ui/selection/m_selection.strategy.standard.ts index 98dd33692ecc..2b1cd10c4fa1 100644 --- a/packages/devextreme/js/__internal/ui/selection/m_selection.strategy.standard.ts +++ b/packages/devextreme/js/__internal/ui/selection/m_selection.strategy.standard.ts @@ -512,7 +512,7 @@ export default class StandardStrategy extends SelectionStrategy { const { selectedItems, selectedItemKeys, keyHashIndices } = this.options; this._storedSelectionState = { - keyHashIndices: { ...keyHashIndices }, + keyHashIndices: JSON.stringify(keyHashIndices), selectedItems: [...selectedItems], selectedItemKeys: [...selectedItemKeys], }; @@ -524,7 +524,7 @@ export default class StandardStrategy extends SelectionStrategy { const { selectedItemKeys, selectedItems, keyHashIndices } = this._storedSelectionState!; this._setOption('selectedItemKeys', selectedItemKeys); this._setOption('selectedItems', selectedItems); - this._setOption('keyHashIndices', keyHashIndices); + this._setOption('keyHashIndices', JSON.parse(keyHashIndices)); } _onePageSelectAll(isDeselect: boolean): DeferredObj { diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets/selection.test.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets/selection.test.js index 0c30d0d2d480..5443a3199ab7 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets/selection.test.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets/selection.test.js @@ -2560,6 +2560,54 @@ QUnit.module('onSelectionChanging', { assert.strictEqual(selectionChangingHandler.callCount, 2, 'selectionChanging is called once'); }); + QUnit.test('isItemSelected should work correctly after selection change is cancelled', function(assert) { + let cancelSelectionChange = false; + const selectionChangingHandler = sinon.spy((args) => { + args.cancel = cancelSelectionChange; + }); + + const selection = new Selection({ + ...this.basicSelectionConfig, + onSelectionChanging: selectionChangingHandler + }); + + this.dataSource.load(); + + const firstItem = this.data[0]; + + cancelSelectionChange = false; + selection.select(firstItem); + + cancelSelectionChange = true; + selection.deselect(firstItem); + + const isFirstItemSelected = selection.isItemSelected(firstItem); + assert.strictEqual(isFirstItemSelected, true, 'item is still selected after canceled deselect'); + }); + + QUnit.test('getSelectAllState should work correctly after selection change is cancelled', function(assert) { + let cancelSelectionChange = false; + const selectionChangingHandler = sinon.spy((args) => { + args.cancel = cancelSelectionChange; + }); + + const selection = new Selection({ + ...this.basicSelectionConfig, + onSelectionChanging: selectionChangingHandler + }); + + this.dataSource.load(); + + cancelSelectionChange = false; + selection.selectAll(true); + + cancelSelectionChange = true; + selection.deselect(true); + + const areAllItemsSelected = selection.getSelectAllState(true); + assert.strictEqual(areAllItemsSelected, true, 'items are still selected after canceled deselectAll'); + }); + QUnit.module('select all by one page', { beforeEach: function() { this.dataSource = createDataSource(this.data, {}, { paginate: true, pageSize: 3 }); diff --git a/packages/devextreme/testing/tests/DevExpress.ui/collectionWidgetParts/editingTests.js b/packages/devextreme/testing/tests/DevExpress.ui/collectionWidgetParts/editingTests.js index c381f42ac8db..6fcf86c77c57 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui/collectionWidgetParts/editingTests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui/collectionWidgetParts/editingTests.js @@ -409,6 +409,50 @@ module('onSelectionChanging event', () => { assert.strictEqual(instance.option('selectedIndex'), 1, 'selectedIndex should be updated'); }); + QUnit.test('if e.cancel is false even if previous request was cancelled and dataSource.store.key is used', function(assert) { + let cancelSelectionChange = false; + const selectionChangingHandler = sinon.spy((e) => { + e.cancel = cancelSelectionChange; + }); + const selectionChangedHandler = sinon.stub(); + + const $element = $('#cmp'); + const instance = new TestComponent($element, { + dataSource: new DataSource({ + store: new ArrayStore({ + key: 'id', + data: [{ text: '1', id: 1 }, { text: '2', id: 2 }], + }), + }), + selectionMode: 'multiple', + onSelectionChanging: selectionChangingHandler, + onSelectionChanged: selectionChangedHandler + }); + + assert.strictEqual(instance.option('selectedIndex'), -1, 'no item is selected initially'); + + const $items = $(instance.itemElements()); + const $firstItem = $items.eq(0); + + cancelSelectionChange = false; + $firstItem.trigger('dxclick'); + assert.strictEqual(selectionChangingHandler.callCount, 1, 'selectionChanging should be raised once'); + assert.strictEqual(selectionChangedHandler.callCount, 1, 'selectionChanged should be raised once'); + assert.strictEqual(instance.option('selectedIndex'), 0, 'item is selected'); + + cancelSelectionChange = true; + $firstItem.trigger('dxclick'); + assert.strictEqual(selectionChangingHandler.callCount, 2, 'selectionChanging should be raised once'); + assert.strictEqual(selectionChangedHandler.callCount, 1, 'selectionChanged is not raised'); + assert.strictEqual(instance.option('selectedIndex'), 0, 'item is still selected'); + + cancelSelectionChange = false; + $firstItem.trigger('dxclick'); + assert.strictEqual(selectionChangingHandler.callCount, 3, 'selectionChanging should be raised once'); + assert.strictEqual(selectionChangedHandler.callCount, 2, 'selectionChanged is raised'); + assert.strictEqual(instance.option('selectedIndex'), -1, 'item is deselected'); + }); + QUnit.test('if e.cancel is a promise resolved with false', function(assert) { const done = assert.async();