diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/selection/controller.integration.test.ts b/packages/devextreme/js/__internal/grids/new/grid_core/selection/controller.integration.test.ts index 46ae9740c078..64742a87903b 100644 --- a/packages/devextreme/js/__internal/grids/new/grid_core/selection/controller.integration.test.ts +++ b/packages/devextreme/js/__internal/grids/new/grid_core/selection/controller.integration.test.ts @@ -13,6 +13,7 @@ const SELECTORS = { card: '.dx-cardview-card', cardCheckbox: '.dx-checkbox-container', selectAllButton: '[aria-label="Select all"]', + clearSelectionButton: '[aria-label="Clear selection"]', }; const setup = (options: GridCoreOptions = {}): CardView => { @@ -31,6 +32,8 @@ const getCardCheckboxes = (): NodeListOf => document.querySelectorAll(S const getSelectAllButton = (): Element | null => document.querySelector(SELECTORS.selectAllButton); +const getClearSelectionButton = (): Element | null => document.querySelector(SELECTORS.clearSelectionButton); + const checkError = (): void => expect(throwError).toHaveBeenCalledWith('E1042', 'CardView'); jest.mock('@ts/grids/new/grid_core/options_validation/utils', () => ({ @@ -175,6 +178,30 @@ describe('when keyExpr is missing', () => { }); }); +describe('clear selection toolbar button', () => { + it('should clear selection after runtime selectedCardKeys option update', () => { + const cardView = setup({ + keyExpr: 'id', + dataSource: [{ id: 1 }, { id: 2 }], + selection: { + mode: 'multiple', + }, + selectedCardKeys: [], + }); + + expect(cardView.getSelectedCardKeys()).toEqual([]); + + cardView.option('selectedCardKeys', [2]); + + expect(cardView.getSelectedCardKeys()).toEqual([2]); + + const clearSelectionButton = getClearSelectionButton(); + clearSelectionButton?.dispatchEvent(new MouseEvent('click')); + + expect(cardView.getSelectedCardKeys()).toEqual([]); + }); +}); + describe('selectAll with filters', () => { it('should select only cards matching filterValue', () => { const cardView = setup({ diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/selection/controller.test.ts b/packages/devextreme/js/__internal/grids/new/grid_core/selection/controller.test.ts index 5f47fca4b12d..042eeff4b9cf 100644 --- a/packages/devextreme/js/__internal/grids/new/grid_core/selection/controller.test.ts +++ b/packages/devextreme/js/__internal/grids/new/grid_core/selection/controller.test.ts @@ -42,6 +42,29 @@ describe('SelectionController', () => { selectionController.selectCards([1]); expect(itemsController.items).toMatchSnapshot(); }); + + it('should only be called once when selectedCardKeys are updated', () => { + const { + selectionController, + optionsController, + } = setup({ + keyExpr: 'id', + dataSource: [{ id: 1, value: 'test' }, { id: 2, value: 'test2' }], + selection: { + mode: 'multiple', + }, + }); + + const selectCardsSpy = jest.spyOn(selectionController, 'selectCards'); + + optionsController.option('selectedCardKeys', [1]); + + expect(selectCardsSpy).toHaveBeenCalledTimes(1); + + optionsController.option('selectedCardKeys', []); + + expect(selectCardsSpy).toHaveBeenCalledTimes(2); + }); }); describe('deselectCards', () => { diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/selection/controller.ts b/packages/devextreme/js/__internal/grids/new/grid_core/selection/controller.ts index aeb452890f66..98ef90b6d003 100644 --- a/packages/devextreme/js/__internal/grids/new/grid_core/selection/controller.ts +++ b/packages/devextreme/js/__internal/grids/new/grid_core/selection/controller.ts @@ -154,14 +154,15 @@ export class SelectionController { Need to get rid of `selectionHelper.peek()` inside of selectCards() and pass selectionHelper from here */ - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - this.selectionHelper.value; - + const selectionHelper = this.selectionHelper.value; const isLoaded = this.dataController.isLoaded.value; if (isLoaded) { - const selectedCardKeys = this.selectedCardKeys.peek(); + const keys = this.selectedCardKeys.value; + const hasExactSelectedItems = selectionHelper?.hasExactSelectedItems(keys); - this.selectCards(selectedCardKeys); + if (!hasExactSelectedItems) { + this.selectCards(keys); + } } }); diff --git a/packages/devextreme/js/__internal/ui/selection/selection.ts b/packages/devextreme/js/__internal/ui/selection/selection.ts index cf5ba98cbb3f..20a62ae1a6a2 100644 --- a/packages/devextreme/js/__internal/ui/selection/selection.ts +++ b/packages/devextreme/js/__internal/ui/selection/selection.ts @@ -402,4 +402,15 @@ export default class Selection< loadSelectedItemsWithFilter(): DeferredObj { return this._selectionStrategy.loadSelectedItemsWithFilter(); } + + hasExactSelectedItems(keys: TKey[]): boolean { + const { selectedItems, keyOf } = this.options; + if (keys.length === selectedItems.length) { + return keys.every((key) => selectedItems.some((item) => { + const isKeysEqual = this._selectionStrategy.equalKeys(keyOf(item), key); + return isKeysEqual; + })); + } + return false; + } }