diff --git a/e2e/testcafe-devextreme/tests/cardView/headerFilter/remote.functional.ts b/e2e/testcafe-devextreme/tests/cardView/headerFilter/remote.functional.ts index 2f2016b76954..4a51c6bedbfa 100644 --- a/e2e/testcafe-devextreme/tests/cardView/headerFilter/remote.functional.ts +++ b/e2e/testcafe-devextreme/tests/cardView/headerFilter/remote.functional.ts @@ -532,3 +532,53 @@ const clearRemoteOperations = () => ClientFunction(() => { await clearRemoteOperations(); }); }); + +test('remote operations: true -> should load data for each search value in header filter', async (t) => { + // arrange + const cardView = new CardView(CARD_VIEW_SELECTOR); + const filterIcon = cardView + .getHeaderPanel() + .getHeaderItem() + .getFilterIcon(); + + // act + await t.click(filterIcon); + + // assert + const list = cardView.getHeaderFilterList(); + await t.expect(list.getItems().count).eql(10); + + // act + await t.typeText(list.searchInput, 'A_1'); + + // assert + await t.expect(list.getItems().count).eql(1); + await t.expect(list.getItem(0).text).eql('A_1'); + + // act + await t.typeText(list.searchInput, '1'); + + // assert + await t.expect(list.getItems().count).eql(0); +}).before(async (t) => { + await t.addRequestHooks(remoteApiMock); + await createWidget('dxCardView', () => ({ + dataSource: { + store: (window as any).DevExpress.data.AspNet.createStore({ + key: 'id', + loadUrl: 'https://api/data', + }), + }, + columns: ['A'], + remoteOperations: true, + headerFilter: { + visible: true, + search: { + enabled: true, + }, + }, + height: 600, + })); +}).after(async (t) => { + await t.removeRequestHooks(remoteApiMock); +}); diff --git a/e2e/testcafe-devextreme/tests/cardView/helpers/remoteApiMock.ts b/e2e/testcafe-devextreme/tests/cardView/helpers/remoteApiMock.ts index 52d25ae55513..94a37b7f2807 100644 --- a/e2e/testcafe-devextreme/tests/cardView/helpers/remoteApiMock.ts +++ b/e2e/testcafe-devextreme/tests/cardView/helpers/remoteApiMock.ts @@ -14,6 +14,22 @@ export const remoteDataGroupedByA = new Array(10) })); export const remoteApiMock = RequestMock() + .onRequestTo(/\/api\/data\?.*group=.*filter=.*A_11/) + .respond( + { + data: remoteDataGroupedByA.filter((item) => item.key.includes('A_11')), + }, + 200, + { 'access-control-allow-origin': '*' }, + ) + .onRequestTo(/\/api\/data\?.*group=.*filter=.*A_1/) + .respond( + { + data: remoteDataGroupedByA.filter((item) => item.key.includes('A_1')), + }, + 200, + { 'access-control-allow-origin': '*' }, + ) .onRequestTo(/\/api\/data\?.*group=/) .respond( { diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/columns_controller.ts b/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/columns_controller.ts index 551056168201..03826fecae71 100644 --- a/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/columns_controller.ts +++ b/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/columns_controller.ts @@ -10,7 +10,7 @@ import type { OptionWithChanges } from '@ts/grids/new/grid_core/options_controll import { OptionsController } from '../options_controller/options_controller'; import { getTreeNodeByPath } from '../utils/tree/index'; import { updateColumnSettings } from './columns_settings/index'; -import { IGNORE_COLUMN_OPTION_NAMES } from './const'; +import { COLUMNS_OBJ_COMPARE_DEPTH, IGNORE_COLUMN_OPTION_NAMES } from './const'; import type { ColumnProperties, ColumnSettings, PreNormalizedColumn } from './options'; import type { Column, ColumnsConfigurationFromData, VisibleColumn } from './types'; import { @@ -176,7 +176,7 @@ export class ColumnsController { newValue: unknown, prevValue: unknown, ): void { - if (!equalByValue(prevValue, newValue, { maxDepth: 5 })) { + if (!equalByValue(prevValue, newValue, { maxDepth: COLUMNS_OBJ_COMPARE_DEPTH })) { const fullOptionPath = `columns[${columnIndex}].${optionName}`; this.options.notifyColumnOptionChanged(fullOptionPath, newValue, prevValue); } diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/const.ts b/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/const.ts index 862389a7ea76..86fc90517dd0 100644 --- a/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/const.ts +++ b/packages/devextreme/js/__internal/grids/new/grid_core/columns_controller/const.ts @@ -1,3 +1,5 @@ export const IGNORE_COLUMN_OPTION_NAMES = { selector: true, }; + +export const COLUMNS_OBJ_COMPARE_DEPTH = 5; diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/const.ts b/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/const.ts new file mode 100644 index 000000000000..abaaee3340ac --- /dev/null +++ b/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/const.ts @@ -0,0 +1 @@ +export const FILTER_OBJ_COMPARE_DEPTH = 6; diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/data_controller.ts b/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/data_controller.ts index 02875f1389db..b7e590034a65 100644 --- a/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/data_controller.ts +++ b/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/data_controller.ts @@ -17,6 +17,7 @@ import { normalizeFilterWithSelectors } from '../filtering/utils'; import { LifeCycleController } from '../lifecycle/controller'; import { OptionsController } from '../options_controller/options_controller'; import { SortingController } from '../sorting_controller/index'; +import { FILTER_OBJ_COMPARE_DEPTH } from './const'; import { StoreLoadAdapter } from './store_load_adapter/index'; import type { DataObject, Key } from './types'; import { @@ -30,8 +31,6 @@ import { updateItemsImmutable, } from './utils'; -const FILTER_OBJ_COMPARE_DEPTH = 6; - export class DataController { private readonly pendingLocalOperations = {}; diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/deferred_cache.test.ts b/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/deferred_cache.test.ts index e62d0ae4b6c0..3bba6a564bb3 100644 --- a/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/deferred_cache.test.ts +++ b/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/deferred_cache.test.ts @@ -83,6 +83,22 @@ describe('DataController', () => { expect(originFn).toHaveBeenCalledTimes(2); }); + it('should call origin fn if args differ on deep value', async () => { + const firstArgs = { filter: [['text', 'contains', 'tra']] }; + const secondArgs = { filter: [['text', 'contains', 'travel']] }; + const originFn = jest + .fn() + .mockImplementation(() => createDeferred().resolve()); + const decoratedFn = deferredCache( + originFn as (args: (typeof firstArgs | typeof secondArgs)) => DeferredObj, + ); + + await decoratedFn(firstArgs); + await decoratedFn(secondArgs); + + expect(originFn).toHaveBeenCalledTimes(2); + }); + it('should return result from origin fn', async () => { const expectedResult = { a: 'A' }; const originFn = jest diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/deferred_cache.ts b/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/deferred_cache.ts index ab5e867b4559..83993e0e1077 100644 --- a/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/deferred_cache.ts +++ b/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/deferred_cache.ts @@ -2,6 +2,8 @@ import { equalByValue } from '@js/core/utils/common'; import type { DeferredObj } from '@js/core/utils/deferred'; import { Deferred } from '@js/core/utils/deferred'; +import { FILTER_OBJ_COMPARE_DEPTH } from './const'; + // @ts-expect-error bad deferred ctor type // eslint-disable-next-line @typescript-eslint/no-explicit-any const createDeferred = (): any => new Deferred(); @@ -19,7 +21,7 @@ export const deferredCache = ( ): DeferredObj => { const hasPreviousCall = lastArgs !== null && cachedResult !== null; const isArgsSame = hasPreviousCall - ? equalByValue(lastArgs, args) + ? equalByValue(lastArgs, args, { maxDepth: FILTER_OBJ_COMPARE_DEPTH }) : false; if (hasPreviousCall && isArgsSame) {