From 2511e62bf35fd221e889f2ea69429375b29d9f5e Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Fri, 7 Aug 2026 16:57:10 +0600 Subject: [PATCH 01/11] fix --- .../data/data_source/m_data_source.ts | 4 +- .../js/__internal/data/data_source/types.ts | 21 ++++ .../controllers/m_ai_column_controller.ts | 8 +- .../m_columns_controller.ts | 7 +- .../m_columns_controller_utils.ts | 21 ++-- .../grid_core/columns_controller/types.ts | 21 ++++ .../data_controller/data_controller.ts | 107 ++++++++++-------- .../grids/grid_core/data_controller/types.ts | 4 +- .../m_data_source_adapter.ts | 19 ++-- .../grid_core/data_source_adapter/types.ts | 30 +++++ .../m_data_source_adapter.ts | 4 +- 11 files changed, 171 insertions(+), 75 deletions(-) create mode 100644 packages/devextreme/js/__internal/data/data_source/types.ts diff --git a/packages/devextreme/js/__internal/data/data_source/m_data_source.ts b/packages/devextreme/js/__internal/data/data_source/m_data_source.ts index 292ccff274d8..8841c637c786 100644 --- a/packages/devextreme/js/__internal/data/data_source/m_data_source.ts +++ b/packages/devextreme/js/__internal/data/data_source/m_data_source.ts @@ -22,6 +22,8 @@ import { } from '@js/core/utils/type'; import commonUtils from '@ts/core/utils/m_common'; +import type { LoadOperation } from './types'; + export const DataSource = Class.inherit({ ctor(options) { // @ts-expect-error @@ -470,7 +472,7 @@ export const DataSource = Class.inherit({ } }, - _createLoadOperation(deferred) { + _createLoadOperation(deferred): LoadOperation { const operationId = this._operationManager.add(deferred); const storeLoadOptions = this._createStoreLoadOptions(); diff --git a/packages/devextreme/js/__internal/data/data_source/types.ts b/packages/devextreme/js/__internal/data/data_source/types.ts new file mode 100644 index 000000000000..82a6c7acb195 --- /dev/null +++ b/packages/devextreme/js/__internal/data/data_source/types.ts @@ -0,0 +1,21 @@ +import type { DataSourceOptionsStub } from '@js/data/data_source'; + +export interface StoreLoadOptions { + sort?: DataSourceOptionsStub['sort']; + filter?: DataSourceOptionsStub['filter']; + langParams?: DataSourceOptionsStub['langParams']; + select?: DataSourceOptionsStub['select']; + group?: DataSourceOptionsStub['group']; + requireTotalCount?: DataSourceOptionsStub['requireTotalCount']; + searchOperation?: DataSourceOptionsStub['searchOperation']; + searchValue?: DataSourceOptionsStub['searchValue']; + searchExpr?: DataSourceOptionsStub['searchExpr']; + skip?: number; + take?: number; + userData?: unknown; +} + +export interface LoadOperation { + operationId: number; + storeLoadOptions: StoreLoadOptions; +} diff --git a/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_column_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_column_controller.ts index 4b1189fc3819..db320c8b942f 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_column_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_column_controller.ts @@ -5,7 +5,7 @@ import type { Column } from '@ts/grids/grid_core/columns_controller/types'; import type { ColumnsController } from '../../columns_controller/m_columns_controller'; import type { DataController } from '../../data_controller/data_controller'; -import type { HandleDataChangedArguments, UserData } from '../../data_controller/types'; +import type { HandleDataChangedEvent, UserData } from '../../data_controller/types'; import { Controller } from '../../m_modules'; import type { RowKey } from '../../m_types'; import gridCoreUtils from '../../m_utils'; @@ -20,7 +20,7 @@ export class AIColumnController extends Controller { private aiColumnIntegrationController!: AIColumnIntegrationController; - private dataSourceChangedHandler!: (e?: HandleDataChangedArguments) => void; + private dataSourceChangedHandler!: (e?: HandleDataChangedEvent) => void; private storeUpdatedHandler!: (key: RowKey) => void; @@ -188,8 +188,8 @@ export class AIColumnController extends Controller { } } - private handleDataSourceChanged(args?: HandleDataChangedArguments): void { - if (args?.changeType === 'loadError') { + private handleDataSourceChanged(e?: HandleDataChangedEvent): void { + if (e?.changeType === 'loadError') { return; } diff --git a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts index ccc2403cff2f..3fb7d5270680 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts @@ -5,6 +5,7 @@ import { DataSource } from '@js/common/data/data_source/data_source'; import { normalizeDataSourceOptions } from '@js/common/data/data_source/utils'; import config from '@js/core/config'; import $ from '@js/core/renderer'; +import type { Callback } from '@js/core/utils/callbacks'; import Callbacks from '@js/core/utils/callbacks'; import { compileGetter } from '@js/core/utils/data'; import { Deferred, when } from '@js/core/utils/deferred'; @@ -19,7 +20,7 @@ import Store from '@js/data/abstract_store'; import filterUtils from '@js/ui/shared/filtering'; import errors from '@js/ui/widget/ui.errors'; import inflector from '@ts/core/utils/m_inflector'; -import type { Column, FilterField } from '@ts/grids/grid_core/columns_controller/types'; +import type { Column, ColumnsChanges, FilterField } from '@ts/grids/grid_core/columns_controller/types'; import type { DataController } from '@ts/grids/grid_core/data_controller/data_controller'; import type { FocusController } from '@ts/grids/grid_core/focus/m_focus'; import type { StateStoringController } from '@ts/grids/grid_core/state_storing/m_state_storing_core'; @@ -126,11 +127,11 @@ export class ColumnsController extends modules.Controller { private __sortingUpdated: any; - public columnsChanged: any; + public columnsChanged!: Callback<[ColumnsChanges]>; public aiColumnOptionChanged: any; - public _columnChanges: any; + public _columnChanges?: ColumnsChanges; protected _dataController!: DataController; diff --git a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller_utils.ts b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller_utils.ts index 31660d62217c..da98089c0960 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller_utils.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller_utils.ts @@ -33,7 +33,9 @@ import { VIRTUAL_COMMAND_COLUMN_NAME, } from './const'; import type { ColumnsController } from './m_columns_controller'; -import type { Column, ColumnIndex, DropLocationNames } from './types'; +import type { + Column, ColumnIndex, ColumnsChanges, DropLocationNames, +} from './types'; const warnFixedInChildColumnsOnce = (controller: ColumnsController, childColumns: any[]): void => { if (controller?._isWarnedAboutUnsupportedProperties) return; @@ -610,16 +612,19 @@ export function assignColumns(that, columns) { that.updateColumnDataTypes(); } -export const updateColumnChanges = function (that: ColumnsController, changeType, optionName?, columnIndex?) { - const columnChanges = that._columnChanges || { +export const updateColumnChanges = ( + that: ColumnsController, + changeType: Exclude, + optionName?: string, + columnIndex?: number, +): void => { + const columnChanges: ColumnsChanges = that._columnChanges ?? { optionNames: { length: 0 }, changeTypes: { length: 0 }, columnIndex, // TODO replace columnIndex -> columnIndices }; - optionName = optionName || 'all'; - - optionName = optionName.split('.')[0]; + const normalizedOptionName = (optionName ?? 'all').split('.')[0] as keyof Column | 'all'; const { changeTypes } = columnChanges; @@ -630,8 +635,8 @@ export const updateColumnChanges = function (that: ColumnsController, changeType const { optionNames } = columnChanges; - if (optionName && !optionNames[optionName]) { - optionNames[optionName] = true; + if (normalizedOptionName && !optionNames[normalizedOptionName]) { + optionNames[normalizedOptionName] = true; optionNames.length++; } if (columnIndex === undefined || columnIndex !== columnChanges.columnIndex) { diff --git a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/types.ts b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/types.ts index 4e6e85cd0652..7307a9f03be5 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/types.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/types.ts @@ -26,3 +26,24 @@ export interface Column extends ColumnBase { rowspan?: number; colspan?: number; } + +export interface ColumnsChanges { + changeTypes: { + sorting?: boolean; + grouping?: boolean; + groupExpanding?: boolean; + columns?: boolean; + filtering?: boolean; + event?: unknown; + virtualColumnsScrolling?: boolean; + length: number; + }; + optionNames: { + [name in keyof Column]?: boolean; + } & { + all?: boolean; + length: number; + }; + columnIndex?: number; + columnIndices?: number[]; +} diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts index ce921e7cdadd..1ff92ba6916a 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts @@ -39,7 +39,8 @@ import type { SelectionController } from '@ts/grids/grid_core/selection/m_select import type { StateStoringController } from '@ts/grids/grid_core/state_storing/m_state_storing_core'; import type { ValidatingController } from '@ts/grids/grid_core/validating/m_validating'; -import type { OperationTypes } from '../data_source_adapter/types'; +import type { ColumnsChanges } from '../columns_controller/types'; +import type { LoadOperation, OperationTypes } from '../data_source_adapter/types'; import modules from '../m_modules'; import type { Controllers, Module, OptionChanged, RowKey, @@ -52,7 +53,7 @@ import type { DataChange, DataSourceAdapterLike, Filter, - HandleDataChangedArguments, + HandleDataChangedEvent, Item, PagingChanges, PagingDataSource, @@ -148,19 +149,15 @@ export class DataController extends DataHelperMixin(modules.Controller) { protected _validatingController!: ValidatingController; - private _columnsChangedHandler!: (e: unknown) => void; - private _loadingChangedHandler!: (isLoading: boolean) => void; private _loadErrorHandler!: (e: unknown) => void; - private _customizeStoreLoadOptionsHandler!: (e: unknown) => void; - private _changingHandler!: (e: unknown) => void; private _dataPushedHandler!: (changes: unknown) => void; - private _dataChangedHandler!: (e: HandleDataChangedArguments) => void; + private _dataChangedHandler!: (e: HandleDataChangedEvent) => void; public init(): void { this._items = []; @@ -182,14 +179,12 @@ export class DataController extends DataHelperMixin(modules.Controller) { this._isPaging = false; this._currentOperationTypes = null; this._dataChangedHandler = this._handleDataChanged.bind(this); - this._columnsChangedHandler = this._handleColumnsChanged.bind(this); this._loadingChangedHandler = this._handleLoadingChanged.bind(this); this._loadErrorHandler = this._handleLoadError.bind(this); - this._customizeStoreLoadOptionsHandler = this._handleCustomizeStoreLoadOptions.bind(this); this._changingHandler = this._handleChanging.bind(this); this._dataPushedHandler = this._handleDataPushed.bind(this); - this._columnsController.columnsChanged.add(this._columnsChangedHandler); + this._columnsController.columnsChanged.add(this._columnsChangedHandler.bind(this)); this._isLoading = false; this._isCustomLoading = false; @@ -358,18 +353,19 @@ export class DataController extends DataHelperMixin(modules.Controller) { return adapter ? adapter._dataSource : null; } - public getCombinedFilter(returnDataField?) { + public getCombinedFilter(returnDataField?: boolean): Filter { return this.combinedFilter(undefined, returnDataField); } - private combinedFilter(filter, returnDataField?) { + private combinedFilter(filter: Filter, returnDataField?: boolean): Filter { if (!this._dataSource) { return filter; } - let combined = filter ?? this._dataSource.filter(); + let combined: Filter = filter ?? this._dataSource.filter(); - const isColumnsTypesDefined = this._columnsController.isDataSourceApplied() || this._columnsController.isAllDataTypesDefined(); + const isColumnsTypesDefined = this._columnsController.isDataSourceApplied() + || this._columnsController.isAllDataTypesDefined(); if (isColumnsTypesDefined) { const additionalFilter = this._calculateAdditionalFilter(); @@ -386,11 +382,11 @@ export class DataController extends DataHelperMixin(modules.Controller) { return combined; } - public waitReady() { + public waitReady(): DeferredObj { if (this._updateLockCount) { // @ts-expect-error this._readyDeferred = new Deferred(); - return this._readyDeferred; + return this._readyDeferred as DeferredObj; } return when(); } @@ -399,7 +395,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { * @extended: selection * @protected */ - protected _endUpdateCore() { + protected _endUpdateCore(): void { const changes = this._changes; if (changes.length) { @@ -415,7 +411,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { } // Handlers - private _handleCustomizeStoreLoadOptions(e) { + private readonly _customizeStoreLoadOptionsHandler = (e: LoadOperation): void => { const columnsController = this._columnsController; const dataSource = this._dataSource; const { storeLoadOptions } = e; @@ -426,9 +422,11 @@ export class DataController extends DataHelperMixin(modules.Controller) { storeLoadOptions.filter = this.combinedFilter(storeLoadOptions.filter); - if (storeLoadOptions.filter?.length === 1 && storeLoadOptions.filter[0] === '!') { + if (Array.isArray(storeLoadOptions.filter) + && storeLoadOptions.filter.length === 1 + && storeLoadOptions.filter[0] === '!') { e.data = []; - e.extra = e.extra || {}; + e.extra = e.extra ?? {}; e.extra.totalCount = 0; } @@ -444,21 +442,20 @@ export class DataController extends DataHelperMixin(modules.Controller) { dataSource.sort(storeLoadOptions.sort); dataSource.group(storeLoadOptions.group); - storeLoadOptions.sort = columnsController.getSortDataSourceParameters(!dataSource.remoteOperations().sorting); + storeLoadOptions.sort = columnsController + .getSortDataSourceParameters(!dataSource.remoteOperations().sorting); - e.group = columnsController.getGroupDataSourceParameters(!dataSource.remoteOperations().grouping); - } + e.group = columnsController + .getGroupDataSourceParameters(!dataSource.remoteOperations().grouping); + }; - private _handleColumnsChanged(e) { + private _columnsChangedHandler(e: ColumnsChanges): void { const that = this; - const { changeTypes } = e; - const { optionNames } = e; - let filterValue; - let filterValues; - let filterApplied; + const { changeTypes, optionNames } = e; + let filterApplied = false; // B255430 - const updateItemsHandler = function (change) { + const updateItemsHandler = (change: ColumnsChanges): void => { that._columnsController.columnsChanged.remove(updateItemsHandler); that.updateItems({ @@ -475,11 +472,22 @@ export class DataController extends DataHelperMixin(modules.Controller) { that.reload(); } } else if (changeTypes.columns) { - filterValues = that._columnsController.columnOption(e.columnIndex, 'filterValues'); - if (optionNames.filterValues || optionNames.filterType && Array.isArray(filterValues) || optionNames.filterValue || optionNames.selectedFilterOperation || optionNames.allowFiltering) { - filterValue = that._columnsController.columnOption(e.columnIndex, 'filterValue'); - - if (Array.isArray(filterValues) || e.columnIndex === undefined || isDefined(filterValue) || !optionNames.selectedFilterOperation || optionNames.filterValue) { + const filterValues = that._columnsController.columnOption(e.columnIndex, 'filterValues'); + + if (optionNames.filterValues + || (optionNames.filterType && Array.isArray(filterValues)) + || optionNames.filterValue + || optionNames.selectedFilterOperation + || optionNames.allowFiltering + ) { + const filterValue = that._columnsController.columnOption(e.columnIndex, 'filterValue'); + + if (Array.isArray(filterValues) + || e.columnIndex === undefined + || isDefined(filterValue) + || !optionNames.selectedFilterOperation + || optionNames.filterValue + ) { that._applyFilter(); filterApplied = true; } @@ -494,6 +502,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { 'filterValues', 'filterType', ]; + if (!that._needApplyFilter && !gridCoreUtils.checkChanges(optionNames, excludedOptionNames)) { // TODO remove resubscribing that._columnsController.columnsChanged.add(updateItemsHandler); @@ -501,12 +510,15 @@ export class DataController extends DataHelperMixin(modules.Controller) { if (isDefined(optionNames.visible)) { const column = that._columnsController.columnOption(e.columnIndex); - if (column && (isDefined(column.filterValue) || isDefined(column.filterValues))) { + const hasFilterValue = isDefined(column?.filterValue) || isDefined(column?.filterValues); + + if (hasFilterValue) { that._applyFilter(); filterApplied = true; } } } + if (!filterApplied && changeTypes.filtering && !this._needApplyFilter) { that.reload(); } @@ -515,7 +527,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { /** * @extended: selection */ - protected _handleDataChanged(e: HandleDataChangedArguments) { + protected _handleDataChanged(e: HandleDataChangedEvent): void { const that = this; const dataSource = that._dataSource; const columnsController = that._columnsController; @@ -537,12 +549,12 @@ export class DataController extends DataHelperMixin(modules.Controller) { that._isDataSourceApplying = false; - const hasAdditionalFilter = () => { + const hasAdditionalFilter = (): boolean => { const additionalFilter = that._calculateAdditionalFilter(); - return additionalFilter?.length; + return Boolean(additionalFilter?.length); }; - const needApplyFilter = that._needApplyFilter; + const needApplyFilter = that._needApplyFilter; that._needApplyFilter = false; if (needApplyFilter && !that._isAllDataTypesDefined && hasAdditionalFilter()) { @@ -615,7 +627,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { return dataSource; } - protected _initDataSource() { + protected _initDataSource(): void { const hadDataSource = !!this._dataSource; super._initDataSource(); @@ -639,23 +651,26 @@ export class DataController extends DataHelperMixin(modules.Controller) { /** * @extended: selection, virtual_scrolling */ - protected _loadDataSource() { + // The mixin base types this as `void`, but the override returns a Deferred + // eslint-disable-next-line @typescript-eslint/no-misused-promises + protected _loadDataSource(): DeferredObj { const that = this; const dataSource = that._dataSource; - // @ts-expect-error - const result = new Deferred(); + // @ts-expect-error Deferred lacks a construct signature in its typings + const result: DeferredObj = new Deferred(); when(this._columnsController.refresh(true)).always(() => { if (dataSource) { - dataSource.load().done(function () { + dataSource.load().done((...args: unknown[]) => { that._isPaging = false; - result.resolve.apply(result, arguments); + result.resolve(...args); }).fail(result.reject); } else { result.resolve(); } }); + // @ts-expect-error promise() is typed as Promise but returns a Deferred-like value at runtime return result.promise(); } diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts index 638e0c3e0c78..f85bc2268b1d 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts @@ -58,7 +58,7 @@ export type FilterExpression = ((data: UserData) => boolean) | unknown[]; export type Filter = FilterExpression | null | undefined; -export interface HandleDataChangedArguments { +export interface HandleDataChangedEvent { changeType?: 'refresh' | 'update' | 'loadError'; isDelayed?: boolean; isLiveUpdate?: boolean; @@ -97,7 +97,7 @@ interface UpdateChange extends DataChangeBase { export type DataChange = | UpdateChange | SelectionChange | FocusedRowChange - | (DataChangeBase & HandleDataChangedArguments) + | (DataChangeBase & HandleDataChangedEvent) | (DataChangeBase & { changeType: 'pageIndex' }) | (DataChangeBase & { changeType?: 'refresh', isLiveUpdate: boolean; isOptionChanged: boolean }) | (DataChangeBase & { changeType?: 'refresh', event: unknown; virtualColumnsScrolling: boolean }) diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts index 7c2dce5d54df..d7a452d8305a 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-dynamic-delete */ import ArrayStore from '@js/common/data/array_store'; import { applyBatch } from '@js/common/data/array_utils'; +import type { Callback } from '@js/core/utils/callbacks'; import Callbacks from '@js/core/utils/callbacks'; import { getKeyHash } from '@js/core/utils/common'; import { Deferred, when } from '@js/core/utils/deferred'; @@ -18,7 +19,7 @@ import { getPageDataFromCache, setPageDataToCache, } from './m_data_source_adapter_utils'; -import type { OperationTypes } from './types'; +import type { LoadOperation, OperationTypes } from './types'; export default class DataSourceAdapter extends modules.Controller { protected _dataSource: any; @@ -71,7 +72,7 @@ export default class DataSourceAdapter extends modules.Controller { private loadError: any; - private customizeStoreLoadOptions: any; + private customizeStoreLoadOptions!: Callback<[LoadOperation]>; private changing: any; @@ -79,7 +80,7 @@ export default class DataSourceAdapter extends modules.Controller { private _dataChangedHandler!: (e: any) => any; - private _customizeStoreLoadOptionsHandler!: (e: any) => any; + private _customizeStoreLoadOptionsHandlerProxy!: (e: LoadOperation) => void; private _dataLoadedHandler!: (e: any) => any; @@ -118,7 +119,7 @@ export default class DataSourceAdapter extends modules.Controller { that.pushed = Callbacks(); that._dataChangedHandler = that._handleDataChanged.bind(that); - that._customizeStoreLoadOptionsHandler = that._handleCustomizeStoreLoadOptions.bind(that); + that._customizeStoreLoadOptionsHandlerProxy = that._customizeStoreLoadOptionsHandler.bind(that); that._dataLoadedHandler = that._handleDataLoaded.bind(that); that._loadingChangedHandler = that._handleLoadingChanged.bind(that); that._loadErrorHandler = that._handleLoadError.bind(that); @@ -126,7 +127,7 @@ export default class DataSourceAdapter extends modules.Controller { that._changingHandler = that._handleChanging.bind(that); dataSource.on('changed', that._dataChangedHandler); - dataSource.on('customizeStoreLoadOptions', that._customizeStoreLoadOptionsHandler); + dataSource.on('customizeStoreLoadOptions', that._customizeStoreLoadOptionsHandlerProxy); dataSource.on('customizeLoadResult', that._dataLoadedHandler); dataSource.on('loadingChanged', that._loadingChangedHandler); dataSource.on('loadError', that._loadErrorHandler); @@ -148,7 +149,7 @@ export default class DataSourceAdapter extends modules.Controller { const store = dataSource.store(); dataSource.off('changed', that._dataChangedHandler); - dataSource.off('customizeStoreLoadOptions', that._customizeStoreLoadOptionsHandler); + dataSource.off('customizeStoreLoadOptions', that._customizeStoreLoadOptionsHandlerProxy); dataSource.off('customizeLoadResult', that._dataLoadedHandler); dataSource.off('loadingChanged', that._loadingChangedHandler); dataSource.off('loadError', that._loadErrorHandler); @@ -401,7 +402,7 @@ export default class DataSourceAdapter extends modules.Controller { } } - protected _handleCustomizeStoreLoadOptions(options) { + protected _customizeStoreLoadOptionsHandler(options: LoadOperation): void { this._handleDataLoading(options); if (!(options.data?.length === 0)) { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing @@ -412,7 +413,7 @@ export default class DataSourceAdapter extends modules.Controller { /** * @extended: virtual_scrolling */ - protected _handleDataLoading(options) { + protected _handleDataLoading(options: LoadOperation): void { const dataSource = this._dataSource; const lastLoadOptions = this._lastLoadOptions; @@ -837,7 +838,7 @@ export default class DataSourceAdapter extends modules.Controller { that._scheduleCustomLoadCallbacks(d); dataSource._scheduleLoadCallbacks(d); - that._handleCustomizeStoreLoadOptions(loadResult); + that._customizeStoreLoadOptionsHandler(loadResult); executeTask(() => { if (!dataSource.store()) { return d.reject('canceled'); diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/types.ts b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/types.ts index 87420f2a54ec..91aa29734a01 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/types.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/types.ts @@ -1,3 +1,11 @@ +import type { Mode } from '@js/common'; +import type { Properties } from '@js/ui/data_grid'; +import type { LoadOperation as BaseLoadOperation, StoreLoadOptions } from '@ts/data/data_source/types'; + +export type RemoteOperations = Properties['remoteOperations']; + +export type RemoteOperationsOptions = Exclude; + export interface OperationTypes { reload?: boolean; fullReload?: boolean; @@ -11,3 +19,25 @@ export interface OperationTypes { pageSize?: boolean; paging?: boolean; } + +export interface LoadOperation extends BaseLoadOperation { + data?: unknown[]; + cachedStoreData?: unknown[]; + delay?: number; + storeLoadOptions: StoreLoadOptions & { + isLoadingAll?: boolean; + }; + originalStoreLoadOptions?: StoreLoadOptions; + remoteOperations?: RemoteOperations; + isCustomLoading?: boolean; + pageIndex?: number; + lastLoadOptions?: StoreLoadOptions & { + pageIndex: number; + pageSize: number; + }; + operationTypes?: OperationTypes; + group?: unknown[] | null; + extra?: { + totalCount?: number; + } +} diff --git a/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.ts b/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.ts index 5de5940edafa..b98d5b7fe7d1 100644 --- a/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.ts +++ b/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.ts @@ -328,7 +328,7 @@ export class DataSourceAdapterTreeList extends DataSourceAdapter { /** * @extended: TreeLists's data_source_adapter */ - protected _handleCustomizeStoreLoadOptions(options) { + protected _customizeStoreLoadOptionsHandler(options): void { const rootValue: any = this.option('rootValue'); const parentIdExpr = this.option('parentIdExpr'); let { parentIds } = options.storeLoadOptions; @@ -337,7 +337,7 @@ export class DataSourceAdapterTreeList extends DataSourceAdapter { options.isCustomLoading = false; } - super._handleCustomizeStoreLoadOptions.apply(this, arguments as any); + super._customizeStoreLoadOptionsHandler.apply(this, arguments as any); if (options.remoteOperations.filtering && !options.isCustomLoading) { if (isFullBranchFilterMode(this) && options.cachedStoreData || !options.storeLoadOptions.filter) { From 9976b5b5d69c7531ae80ecfbbcad89784516bff1 Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Fri, 7 Aug 2026 17:14:03 +0600 Subject: [PATCH 02/11] apply copilot's review --- .../devextreme/js/__internal/data/data_source/m_data_source.ts | 2 +- packages/devextreme/js/__internal/data/data_source/types.ts | 1 + .../js/__internal/grids/grid_core/data_source_adapter/types.ts | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/devextreme/js/__internal/data/data_source/m_data_source.ts b/packages/devextreme/js/__internal/data/data_source/m_data_source.ts index 8841c637c786..c27f6597d819 100644 --- a/packages/devextreme/js/__internal/data/data_source/m_data_source.ts +++ b/packages/devextreme/js/__internal/data/data_source/m_data_source.ts @@ -414,7 +414,7 @@ export const DataSource = Class.inherit({ this._scheduleFailCallbacks(d); this._scheduleChangedCallbacks(d); - const loadOperation = this._createLoadOperation(d); + const loadOperation: LoadOperation = this._createLoadOperation(d); this._eventsStrategy.fireEvent('customizeStoreLoadOptions', [loadOperation]); diff --git a/packages/devextreme/js/__internal/data/data_source/types.ts b/packages/devextreme/js/__internal/data/data_source/types.ts index 82a6c7acb195..c707b757ce36 100644 --- a/packages/devextreme/js/__internal/data/data_source/types.ts +++ b/packages/devextreme/js/__internal/data/data_source/types.ts @@ -18,4 +18,5 @@ export interface StoreLoadOptions { export interface LoadOperation { operationId: number; storeLoadOptions: StoreLoadOptions; + delay?: number; } diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/types.ts b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/types.ts index 91aa29734a01..453cfd0cb007 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/types.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/types.ts @@ -23,7 +23,6 @@ export interface OperationTypes { export interface LoadOperation extends BaseLoadOperation { data?: unknown[]; cachedStoreData?: unknown[]; - delay?: number; storeLoadOptions: StoreLoadOptions & { isLoadingAll?: boolean; }; From bdc0695617f456dbab629dc45518ac8d3264d87b Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Fri, 7 Aug 2026 17:22:03 +0600 Subject: [PATCH 03/11] fix qunit --- .../dataController.tests.js | 2 +- .../dataSource.tests.js | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataController.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataController.tests.js index a7a0e48d9432..a6d12714c8b5 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataController.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataController.tests.js @@ -8066,7 +8066,7 @@ QUnit.module('Filtering', { isApplyFilterCalled = true; }; - this.dataController._handleColumnsChanged({ + this.dataController._columnsChangedHandler({ changeTypes: { columns: 'test' }, diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataSource.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataSource.tests.js index 2514af9584d4..213c5d84dfc5 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataSource.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataSource.tests.js @@ -7476,17 +7476,17 @@ QUnit.module('New virtual scrolling mode', { const dataSource = this.createDataSource({ pageSize: 3 }); - const dataLoadingHandler = dataSource._customizeStoreLoadOptionsHandler; + const originalDataLoadingHandler = dataSource._customizeStoreLoadOptionsHandlerProxy; const takeValues = []; const skipValues = []; - dataSource._customizeStoreLoadOptionsHandler = function(options) { - dataLoadingHandler.apply(dataSource, arguments); + dataSource._customizeStoreLoadOptionsHandlerProxy = function(options) { + originalDataLoadingHandler.apply(dataSource, arguments); skipValues.push(options.storeLoadOptions.skip); takeValues.push(options.storeLoadOptions.take); }; - dataSource._dataSource.off('customizeStoreLoadOptions', dataLoadingHandler); - dataSource._dataSource.on('customizeStoreLoadOptions', dataSource._customizeStoreLoadOptionsHandler); + dataSource._dataSource.off('customizeStoreLoadOptions', originalDataLoadingHandler); + dataSource._dataSource.on('customizeStoreLoadOptions', dataSource._customizeStoreLoadOptionsHandlerProxy); try { // act @@ -7508,7 +7508,7 @@ QUnit.module('New virtual scrolling mode', { assert.deepEqual(dataSource.items(), TEN_NUMBERS.slice(0, 9), 'second load items'); } finally { dataSource._dataSource.off('customizeStoreLoadOptions', dataSource._dataLoadingHandler); - dataSource._dataSource.on('customizeStoreLoadOptions', dataLoadingHandler); + dataSource._dataSource.on('customizeStoreLoadOptions', originalDataLoadingHandler); } }); From 44ef74417da17f4a373fe190d296a861d7743510 Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Fri, 7 Aug 2026 17:26:01 +0600 Subject: [PATCH 04/11] remove duplicated type --- .../js/__internal/grids/grid_core/data_controller/types.ts | 6 ------ .../grid_core/data_controller/utils/remoteOperations.ts | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts index f85bc2268b1d..f47a55278226 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts @@ -1,7 +1,5 @@ -import type { Mode } from '@js/common'; import type { DataSource } from '@js/common/data'; import type { DeferredObj } from '@js/core/utils/deferred'; -import type { Properties } from '@js/ui/data_grid'; import type { OperationTypes } from '../data_source_adapter/types'; @@ -110,7 +108,3 @@ export type PagingResult = number | DeferredObj | Promise; export interface CallbackFlags { stopOnFalse: boolean; } - -export type RemoteOperations = Properties['remoteOperations']; - -export type RemoteOperationsOptions = Exclude; diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/utils/remoteOperations.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/utils/remoteOperations.ts index 0b2b43dd4d09..0f9004a0bcfb 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/utils/remoteOperations.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/utils/remoteOperations.ts @@ -1,7 +1,7 @@ import { isObject } from '@js/core/utils/type'; import type { Store } from '@js/data'; -import type { RemoteOperations, RemoteOperationsOptions } from '../types'; +import type { RemoteOperations, RemoteOperationsOptions } from '../../data_source_adapter/types'; import { isCustomStore, isLocalStore } from './store'; export function normalizeRemoteOperations( From aecfd476432377d2336604a31f8be1f72b88aeca Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Fri, 7 Aug 2026 19:12:50 +0600 Subject: [PATCH 05/11] apply minor comment --- .../js/__internal/data/data_source/types.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/devextreme/js/__internal/data/data_source/types.ts b/packages/devextreme/js/__internal/data/data_source/types.ts index c707b757ce36..0ae4a0f324d8 100644 --- a/packages/devextreme/js/__internal/data/data_source/types.ts +++ b/packages/devextreme/js/__internal/data/data_source/types.ts @@ -1,15 +1,10 @@ import type { DataSourceOptionsStub } from '@js/data/data_source'; -export interface StoreLoadOptions { - sort?: DataSourceOptionsStub['sort']; - filter?: DataSourceOptionsStub['filter']; - langParams?: DataSourceOptionsStub['langParams']; - select?: DataSourceOptionsStub['select']; - group?: DataSourceOptionsStub['group']; - requireTotalCount?: DataSourceOptionsStub['requireTotalCount']; - searchOperation?: DataSourceOptionsStub['searchOperation']; - searchValue?: DataSourceOptionsStub['searchValue']; - searchExpr?: DataSourceOptionsStub['searchExpr']; +export interface StoreLoadOptions extends Pick< + DataSourceOptionsStub, + 'sort' | 'filter' | 'langParams' | 'select' | 'group' + | 'requireTotalCount' | 'searchOperation' | 'searchValue' | 'searchExpr' +> { skip?: number; take?: number; userData?: unknown; From 2e0588cff5bc5a748c34658e79de5868dbda130f Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Fri, 7 Aug 2026 20:56:43 +0600 Subject: [PATCH 06/11] improve DataChange type --- .../data/data_source/m_data_source.ts | 10 ++-- .../js/__internal/data/data_source/types.ts | 7 +++ .../controllers/m_ai_column_controller.ts | 8 +-- .../data_controller/data_controller.ts | 50 ++++++++++++++----- .../grids/grid_core/data_controller/types.ts | 18 +++---- .../m_data_source_adapter.ts | 21 ++++---- .../grid_core/data_source_adapter/types.ts | 13 ++++- .../grids/grid_core/editing/m_editing.ts | 2 + .../grids/grid_core/selection/m_selection.ts | 5 +- .../virtual_scrolling/m_virtual_scrolling.ts | 9 ++-- .../grids/tree_list/selection/m_selection.ts | 7 +-- 11 files changed, 99 insertions(+), 51 deletions(-) diff --git a/packages/devextreme/js/__internal/data/data_source/m_data_source.ts b/packages/devextreme/js/__internal/data/data_source/m_data_source.ts index c27f6597d819..ce32e09e5f8d 100644 --- a/packages/devextreme/js/__internal/data/data_source/m_data_source.ts +++ b/packages/devextreme/js/__internal/data/data_source/m_data_source.ts @@ -22,7 +22,7 @@ import { } from '@js/core/utils/type'; import commonUtils from '@ts/core/utils/m_common'; -import type { LoadOperation } from './types'; +import type { ChangedEvent, DataChange, LoadOperation } from './types'; export const DataSource = Class.inherit({ ctor(options) { @@ -334,9 +334,9 @@ export const DataSource = Class.inherit({ }); }, - _fireChanged(args) { + _fireChanged(e?: ChangedEvent) { const date = new Date(); - this._eventsStrategy.fireEvent('changed', args); + this._eventsStrategy.fireEvent('changed', [e]); // @ts-expect-error this._changedTime = new Date() - date; }, @@ -432,7 +432,7 @@ export const DataSource = Class.inherit({ }); }, - _onPush(changes) { + _onPush(changes: DataChange[]) { if (this._reshapeOnPush) { this.load(); } else { @@ -468,7 +468,7 @@ export const DataSource = Class.inherit({ groupCount: groupLevel, useInsertIndex: true, }); - this._fireChanged([{ changes }]); + this._fireChanged({ changes }); } }, diff --git a/packages/devextreme/js/__internal/data/data_source/types.ts b/packages/devextreme/js/__internal/data/data_source/types.ts index 0ae4a0f324d8..5625a4c76c6d 100644 --- a/packages/devextreme/js/__internal/data/data_source/types.ts +++ b/packages/devextreme/js/__internal/data/data_source/types.ts @@ -1,4 +1,5 @@ import type { DataSourceOptionsStub } from '@js/data/data_source'; +import type { StoreChange } from '@js/data/store'; export interface StoreLoadOptions extends Pick< DataSourceOptionsStub, @@ -15,3 +16,9 @@ export interface LoadOperation { storeLoadOptions: StoreLoadOptions; delay?: number; } + +export type DataChange = StoreChange; + +export interface ChangedEvent { + changes?: DataChange[]; +} diff --git a/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_column_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_column_controller.ts index db320c8b942f..e2639bc78e49 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_column_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_column_controller.ts @@ -5,7 +5,8 @@ import type { Column } from '@ts/grids/grid_core/columns_controller/types'; import type { ColumnsController } from '../../columns_controller/m_columns_controller'; import type { DataController } from '../../data_controller/data_controller'; -import type { HandleDataChangedEvent, UserData } from '../../data_controller/types'; +import type { UserData } from '../../data_controller/types'; +import type { ChangedEvent } from '../../data_source_adapter/types'; import { Controller } from '../../m_modules'; import type { RowKey } from '../../m_types'; import gridCoreUtils from '../../m_utils'; @@ -20,7 +21,7 @@ export class AIColumnController extends Controller { private aiColumnIntegrationController!: AIColumnIntegrationController; - private dataSourceChangedHandler!: (e?: HandleDataChangedEvent) => void; + private dataSourceChangedHandler!: (e?: ChangedEvent) => void; private storeUpdatedHandler!: (key: RowKey) => void; @@ -150,6 +151,7 @@ export class AIColumnController extends Controller { private updateAICells(): void { this.dataController.updateItems({ + changeType: 'refresh', repaintChangesOnly: this.option('repaintChangesOnly'), }); } @@ -188,7 +190,7 @@ export class AIColumnController extends Controller { } } - private handleDataSourceChanged(e?: HandleDataChangedEvent): void { + private handleDataSourceChanged(e?: ChangedEvent): void { if (e?.changeType === 'loadError') { return; } diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts index 1ff92ba6916a..b9a6df71bb92 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts @@ -40,7 +40,7 @@ import type { StateStoringController } from '@ts/grids/grid_core/state_storing/m import type { ValidatingController } from '@ts/grids/grid_core/validating/m_validating'; import type { ColumnsChanges } from '../columns_controller/types'; -import type { LoadOperation, OperationTypes } from '../data_source_adapter/types'; +import type { ChangedEvent, LoadOperation, OperationTypes } from '../data_source_adapter/types'; import modules from '../m_modules'; import type { Controllers, Module, OptionChanged, RowKey, @@ -53,7 +53,6 @@ import type { DataChange, DataSourceAdapterLike, Filter, - HandleDataChangedEvent, Item, PagingChanges, PagingDataSource, @@ -157,7 +156,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { private _dataPushedHandler!: (changes: unknown) => void; - private _dataChangedHandler!: (e: HandleDataChangedEvent) => void; + private _dataChangedHandlerProxy!: (e: ChangedEvent) => void; public init(): void { this._items = []; @@ -178,7 +177,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { this._isPaging = false; this._currentOperationTypes = null; - this._dataChangedHandler = this._handleDataChanged.bind(this); + this._dataChangedHandlerProxy = this._dataChangedHandler.bind(this); this._loadingChangedHandler = this._handleLoadingChanged.bind(this); this._loadErrorHandler = this._handleLoadError.bind(this); this._changingHandler = this._handleChanging.bind(this); @@ -401,7 +400,11 @@ export class DataController extends DataHelperMixin(modules.Controller) { if (changes.length) { this._changes = []; const repaintChangesOnly = changes.every((change) => change.repaintChangesOnly); - this.updateItems(changes.length === 1 ? changes[0] : { repaintChangesOnly }); + const change: DataChange = changes.length === 1 + ? changes[0] + : { changeType: 'refresh', repaintChangesOnly }; + + this.updateItems(change); } if (this._readyDeferred) { @@ -459,6 +462,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { that._columnsController.columnsChanged.remove(updateItemsHandler); that.updateItems({ + changeType: 'refresh', repaintChangesOnly: false, event: change?.changeTypes?.event, virtualColumnsScrolling: change?.changeTypes?.virtualColumnsScrolling, @@ -527,7 +531,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { /** * @extended: selection */ - protected _handleDataChanged(e: HandleDataChangedEvent): void { + protected _dataChangedHandler(e?: ChangedEvent): void { const that = this; const dataSource = that._dataSource; const columnsController = that._columnsController; @@ -543,7 +547,9 @@ export class DataController extends DataHelperMixin(modules.Controller) { that._handleLoadingChanged(false); } + // @ts-expect-error e.isDelayed is set for virtual scrolling with scrolling.legacyMode if (isAsyncDataSourceApplying && e?.isDelayed) { + // @ts-expect-error e.isDelayed is set for virtual scrolling with scrolling.legacyMode e.isDelayed = false; } @@ -562,7 +568,16 @@ export class DataController extends DataHelperMixin(modules.Controller) { that._applyFilter(); } else { this._currentOperationTypes = dataSource.operationTypes(); - that.updateItems(e, true); + + const change: DataChange = e?.changeType === 'loadError' + ? e + : { + // use e?.changeType, because it is passed from virtual scrolling with scrolling.legacyMode + changeType: e?.changeType ?? 'refresh', + changes: e?.changes, + }; + + that.updateItems(change, true); } }).fail(() => { that._isDataSourceApplying = false; @@ -1114,7 +1129,6 @@ export class DataController extends DataHelperMixin(modules.Controller) { let items; const dataSource = this._dataSource; - change.changeType ??= 'refresh'; change.operationTypes ??= this._currentOperationTypes; this._currentOperationTypes = null; @@ -1180,7 +1194,10 @@ export class DataController extends DataHelperMixin(modules.Controller) { } } - public updateItems(change: DataChange = {}, isDataChanged?: boolean) { + public updateItems( + change: DataChange = { changeType: 'refresh' }, + isDataChanged?: boolean, + ) { change.isFirstRender = !this.changed.fired(); if (this._repaintChangesOnly !== undefined) { @@ -1383,7 +1400,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { if (!dataSource && oldDataSource) { oldDataSource.cancelAll(); - oldDataSource.changed.remove(that._dataChangedHandler); + oldDataSource.changed.remove(that._dataChangedHandlerProxy); oldDataSource.loadingChanged.remove(that._loadingChangedHandler); oldDataSource.loadError.remove(that._loadErrorHandler); oldDataSource.customizeStoreLoadOptions.remove(that._customizeStoreLoadOptionsHandler); @@ -1403,7 +1420,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { that._isLoading = !dataSource.isLoaded(); that._needApplyFilter = true; that._isAllDataTypesDefined = that._columnsController.isAllDataTypesDefined(); - dataSource.changed.add(that._dataChangedHandler); + dataSource.changed.add(that._dataChangedHandlerProxy); dataSource.loadingChanged.add(that._loadingChangedHandler); dataSource.loadError.add(that._loadErrorHandler); dataSource.customizeStoreLoadOptions.add(that._customizeStoreLoadOptionsHandler); @@ -1659,7 +1676,10 @@ export class DataController extends DataHelperMixin(modules.Controller) { that._repaintChangesOnly = undefined; }).done(d.resolve).fail(d.reject); } else { - that.updateItems({ repaintChangesOnly: options.changesOnly }); + that.updateItems({ + changeType: 'refresh', + repaintChangesOnly: options.changesOnly, + }); d.resolve(); } }); @@ -1690,7 +1710,11 @@ export class DataController extends DataHelperMixin(modules.Controller) { rowIndexes = Array.isArray(rowIndexes) ? rowIndexes : [rowIndexes]; if (rowIndexes.length > 1 || isDefined(rowIndexes[0])) { - this.updateItems({ changeType: 'update', rowIndices: rowIndexes, isFullUpdate: !changesOnly }); + this.updateItems({ + changeType: 'update', + rowIndices: rowIndexes, + isFullUpdate: !changesOnly, + }); } } diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts index f47a55278226..d27b50eeac58 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts @@ -1,7 +1,7 @@ import type { DataSource } from '@js/common/data'; import type { DeferredObj } from '@js/core/utils/deferred'; -import type { OperationTypes } from '../data_source_adapter/types'; +import type { ChangedEvent, OperationTypes } from '../data_source_adapter/types'; export interface SyncPagingOptions { paginate?: boolean; @@ -56,13 +56,6 @@ export type FilterExpression = ((data: UserData) => boolean) | unknown[]; export type Filter = FilterExpression | null | undefined; -export interface HandleDataChangedEvent { - changeType?: 'refresh' | 'update' | 'loadError'; - isDelayed?: boolean; - isLiveUpdate?: boolean; - error?: unknown; -} - interface DataChangeBase { isFirstRender?: boolean; repaintChangesOnly?: boolean; @@ -95,11 +88,12 @@ interface UpdateChange extends DataChangeBase { export type DataChange = | UpdateChange | SelectionChange | FocusedRowChange - | (DataChangeBase & HandleDataChangedEvent) + | (DataChangeBase & ChangedEvent) + | (DataChangeBase & { changeType: 'refresh' }) | (DataChangeBase & { changeType: 'pageIndex' }) - | (DataChangeBase & { changeType?: 'refresh', isLiveUpdate: boolean; isOptionChanged: boolean }) - | (DataChangeBase & { changeType?: 'refresh', event: unknown; virtualColumnsScrolling: boolean }) - | (DataChangeBase & { changeType?: 'refresh', useProcessedItemsCache: boolean; cancelEmptyChanges: boolean }); + | (DataChangeBase & { changeType: 'refresh', isLiveUpdate: boolean; isOptionChanged: boolean }) + | (DataChangeBase & { changeType: 'refresh', event: unknown; virtualColumnsScrolling: boolean }) + | (DataChangeBase & { changeType: 'refresh', useProcessedItemsCache: boolean; cancelEmptyChanges: boolean }); export type PagingOptionName = 'pageIndex' | 'pageSize'; diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts index d7a452d8305a..f3f605b993bd 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts @@ -19,7 +19,7 @@ import { getPageDataFromCache, setPageDataToCache, } from './m_data_source_adapter_utils'; -import type { LoadOperation, OperationTypes } from './types'; +import type { ChangedEvent, LoadOperation, OperationTypes } from './types'; export default class DataSourceAdapter extends modules.Controller { protected _dataSource: any; @@ -66,7 +66,7 @@ export default class DataSourceAdapter extends modules.Controller { private _isCustomLoading: any; - protected changed: any; + protected changed!: Callback<[ChangedEvent?]>; protected loadingChanged: any; @@ -78,7 +78,7 @@ export default class DataSourceAdapter extends modules.Controller { private pushed: any; - private _dataChangedHandler!: (e: any) => any; + private _dataChangedHandlerProxy!: (e: ChangedEvent) => void; private _customizeStoreLoadOptionsHandlerProxy!: (e: LoadOperation) => void; @@ -118,7 +118,7 @@ export default class DataSourceAdapter extends modules.Controller { that.changing = Callbacks(); that.pushed = Callbacks(); - that._dataChangedHandler = that._handleDataChanged.bind(that); + that._dataChangedHandlerProxy = that._dataChangedHandler.bind(that); that._customizeStoreLoadOptionsHandlerProxy = that._customizeStoreLoadOptionsHandler.bind(that); that._dataLoadedHandler = that._handleDataLoaded.bind(that); that._loadingChangedHandler = that._handleLoadingChanged.bind(that); @@ -126,7 +126,7 @@ export default class DataSourceAdapter extends modules.Controller { that._pushHandler = that._handlePush.bind(that); that._changingHandler = that._handleChanging.bind(that); - dataSource.on('changed', that._dataChangedHandler); + dataSource.on('changed', that._dataChangedHandlerProxy); dataSource.on('customizeStoreLoadOptions', that._customizeStoreLoadOptionsHandlerProxy); dataSource.on('customizeLoadResult', that._dataLoadedHandler); dataSource.on('loadingChanged', that._loadingChangedHandler); @@ -148,7 +148,7 @@ export default class DataSourceAdapter extends modules.Controller { const dataSource = that._dataSource; const store = dataSource.store(); - dataSource.off('changed', that._dataChangedHandler); + dataSource.off('changed', that._dataChangedHandlerProxy); dataSource.off('customizeStoreLoadOptions', that._customizeStoreLoadOptionsHandlerProxy); dataSource.off('customizeLoadResult', that._dataLoadedHandler); dataSource.off('loadingChanged', that._loadingChangedHandler); @@ -638,11 +638,14 @@ export default class DataSourceAdapter extends modules.Controller { /** * @extended: virtual_scrolling */ - protected _handleDataChanged(args) { + // ChangedEvent + protected _dataChangedHandler(e?: ChangedEvent) { let currentTotalCount; const dataSource = this._dataSource; let isLoading = false; - const isDataLoading = !args || isDefined(args.changeType); + + // At this stage e.changeType can be defined only if virtual scrolling and scrolling.legacyMode is true + const isDataLoading = !e || isDefined(e.changeType); const itemsCount = this.itemsCount(); @@ -684,7 +687,7 @@ export default class DataSourceAdapter extends modules.Controller { this._lastOperationTypes = {}; this.component._optionCache = {}; - this.changed.fire(args); + this.changed.fire(e); this.component._optionCache = undefined; } } diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/types.ts b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/types.ts index 453cfd0cb007..fbb3b666ca9c 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/types.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/types.ts @@ -1,6 +1,10 @@ import type { Mode } from '@js/common'; import type { Properties } from '@js/ui/data_grid'; -import type { LoadOperation as BaseLoadOperation, StoreLoadOptions } from '@ts/data/data_source/types'; +import type { + ChangedEvent as BaseChangedEvent, + LoadOperation as BaseLoadOperation, + StoreLoadOptions, +} from '@ts/data/data_source/types'; export type RemoteOperations = Properties['remoteOperations']; @@ -40,3 +44,10 @@ export interface LoadOperation extends BaseLoadOperation { totalCount?: number; } } + +export interface ChangedEvent extends BaseChangedEvent { + // When virtual scrolling with scrolling.legacyMode, changeType + // also can be 'append', 'prepend', 'pageIndex' in case of + changeType?: 'loadError'; + error?: unknown; +} diff --git a/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing.ts b/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing.ts index 37adb47ebe58..b5b45608d052 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing.ts @@ -615,6 +615,7 @@ class EditingControllerImpl extends modules.ViewController { }); dataController.updateItems({ + changeType: 'refresh', repaintChangesOnly: true, isLiveUpdate: false, isOptionChanged: true, @@ -1983,6 +1984,7 @@ class EditingControllerImpl extends modules.ViewController { const dataController = this._dataController; dataController.updateItems({ + changeType: 'refresh', repaintChangesOnly: this.option('repaintChangesOnly'), }); } diff --git a/packages/devextreme/js/__internal/grids/grid_core/selection/m_selection.ts b/packages/devextreme/js/__internal/grids/grid_core/selection/m_selection.ts index c2fa2bd835aa..13f585b88d84 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/selection/m_selection.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/selection/m_selection.ts @@ -25,6 +25,7 @@ import type { RowsView } from '@ts/grids/grid_core/views/m_rows_view'; import Selection from '@ts/ui/selection/selection'; import type { DataController } from '../data_controller/data_controller'; +import type { ChangedEvent } from '../data_source_adapter/types'; import modules from '../m_modules'; import gridCoreUtils from '../m_utils'; @@ -675,9 +676,9 @@ export const dataSelectionExtenderMixin = (Base: ModuleType) => } // eslint-disable-next-line - protected _handleDataChanged(e?) { + protected _dataChangedHandler(e?: ChangedEvent) { const hasLoadOperation = this.hasLoadOperation(); - super._handleDataChanged.apply(this, arguments as any); + super._dataChangedHandler(e); if (hasLoadOperation && !this._repaintChangesOnly) { this._selectionController.focusedItemIndex(-1); diff --git a/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts index b4f07eb89859..060d46b2d5c2 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts @@ -18,6 +18,7 @@ import type { ModuleType } from '@ts/grids/grid_core/m_types'; import type { ResizingController } from '@ts/grids/grid_core/views/m_grid_view'; import type { RowsView } from '@ts/grids/grid_core/views/m_rows_view'; +import type { ChangedEvent } from '../data_source_adapter/types'; import gridCoreUtils from '../m_utils'; import type { RowsViewScrollEvent } from '../views/types'; import { subscribeToExternalScrollers, VirtualScrollController } from './m_virtual_scrolling_core'; @@ -227,15 +228,16 @@ export const dataSourceAdapterExtender = (Base: ModuleType) = super._handleLoadError.apply(this, arguments as any); } - protected _handleDataChanged(e) { + protected _dataChangedHandler(e?: ChangedEvent) { if (this.option(LEGACY_SCROLLING_MODE) === false) { this._items = this._dataSource.items().slice(); this._totalCount = this._dataSourceTotalCount(true); - super._handleDataChanged.apply(this, arguments as any); + super._dataChangedHandler(e); return; } - const callBase = super._handleDataChanged.bind(this); + // MYTODO + const callBase = super._dataChangedHandler.bind(this); this._virtualScrollController.handleDataChanged(callBase, e); } @@ -1169,6 +1171,7 @@ export const data = (Base: ModuleType) => class VirtualScrolling if (needToUpdateItems) { const noPendingChangesInEditing = !this._editingController?.getChanges()?.length; this.updateItems({ + changeType: 'refresh', repaintChangesOnly: true, needUpdateDimensions: true, useProcessedItemsCache: noPendingChangesInEditing, diff --git a/packages/devextreme/js/__internal/grids/tree_list/selection/m_selection.ts b/packages/devextreme/js/__internal/grids/tree_list/selection/m_selection.ts index b267b92316ae..eecb0a2b6604 100644 --- a/packages/devextreme/js/__internal/grids/tree_list/selection/m_selection.ts +++ b/packages/devextreme/js/__internal/grids/tree_list/selection/m_selection.ts @@ -7,6 +7,7 @@ import { extend } from '@js/core/utils/extend'; import { isDefined } from '@js/core/utils/type'; import type { ColumnHeadersView } from '@ts/grids/grid_core/column_headers/m_column_headers'; import type { DataController } from '@ts/grids/grid_core/data_controller/data_controller'; +import type { ChangedEvent } from '@ts/grids/grid_core/data_source_adapter/types'; import type { ModuleType } from '@ts/grids/grid_core/m_types'; import type { SelectionController } from '@ts/grids/grid_core/selection/m_selection'; import { @@ -28,17 +29,17 @@ const nodeExists = function (array, currentKey) { }; const data = (Base: ModuleType) => class DataSelectionTreeListExtender extends dataSelectionExtenderMixin(Base) { - protected _handleDataChanged(e) { + protected _dataChangedHandler(e?: ChangedEvent) { // @ts-expect-error const isRecursiveSelection = this._selectionController.isRecursiveSelection(); - if (isRecursiveSelection && (!e || e.changeType !== 'updateSelectionState')) { + if (isRecursiveSelection) { // @ts-expect-error this._selectionController.updateSelectionState({ selectedItemKeys: this.option('selectedRowKeys'), }); } - super._handleDataChanged.apply(this, arguments as any); + super._dataChangedHandler(e); } private loadDescendants() { From 09ef4acf98312e4d61f5a5b281b758b1cb676730 Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Fri, 7 Aug 2026 21:11:20 +0600 Subject: [PATCH 07/11] small improvement --- .../js/__internal/data/data_source/m_data_source.ts | 5 +++-- packages/devextreme/js/__internal/data/data_source/types.ts | 4 +--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/devextreme/js/__internal/data/data_source/m_data_source.ts b/packages/devextreme/js/__internal/data/data_source/m_data_source.ts index ce32e09e5f8d..5328dbdeafb3 100644 --- a/packages/devextreme/js/__internal/data/data_source/m_data_source.ts +++ b/packages/devextreme/js/__internal/data/data_source/m_data_source.ts @@ -20,9 +20,10 @@ import { create } from '@js/core/utils/queue'; import { isBoolean, isDefined, isEmptyObject, isNumeric, isObject, isString, } from '@js/core/utils/type'; +import type { StoreChange } from '@js/data/store'; import commonUtils from '@ts/core/utils/m_common'; -import type { ChangedEvent, DataChange, LoadOperation } from './types'; +import type { ChangedEvent, LoadOperation } from './types'; export const DataSource = Class.inherit({ ctor(options) { @@ -432,7 +433,7 @@ export const DataSource = Class.inherit({ }); }, - _onPush(changes: DataChange[]) { + _onPush(changes: StoreChange[]) { if (this._reshapeOnPush) { this.load(); } else { diff --git a/packages/devextreme/js/__internal/data/data_source/types.ts b/packages/devextreme/js/__internal/data/data_source/types.ts index 5625a4c76c6d..18a7070d76d3 100644 --- a/packages/devextreme/js/__internal/data/data_source/types.ts +++ b/packages/devextreme/js/__internal/data/data_source/types.ts @@ -17,8 +17,6 @@ export interface LoadOperation { delay?: number; } -export type DataChange = StoreChange; - export interface ChangedEvent { - changes?: DataChange[]; + changes?: StoreChange[]; } From 973fc3246e016f0ce5a9ba76c8477d2399482f1e Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Fri, 7 Aug 2026 21:35:00 +0600 Subject: [PATCH 08/11] fix qunit --- .../grid_core/data_controller/data_controller.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts index b9a6df71bb92..d75619a982a8 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts @@ -569,13 +569,13 @@ export class DataController extends DataHelperMixin(modules.Controller) { } else { this._currentOperationTypes = dataSource.operationTypes(); - const change: DataChange = e?.changeType === 'loadError' - ? e - : { - // use e?.changeType, because it is passed from virtual scrolling with scrolling.legacyMode + const change: DataChange = isDefined(e) + ? { + ...e, changeType: e?.changeType ?? 'refresh', - changes: e?.changes, - }; + // need to cast, because in virtual scrolling with scrolling.legacyMode, e has more fields + } as DataChange + : { changeType: 'refresh' }; that.updateItems(change, true); } From 586bfe6319e5c9277877b4295597fbdc154fbece Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Fri, 7 Aug 2026 21:54:52 +0600 Subject: [PATCH 09/11] move remoteOperations to adapter --- .../grids/data_grid/summary/m_summary.ts | 14 +++++++++----- .../data_controller/data_controller.ts | 17 ++--------------- .../m_data_source_adapter.ts | 16 +++++++++++----- .../utils/__tests__/remoteOperations.test.ts | 0 .../utils/__tests__/store.test.ts | 0 .../utils/remoteOperations.ts | 8 ++++++-- .../utils/store.ts | 0 .../grids/grid_core/views/m_rows_view.ts | 2 +- .../m_data_source_adapter.test.ts | 2 +- .../m_data_source_adapter.ts | 5 ++--- 10 files changed, 32 insertions(+), 32 deletions(-) rename packages/devextreme/js/__internal/grids/grid_core/{data_controller => data_source_adapter}/utils/__tests__/remoteOperations.test.ts (100%) rename packages/devextreme/js/__internal/grids/grid_core/{data_controller => data_source_adapter}/utils/__tests__/store.test.ts (100%) rename packages/devextreme/js/__internal/grids/grid_core/{data_controller => data_source_adapter}/utils/remoteOperations.ts (91%) rename packages/devextreme/js/__internal/grids/grid_core/{data_controller => data_source_adapter}/utils/store.ts (100%) diff --git a/packages/devextreme/js/__internal/grids/data_grid/summary/m_summary.ts b/packages/devextreme/js/__internal/grids/data_grid/summary/m_summary.ts index 96542d63d88f..45da13a9c51a 100644 --- a/packages/devextreme/js/__internal/grids/data_grid/summary/m_summary.ts +++ b/packages/devextreme/js/__internal/grids/data_grid/summary/m_summary.ts @@ -817,11 +817,15 @@ const data = (Base: ModuleType) => class SummaryDataControllerEx return sortByGroups; } - protected _createDataSourceAdapterCore(dataSource, remoteOperations) { - const that = this; - const dataSourceAdapter = super._createDataSourceAdapterCore(dataSource, remoteOperations); - - dataSourceAdapter.summaryGetter((currentRemoteOperations) => that._getSummaryOptions(currentRemoteOperations || remoteOperations)); + protected _createDataSourceAdapter(dataSource) { + const dataSourceAdapter = super._createDataSourceAdapter(dataSource); + + dataSourceAdapter.summaryGetter((currentRemoteOperations) => { + const result = this._getSummaryOptions( + currentRemoteOperations ?? dataSourceAdapter.remoteOperations(), + ); + return result; + }); return dataSourceAdapter; } diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts index d75619a982a8..771bf9cfd698 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts @@ -60,7 +60,6 @@ import type { PagingResult, } from './types'; import { resolvePaginate, syncPaging } from './utils/paging'; -import { normalizeRemoteOperations } from './utils/remoteOperations'; import { generateRowValues } from './utils/row_values'; export class DataController extends DataHelperMixin(modules.Controller) { @@ -1374,26 +1373,14 @@ export class DataController extends DataHelperMixin(modules.Controller) { protected _getDataSourceAdapter(): any {} - /** - * @extended: DataGrid's summary - */ - protected _createDataSourceAdapterCore(dataSource, remoteOperations) { + protected _createDataSourceAdapter(dataSource) { const dataSourceAdapterProvider = this._getDataSourceAdapter(); const dataSourceAdapter = dataSourceAdapterProvider.create(this.component); - dataSourceAdapter.init(dataSource, remoteOperations); + dataSourceAdapter.init(dataSource); return dataSourceAdapter; } - private _createDataSourceAdapter(dataSource) { - const remoteOperations = normalizeRemoteOperations( - this.option('remoteOperations'), - dataSource.store(), - ); - - return this._createDataSourceAdapterCore(dataSource, remoteOperations); - } - private setDataSource(dataSource) { const that = this; const oldDataSource = that._dataSource; diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts index f3f605b993bd..488063ae250a 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts @@ -19,12 +19,15 @@ import { getPageDataFromCache, setPageDataToCache, } from './m_data_source_adapter_utils'; -import type { ChangedEvent, LoadOperation, OperationTypes } from './types'; +import type { + ChangedEvent, LoadOperation, OperationTypes, RemoteOperationsOptions, +} from './types'; +import { normalizeRemoteOperations } from './utils/remoteOperations'; export default class DataSourceAdapter extends modules.Controller { protected _dataSource: any; - private _remoteOperations: any; + private _remoteOperations!: RemoteOperationsOptions; private _isLastPage!: boolean; @@ -96,11 +99,14 @@ export default class DataSourceAdapter extends modules.Controller { private readonly group!: (args?: any) => any; - public init(dataSource?, remoteOperations?) { + public init(dataSource?) { const that = this; that._dataSource = dataSource; - that._remoteOperations = remoteOperations || {}; + that._remoteOperations = normalizeRemoteOperations( + this.option('remoteOperations'), + dataSource.store(), + ); that._isLastPage = !dataSource.isLastPage(); that._hasLastPage = false; @@ -164,7 +170,7 @@ export default class DataSourceAdapter extends modules.Controller { /** * @extended: TreeLists's data_source_adapter */ - protected remoteOperations() { + public remoteOperations(): RemoteOperationsOptions { return this._remoteOperations; } diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/utils/__tests__/remoteOperations.test.ts b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/utils/__tests__/remoteOperations.test.ts similarity index 100% rename from packages/devextreme/js/__internal/grids/grid_core/data_controller/utils/__tests__/remoteOperations.test.ts rename to packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/utils/__tests__/remoteOperations.test.ts diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/utils/__tests__/store.test.ts b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/utils/__tests__/store.test.ts similarity index 100% rename from packages/devextreme/js/__internal/grids/grid_core/data_controller/utils/__tests__/store.test.ts rename to packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/utils/__tests__/store.test.ts diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/utils/remoteOperations.ts b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/utils/remoteOperations.ts similarity index 91% rename from packages/devextreme/js/__internal/grids/grid_core/data_controller/utils/remoteOperations.ts rename to packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/utils/remoteOperations.ts index 0f9004a0bcfb..af9acb2fe45e 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/utils/remoteOperations.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/utils/remoteOperations.ts @@ -1,13 +1,13 @@ import { isObject } from '@js/core/utils/type'; import type { Store } from '@js/data'; -import type { RemoteOperations, RemoteOperationsOptions } from '../../data_source_adapter/types'; +import type { RemoteOperations, RemoteOperationsOptions } from '../types'; import { isCustomStore, isLocalStore } from './store'; export function normalizeRemoteOperations( remoteOperations: RemoteOperations, store: Store, -): RemoteOperationsOptions | false | undefined { +): RemoteOperationsOptions { const allExceptGroupPagingEnabled: RemoteOperationsOptions = { filtering: true, sorting: true, @@ -33,5 +33,9 @@ export function normalizeRemoteOperations( return allExceptGroupPagingEnabled; } + if (!remoteOperations) { + return {}; + } + return remoteOperations; } diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/utils/store.ts b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/utils/store.ts similarity index 100% rename from packages/devextreme/js/__internal/grids/grid_core/data_controller/utils/store.ts rename to packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/utils/store.ts diff --git a/packages/devextreme/js/__internal/grids/grid_core/views/m_rows_view.ts b/packages/devextreme/js/__internal/grids/grid_core/views/m_rows_view.ts index 75ef56d6eff7..a533a04f2932 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/views/m_rows_view.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/views/m_rows_view.ts @@ -29,7 +29,7 @@ import type { ValidatingController } from '@ts/grids/grid_core/validating/m_vali import type { ResizingController } from '@ts/grids/grid_core/views/m_grid_view'; import { CLASSES as REORDERING_CLASSES } from '../columns_resizing_reordering/const'; -import { isLocalStore } from '../data_controller/utils/store'; +import { isLocalStore } from '../data_source_adapter/utils/store'; import type { EditingController } from '../editing/m_editing'; import gridCoreUtils from '../m_utils'; import { CLASSES } from '../sticky_columns/const'; diff --git a/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.test.ts b/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.test.ts index b14c0231a4f7..580d09497e1f 100644 --- a/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.test.ts +++ b/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.test.ts @@ -70,7 +70,7 @@ describe('TreeList DataSourceAdapter - T1311885 Race Condition', () => { } as any; dataSourceAdapter = new DataSourceAdapterTreeList(mockComponent); - dataSourceAdapter.init(dataSource, { remoteOperations: { filtering: true } }); + dataSourceAdapter.init(dataSource); (dataSourceAdapter as any)._loadDataSource = jest.fn((options: any) => { // @ts-expect-error diff --git a/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.ts b/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.ts index b98d5b7fe7d1..89df376f60af 100644 --- a/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.ts +++ b/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.ts @@ -745,9 +745,8 @@ export class DataSourceAdapterTreeList extends DataSourceAdapter { super._handlePush.apply(this, arguments as any); } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public init(dataSource, remoteOperations) { - super.init.apply(this, arguments as any); + public init(dataSource) { + super.init(dataSource); const dataStructure = this.option('dataStructure'); From 82149812077c34da788983df057e89e367674673 Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Fri, 7 Aug 2026 21:57:24 +0600 Subject: [PATCH 10/11] remove comment --- .../grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts index 060d46b2d5c2..198962ee4162 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts @@ -236,7 +236,6 @@ export const dataSourceAdapterExtender = (Base: ModuleType) = return; } - // MYTODO const callBase = super._dataChangedHandler.bind(this); this._virtualScrollController.handleDataChanged(callBase, e); From a29342b2f5a3a415591c29651c12cec7cab0efd4 Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Fri, 7 Aug 2026 22:13:02 +0600 Subject: [PATCH 11/11] fix tests --- .../utils/__tests__/remoteOperations.test.ts | 8 ++++++-- .../columnsController.tests.js | 10 ++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/utils/__tests__/remoteOperations.test.ts b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/utils/__tests__/remoteOperations.test.ts index 235c4c9cef2c..8c41358dc187 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/utils/__tests__/remoteOperations.test.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/utils/__tests__/remoteOperations.test.ts @@ -30,8 +30,12 @@ describe('normalizeRemoteOperations', () => { }); }); - it('false is returned as is', () => { - expect(normalizeRemoteOperations(false, remoteStore)).toBe(false); + it('an object is returned on false', () => { + expect(normalizeRemoteOperations(false, remoteStore)).toEqual({}); + }); + + it('an object is returned on undefined', () => { + expect(normalizeRemoteOperations(undefined, remoteStore)).toEqual({}); }); it('an object without groupPaging is returned unchanged', () => { diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/columnsController.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/columnsController.tests.js index 622149cb8e07..451f7c908170 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/columnsController.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/columnsController.tests.js @@ -42,10 +42,10 @@ const createMockDataSource = function(items, loadOptions) { }; }; -const createDataSource = function(context, config, remoteOperations) { +const createDataSource = function(context, config) { const dataSource = new DataSource(config); const dataAdapter = dataSourceAdapter.create(context); - dataAdapter.init(dataSource, remoteOperations); + dataAdapter.init(dataSource); return dataAdapter; }; @@ -3102,9 +3102,11 @@ QUnit.module('initialization from dataSource', { beforeEach: setupModule, afterE const items = [{ Column1: 'Test1', Column2: '2012/01/01' }, { Column1: 'Test1', Column2: '2013/05/04' }, { Column1: 'Test2', Column2: '2014/03/05' }]; this.applyOptions({ - columns: ['Column1', { dataField: 'Column2', dataType: 'date' }] + columns: ['Column1', { dataField: 'Column2', dataType: 'date' }], + remoteOperations: { filtering: true, sorting: true, paging: true } }); + // mytodo const dataSource = createDataSource(this, { group: 'Column1', load: function(options) { @@ -3113,7 +3115,7 @@ QUnit.module('initialization from dataSource', { beforeEach: setupModule, afterE totalCount: function() { return 3; } - }, { filtering: true, sorting: true, paging: true }); + }); dataSource.load();