UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

1,034 lines (1,033 loc) • 41.3 kB
/** * DevExtreme (esm/__internal/grids/new/grid_core/selection/controller.test.js) * Version: 25.1.3 * Build date: Wed Jun 25 2025 * * Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ import _extends from "@babel/runtime/helpers/esm/extends"; import { describe, expect, it, jest } from "@jest/globals"; import { DataController } from "../data_controller"; import { getContext } from "../di.test_utils"; import { ItemsController } from "../items_controller/items_controller"; import { OptionsControllerMock } from "../options_controller/options_controller.mock"; import { SelectionController } from "./controller"; const setup = function() { let config = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; const context = getContext(_extends({ selection: { mode: "single" }, selectedCardKeys: [] }, config)); return { optionsController: context.get(OptionsControllerMock), selectionController: context.get(SelectionController), itemsController: context.get(ItemsController), dataController: context.get(DataController) } }; describe("SelectionController", (() => { describe("selectCards", (() => { it("should select item", (() => { const { selectionController: selectionController, itemsController: itemsController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }] }); selectionController.selectCards([1]); expect(itemsController.items).toMatchSnapshot() })) })); describe("deselectCards", (() => { it("should deselect item", (() => { const { selectionController: selectionController, itemsController: itemsController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selectedCardKeys: [1] }); selectionController.deselectCards([1]); expect(itemsController.items).toMatchSnapshot() })) })); describe("selectCardsByIndexes", (() => { it("should select item", (() => { const { selectionController: selectionController, itemsController: itemsController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }] }); selectionController.selectCardsByIndexes([0]); expect(itemsController.items).toMatchSnapshot() })) })); describe("deselectCardsByIndexes", (() => { it("should deselect item", (() => { const { selectionController: selectionController, itemsController: itemsController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selectedCardKeys: [1] }); selectionController.deselectCardsByIndexes([0]); expect(itemsController.items).toMatchSnapshot() })) })); describe("changeCardSelection", (() => { describe("when the control arg equal to false", (() => { it("should update the select state of the item", (() => { const { selectionController: selectionController, itemsController: itemsController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }] }); selectionController.changeCardSelection(0, { control: false }); expect(itemsController.items).toMatchSnapshot() })) })); describe("when the control arg equal to true", (() => { it("should update the select state of the item", (() => { const { selectionController: selectionController, itemsController: itemsController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selectedCardKeys: [1] }); selectionController.changeCardSelection(0, { control: true }); expect(itemsController.items).toMatchSnapshot() })) })); describe("when item is selected and multiple selection enabled", (() => { it("should update the select state of the item", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selectedCardKeys: [1], selection: { mode: "multiple", showCheckBoxesMode: "always" } }); selectionController.changeCardSelection(0); expect(selectionController.getSelectedCardKeys()).toEqual([]) })) })) })); describe("isCardSelected", (() => { describe("when the selectedCardKeys is specified", (() => { it("should return true", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selectedCardKeys: [1] }); expect(selectionController.isCardSelected(1)).toBe(true) })) })); describe("when the selectedCardKeys isn't specified", (() => { it("should return false", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }] }); expect(selectionController.isCardSelected(1)).toBe(false) })) })) })); describe("getSelectedCardKeys", (() => { it("should return the selected card keys", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selectedCardKeys: [1] }); expect(selectionController.getSelectedCardKeys()).toEqual([1]) })) })); describe("getSelectedCardsData", (() => { it("should return the selected cards data", (() => { const { selectionController: selectionController, dataController: dataController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selectedCardKeys: [1] }); expect(selectionController.getSelectedCardsData()).toEqual(dataController.items.peek()) })) })); describe("clearSelection", (() => { it("should clear the selection", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selectedCardKeys: [1] }); selectionController.clearSelection(); expect(selectionController.getSelectedCardKeys().length).toBe(0) })) })); describe("updateSelectionCheckBoxesVisible", (() => { describe("when the selection mode is equal to 'multiple' and the showCheckBoxesMode is equal to 'onClick'", (() => { it("should show the selection checkboxes", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selection: { mode: "multiple", showCheckBoxesMode: "onClick" } }); selectionController.updateSelectionCheckBoxesVisible(true); expect(selectionController.isCheckBoxesVisible.peek()).toBe(true) })); it("should hide the selection checkboxes", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selection: { mode: "multiple", showCheckBoxesMode: "onClick" } }); selectionController.updateSelectionCheckBoxesVisible(false); expect(selectionController.isCheckBoxesVisible.peek()).toBe(false) })) })) })); describe("processLongTap", (() => { describe("when the selection mode is equal to 'multiple' and the showCheckBoxesMode is equal to 'onLongTap'", (() => { it("should render the selection checkbox", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selection: { mode: "multiple", showCheckBoxesMode: "onLongTap" } }); selectionController.processLongTap({ index: 0 }); expect(selectionController.isCheckBoxesRendered.peek()).toBe(true) })) })); describe("when the selection mode is equal to 'multiple' and the showCheckBoxesMode is equal to 'onClick'", (() => { it("should show the selection checkbox", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selection: { mode: "multiple", showCheckBoxesMode: "onClick" } }); selectionController.processLongTap({ index: 0 }); expect(selectionController.isCheckBoxesVisible.peek()).toBe(true) })) })); describe("when the selection mode is equal to 'multiple' and the showCheckBoxesMode is equal to 'none'", (() => { it("should select a first item", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selection: { mode: "multiple", showCheckBoxesMode: "none" } }); selectionController.processLongTap({ index: 0 }); expect(selectionController.getSelectedCardKeys()).toEqual([1]) })) })); describe("when the selection mode is equal to 'multiple' and the showCheckBoxesMode is equal to 'none'", (() => { it("should not select a first item", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selection: { mode: "multiple", showCheckBoxesMode: "always" } }); selectionController.processLongTap({ index: 0 }); expect(selectionController.getSelectedCardKeys()).toEqual([]) })) })) })); describe("isCheckBoxesRendered", (() => { describe("when the selection mode is equal to 'none'", (() => { it("should return false", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selection: { mode: "none" } }); expect(selectionController.isCheckBoxesRendered.peek()).toBe(false) })) })); describe("when the selection mode is equal to 'multiple' and the showCheckBoxesMode is equal to 'always'", (() => { it("should return true", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selection: { mode: "multiple", showCheckBoxesMode: "always" } }); expect(selectionController.isCheckBoxesRendered.peek()).toBe(true) })) })); describe("when the selection mode is equal to 'multiple' and the showCheckBoxesMode is equal to 'onClick'", (() => { it("should return true", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selection: { mode: "multiple", showCheckBoxesMode: "onClick" } }); expect(selectionController.isCheckBoxesRendered.peek()).toBe(true) })) })); describe("when the selection mode is equal to 'multiple' and the showCheckBoxesMode is equal to 'onLongTap'", (() => { it("should return false", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selection: { mode: "multiple", showCheckBoxesMode: "onLongTap" } }); expect(selectionController.isCheckBoxesRendered.peek()).toBe(false) })) })) })); describe("isCheckBoxesVisible", (() => { describe("when the selection mode is equal to 'multiple' and the showCheckBoxesMode is equal to 'onClick'", (() => { it("should return false", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selection: { mode: "multiple", showCheckBoxesMode: "onClick" } }); expect(selectionController.isCheckBoxesVisible.peek()).toBe(false) })) })); describe("when selecting one card", (() => { it("should return false", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test1" }, { id: 2, value: "test2" }, { id: 3, value: "test3" }], selection: { mode: "multiple", showCheckBoxesMode: "onClick" } }); selectionController.selectCards([1]); expect(selectionController.isCheckBoxesVisible.peek()).toBe(false) })) })); describe("when selecting two cards", (() => { it("should return true", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test1" }, { id: 2, value: "test2" }, { id: 3, value: "test3" }], selection: { mode: "multiple", showCheckBoxesMode: "onClick" } }); selectionController.selectCards([1, 2]); expect(selectionController.isCheckBoxesVisible.peek()).toBe(true) })) })); describe("when deselecting all cards", (() => { it("should return false", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test1" }, { id: 2, value: "test2" }, { id: 3, value: "test3" }], selection: { mode: "multiple", showCheckBoxesMode: "onClick" }, selectedCardKeys: [1, 2] }); selectionController.deselectCards([1, 2]); expect(selectionController.isCheckBoxesVisible.peek()).toBe(false) })) })) })); describe("needToHiddenCheckBoxes", (() => { describe("when the selection mode is equal to 'multiple' and the showCheckBoxesMode is equal to 'onClick'", (() => { it("should return true", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selection: { mode: "multiple", showCheckBoxesMode: "onClick" } }); expect(selectionController.needToHiddenCheckBoxes.peek()).toBe(true) })) })); describe("when the selection mode is equal to 'multiple' and the showCheckBoxesMode is equal to 'always'", (() => { it("should return false", (() => { const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selection: { mode: "multiple", showCheckBoxesMode: "always" } }); expect(selectionController.needToHiddenCheckBoxes.peek()).toBe(false) })) })) })); describe("onSelectionChanging", (() => { describe("when selecting a card", (() => { it("should be called", (() => { const selectionChangingMockFn = jest.fn(); const cardData = { id: 1, value: "test" }; const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [cardData], selection: { mode: "multiple" }, onSelectionChanging: selectionChangingMockFn }); selectionController.selectCards([1]); expect(selectionChangingMockFn.mock.calls).toHaveLength(1); expect(selectionChangingMockFn.mock.lastCall).toMatchObject([{ cancel: false, currentDeselectedCardKeys: [], currentSelectedCardKeys: [1], isDeselectAll: false, isSelectAll: false, selectedCardKeys: [1], selectedCardsData: [cardData] }]) })) })); describe("when deselecting a card", (() => { it("should be called", (() => { const selectionChangingMockFn = jest.fn(); const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selection: { mode: "multiple" }, selectedCardKeys: [1], onSelectionChanging: selectionChangingMockFn }); selectionController.deselectCards([1]); expect(selectionChangingMockFn.mock.calls).toHaveLength(1); expect(selectionChangingMockFn.mock.lastCall).toMatchObject([{ cancel: false, currentDeselectedCardKeys: [1], currentSelectedCardKeys: [], isDeselectAll: false, isSelectAll: false, selectedCardKeys: [], selectedCardsData: [] }]) })) })); describe("when selecting all cards", (() => { it("should be called", (() => { const selectionChangingMockFn = jest.fn(); const data = [{ id: 1, value: "test1" }, { id: 2, value: "test2" }]; const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: data, selection: { mode: "multiple", allowSelectAll: true }, onSelectionChanging: selectionChangingMockFn }); selectionController.selectAll(); expect(selectionChangingMockFn.mock.calls).toHaveLength(1); expect(selectionChangingMockFn.mock.lastCall).toMatchObject([{ cancel: false, currentDeselectedCardKeys: [], currentSelectedCardKeys: [1, 2], isDeselectAll: false, isSelectAll: false, selectedCardKeys: [1, 2], selectedCardsData: data }]) })) })); describe("when deselecting all cards", (() => { it("should be called", (() => { const selectionChangingMockFn = jest.fn(); const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test1" }, { id: 2, value: "test2" }], selection: { mode: "multiple", allowSelectAll: true }, selectedCardKeys: [1, 2], onSelectionChanging: selectionChangingMockFn }); selectionController.deselectAll(); expect(selectionChangingMockFn.mock.calls).toHaveLength(1); expect(selectionChangingMockFn.mock.lastCall).toMatchObject([{ cancel: false, currentDeselectedCardKeys: [1, 2], currentSelectedCardKeys: [], isDeselectAll: false, isSelectAll: false, selectedCardKeys: [], selectedCardsData: [] }]) })) })); describe("when a cancel arg is specified as true", (() => { it("should be called", (() => { const selectionChangingMockFn = jest.fn((e => { e.cancel = true })); const cardData = { id: 1, value: "test" }; const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [cardData], selection: { mode: "multiple" }, onSelectionChanging: selectionChangingMockFn }); selectionController.selectCards([1]); expect(selectionChangingMockFn.mock.calls).toHaveLength(1); expect(selectionChangingMockFn.mock.lastCall).toMatchObject([{ cancel: true, currentDeselectedCardKeys: [], currentSelectedCardKeys: [1], isDeselectAll: false, isSelectAll: false, selectedCardKeys: [1], selectedCardsData: [cardData] }]); expect(selectionController.getSelectedCardKeys()).toEqual([]) })) })); describe("when a cancel arg is specified as Promise", (() => { it("should be called", (() => { const cancel = Promise.resolve(true); const selectionChangingMockFn = jest.fn((e => { e.cancel = cancel })); const cardData = { id: 1, value: "test" }; const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [cardData], selection: { mode: "multiple" }, onSelectionChanging: selectionChangingMockFn }); selectionController.selectCards([1]); expect(selectionChangingMockFn.mock.calls).toHaveLength(1); expect(selectionChangingMockFn.mock.lastCall).toMatchObject([{ cancel: cancel, currentDeselectedCardKeys: [], currentSelectedCardKeys: [1], isDeselectAll: false, isSelectAll: false, selectedCardKeys: [1], selectedCardsData: [cardData] }]); expect(selectionController.getSelectedCardKeys()).toEqual([]) })) })) })); describe("onSelectionChanged", (() => { describe("when selecting a card", (() => { it("should be called", (() => { const selectionChangedMockFn = jest.fn(); const cardData = { id: 1, value: "test" }; const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [cardData], selection: { mode: "multiple" }, onSelectionChanged: selectionChangedMockFn }); selectionController.selectCards([1]); expect(selectionChangedMockFn.mock.calls).toHaveLength(1); expect(selectionChangedMockFn.mock.lastCall).toMatchObject([{ currentDeselectedCardKeys: [], currentSelectedCardKeys: [1], isDeselectAll: false, isSelectAll: false, selectedCardKeys: [1], selectedCardsData: [cardData] }]) })) })); describe("when deselecting a card", (() => { it("should be called", (() => { const selectionChangedMockFn = jest.fn(); const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test" }], selection: { mode: "multiple" }, selectedCardKeys: [1], onSelectionChanged: selectionChangedMockFn }); selectionController.deselectCards([1]); expect(selectionChangedMockFn.mock.calls).toHaveLength(1); expect(selectionChangedMockFn.mock.lastCall).toMatchObject([{ currentDeselectedCardKeys: [1], currentSelectedCardKeys: [], isDeselectAll: false, isSelectAll: false, selectedCardKeys: [], selectedCardsData: [] }]) })) })); describe("when selection changes via selectionChanged callback", (() => { it("should update both selectionController and itemsController selection states when selecting single card", (() => { const cardData = { id: 1, value: "test" }; const { selectionController: selectionController, itemsController: itemsController } = setup({ keyExpr: "id", dataSource: [cardData], selection: { mode: "multiple" } }); const setSelectionStateSpy = jest.spyOn(itemsController, "setSelectionState"); const selectionChangedEvent = { addedItemKeys: [1], removedItemKeys: [], selectedItemKeys: [1], selectedItems: [cardData] }; selectionController.selectionChanged(selectionChangedEvent); expect(selectionController.getSelectedCardKeys()).toEqual([1]); expect(setSelectionStateSpy).toHaveBeenCalledWith([1]); const items = itemsController.items.peek(); expect(items[0].isSelected).toBe(true) })); it("should update both selectionController and itemsController when selecting multiple cards", (() => { const cardsData = [{ id: 1, value: "test1" }, { id: 2, value: "test2" }, { id: 3, value: "test3" }]; const { selectionController: selectionController, itemsController: itemsController } = setup({ keyExpr: "id", dataSource: cardsData, selection: { mode: "multiple" } }); const setSelectionStateSpy = jest.spyOn(itemsController, "setSelectionState"); const selectionChangedEvent = { addedItemKeys: [1, 3], removedItemKeys: [], selectedItemKeys: [1, 3], selectedItems: [cardsData[0], cardsData[2]] }; selectionController.selectionChanged(selectionChangedEvent); expect(selectionController.getSelectedCardKeys()).toEqual([1, 3]); expect(setSelectionStateSpy).toHaveBeenCalledWith([1, 3]); const items = itemsController.items.peek(); expect(items[0].isSelected).toBe(true); expect(items[1].isSelected).toBe(false); expect(items[2].isSelected).toBe(true) })); it("should update both selectionController and itemsController when deselecting cards", (() => { const cardsData = [{ id: 1, value: "test1" }, { id: 2, value: "test2" }, { id: 3, value: "test3" }]; const { selectionController: selectionController, itemsController: itemsController } = setup({ keyExpr: "id", dataSource: cardsData, selection: { mode: "multiple" }, selectedCardKeys: [1, 2, 3] }); expect(itemsController.items.peek()[0].isSelected).toBe(true); expect(itemsController.items.peek()[1].isSelected).toBe(true); expect(itemsController.items.peek()[2].isSelected).toBe(true); const setSelectionStateSpy = jest.spyOn(itemsController, "setSelectionState"); const selectionChangedEvent = { addedItemKeys: [], removedItemKeys: [2], selectedItemKeys: [1, 3], selectedItems: [cardsData[0], cardsData[2]] }; selectionController.selectionChanged(selectionChangedEvent); expect(selectionController.getSelectedCardKeys()).toEqual([1, 3]); expect(setSelectionStateSpy).toHaveBeenCalledWith([1, 3]); const items = itemsController.items.peek(); expect(items[0].isSelected).toBe(true); expect(items[1].isSelected).toBe(false); expect(items[2].isSelected).toBe(true) })); it("should throw error E1042 if keyExpr is missing and selectionChanged", (() => { const cardData = { id: 1, value: "test" }; const { selectionController: selectionController } = setup({ dataSource: [cardData], selection: { mode: "multiple" } }); const selectionChangedEvent = { addedItemKeys: [1], removedItemKeys: [], selectedItemKeys: [1], selectedItems: [cardData] }; expect((() => { selectionController.selectionChanged(selectionChangedEvent) })).toThrowError("E1042") })) })); describe("when selecting all cards", (() => { it("should be called", (() => { const selectionChangedMockFn = jest.fn(); const data = [{ id: 1, value: "test1" }, { id: 2, value: "test2" }]; const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: data, selection: { mode: "multiple", allowSelectAll: true }, onSelectionChanged: selectionChangedMockFn }); selectionController.selectAll(); expect(selectionChangedMockFn.mock.calls).toHaveLength(1); expect(selectionChangedMockFn.mock.lastCall).toMatchObject([{ currentDeselectedCardKeys: [], currentSelectedCardKeys: [1, 2], isDeselectAll: false, isSelectAll: false, selectedCardKeys: [1, 2], selectedCardsData: data }]) })) })); describe("when deselecting all cards", (() => { it("should be called", (() => { const selectionChangedMockFn = jest.fn(); const { selectionController: selectionController } = setup({ keyExpr: "id", dataSource: [{ id: 1, value: "test1" }, { id: 2, value: "test2" }], selection: { mode: "multiple", allowSelectAll: true }, selectedCardKeys: [1, 2], onSelectionChanged: selectionChangedMockFn }); selectionController.deselectAll(); expect(selectionChangedMockFn.mock.calls).toHaveLength(1); expect(selectionChangedMockFn.mock.lastCall).toMatchObject([{ currentDeselectedCardKeys: [1, 2], currentSelectedCardKeys: [], isDeselectAll: false, isSelectAll: false, selectedCardKeys: [], selectedCardsData: [] }]) })) })) })) }));