diff --git a/packages/devextreme/js/__internal/grids/data_grid/focus/m_focus.ts b/packages/devextreme/js/__internal/grids/data_grid/focus/m_focus.ts index 9318b076e998..25bd6b720df0 100644 --- a/packages/devextreme/js/__internal/grids/data_grid/focus/m_focus.ts +++ b/packages/devextreme/js/__internal/grids/data_grid/focus/m_focus.ts @@ -2,6 +2,8 @@ import { equalByValue } from '@js/core/utils/common'; import { compileGetter } from '@js/core/utils/data'; import { Deferred } from '@js/core/utils/deferred'; import type { DataController } from '@ts/grids/grid_core/data_controller/data_controller'; +import type { DataSourceController } from '@ts/grids/grid_core/data_source/data_source_controller'; +import type { FocusDataSourceControllerExtension } from '@ts/grids/grid_core/focus/extenders/focus_data_source_controller'; import { focusModule } from '@ts/grids/grid_core/focus/focus_module'; import type { ModuleType } from '@ts/grids/grid_core/m_types'; @@ -24,7 +26,8 @@ DataController & GroupingDataControllerExtension>; const data = (Base: DataControllerBase) => class FocusDataControllerExtender extends focusModule.extenders.controllers.data(Base) { - public declare _dataSource?: GroupingDataSourceAdapter | null; + protected declare dataSourceController: DataSourceController + & FocusDataSourceControllerExtension; private changeRowExpand(path, isRowClick) { // @ts-expect-error @@ -89,47 +92,47 @@ const data = (Base: DataControllerBase) => class FocusDataControllerExtender ext } private _calculateGlobalRowIndexByGroupedData(key) { - const dataSource = this._dataSource; + const dataSourceAdapter = this.dataSourceController.getAdapter(); const filter = this._generateFilterByKey(key); // @ts-expect-error const deferred = new Deferred(); const isGroupKey = Array.isArray(key); - if (isGroupKey || !dataSource) { + if (isGroupKey || !dataSourceAdapter) { return deferred.resolve(-1).promise(); } - const group = dataSource.group(); + const group = dataSourceAdapter.group(); - if (!dataSource._grouping._updatePagingOptions) { + if (!dataSourceAdapter._grouping._updatePagingOptions) { this._calculateGlobalRowIndexByFlatData(key, null, true) .done(deferred.resolve) .fail(deferred.reject); return deferred; } - dataSource.customLoader.load({ + dataSourceAdapter.customLoader.load({ filter: this._concatWithCombinedFilter(filter), group, }).done(({ data }) => { const hasData = Array.isArray(data) && data.length > 0; - if (this._dataSource !== dataSource || !hasData) { + if (this.dataSourceController.getAdapter() !== dataSourceAdapter || !hasData) { return deferred.resolve(-1).promise(); } const groupPath = this._getGroupPath(data, gridCore.normalizeSortingInfo(group).length); this._expandGroupByPath(this, groupPath, 0).done(() => { - this._calculateExpandedRowGlobalIndex(deferred, key, groupPath, group, dataSource); + this._calculateExpandedRowGlobalIndex(deferred, key, groupPath, group, dataSourceAdapter); }).fail(deferred.reject); }).fail(deferred.reject); return deferred.promise(); } - private _calculateExpandedRowGlobalIndex(deferred, key, groupPath, group, dataSource) { - if (this._dataSource !== dataSource) { + private _calculateExpandedRowGlobalIndex(deferred, key, groupPath, group, dataSourceAdapter) { + if (this.dataSourceController.getAdapter() !== dataSourceAdapter) { deferred.resolve(-1); return; } @@ -137,10 +140,10 @@ const data = (Base: DataControllerBase) => class FocusDataControllerExtender ext const groupFilter = createGroupFilter(groupPath, { group }); const scrollingMode = this.option('scrolling.mode'); const isVirtualScrolling = scrollingMode === 'virtual' || scrollingMode === 'infinite'; - const pageSize = dataSource.pageSize(); + const pageSize = dataSourceAdapter.pageSize(); let groupOffset; - dataSource._grouping._updatePagingOptions({ skip: 0, take: MAX_SAFE_INTEGER }, (groupInfo, totalOffset) => { + dataSourceAdapter._grouping._updatePagingOptions({ skip: 0, take: MAX_SAFE_INTEGER }, (groupInfo, totalOffset) => { if (equalByValue(groupInfo.path, groupPath)) { groupOffset = totalOffset; } diff --git a/packages/devextreme/js/__internal/grids/data_grid/grouping/extenders/__tests__/grouping_data_controller.expanding.integration.test.ts b/packages/devextreme/js/__internal/grids/data_grid/grouping/extenders/__tests__/grouping_data_controller.expanding.integration.test.ts new file mode 100644 index 000000000000..6db931f10df7 --- /dev/null +++ b/packages/devextreme/js/__internal/grids/data_grid/grouping/extenders/__tests__/grouping_data_controller.expanding.integration.test.ts @@ -0,0 +1,142 @@ +import { + afterEach, beforeEach, describe, expect, it, +} from '@jest/globals'; +import { + afterTest, + beforeTest, + createDataGrid, + flushAsync, +} from '@ts/grids/grid_core/__tests__/__mock__/helpers/utils'; + +const DATA = [ + { id: 1, group: 'A', value: 10 }, + { id: 2, group: 'A', value: 20 }, + { id: 3, group: 'B', value: 30 }, +]; + +const createGroupedGrid = async ( + autoExpandAll = true, +): ReturnType => createDataGrid({ + dataSource: DATA, + columns: [{ dataField: 'group', groupIndex: 0 }, 'value'], + grouping: { autoExpandAll }, + paging: { enabled: false }, +}); + +const groupRowKeys = ( + instance: Awaited>['instance'], +): unknown[] => instance.getVisibleRows() + .filter((row) => row.rowType === 'group') + .map((row) => row.key); + +describe('Grouping data controller — expanding', () => { + beforeEach(beforeTest); + afterEach(afterTest); + + describe('with a data source', () => { + it('reports every group expanded when autoExpandAll is on', async () => { + const { instance } = await createGroupedGrid(); + await flushAsync(); + + expect(groupRowKeys(instance)).toEqual([['A'], ['B']]); + expect(instance.isRowExpanded(['A'])).toBe(true); + expect(instance.isRowExpanded(['B'])).toBe(true); + }); + + it('reports every group collapsed when autoExpandAll is off', async () => { + const { instance } = await createGroupedGrid(false); + await flushAsync(); + + expect(instance.isRowExpanded(['A'])).toBe(false); + expect(instance.isRowExpanded(['B'])).toBe(false); + }); + + it('collapses one group through collapseRow', async () => { + const { instance } = await createGroupedGrid(); + await flushAsync(); + + const collapsing = instance.collapseRow(['A']); + await flushAsync(); + await collapsing; + + expect(instance.isRowExpanded(['A'])).toBe(false); + expect(instance.isRowExpanded(['B'])).toBe(true); + }); + + it('expands one group through expandRow', async () => { + const { instance } = await createGroupedGrid(false); + await flushAsync(); + + const expanding = instance.expandRow(['A']); + await flushAsync(); + await expanding; + + expect(instance.isRowExpanded(['A'])).toBe(true); + expect(instance.isRowExpanded(['B'])).toBe(false); + }); + + it('collapses every group through collapseAll', async () => { + const { instance } = await createGroupedGrid(); + await flushAsync(); + + instance.collapseAll(); + await flushAsync(); + + expect(instance.isRowExpanded(['A'])).toBe(false); + expect(instance.isRowExpanded(['B'])).toBe(false); + }); + + it('expands every group through expandAll', async () => { + const { instance } = await createGroupedGrid(false); + await flushAsync(); + + instance.expandAll(); + await flushAsync(); + + expect(instance.isRowExpanded(['A'])).toBe(true); + expect(instance.isRowExpanded(['B'])).toBe(true); + }); + + it('resets the page index when collapseAll changes the expanded state', async () => { + const { instance } = await createDataGrid({ + dataSource: DATA, + columns: [{ dataField: 'group', groupIndex: 0 }, 'value'], + grouping: { autoExpandAll: true }, + paging: { pageSize: 2 }, + }); + await flushAsync(); + + const paging = instance.pageIndex(1); + await flushAsync(); + await paging; + expect(instance.pageIndex()).toBe(1); + + instance.collapseAll(); + await flushAsync(); + + expect(instance.pageIndex()).toBe(0); + }); + }); + + describe('with no data source', () => { + it('reports a group as not expanded', async () => { + const { instance } = await createDataGrid({ + columns: [{ dataField: 'group', groupIndex: 0 }, 'value'], + }); + await flushAsync(); + + expect(instance.isRowExpanded(['A'])).toBe(false); + }); + + it('resolves expandRow without reaching a data source', async () => { + const { instance } = await createDataGrid({ + columns: [{ dataField: 'group', groupIndex: 0 }, 'value'], + }); + await flushAsync(); + + await instance.expandRow(['A']); + + expect(instance.getVisibleRows()).toEqual([]); + }); + }); +}); diff --git a/packages/devextreme/js/__internal/grids/data_grid/grouping/extenders/grouping_data_controller.ts b/packages/devextreme/js/__internal/grids/data_grid/grouping/extenders/grouping_data_controller.ts index 769d0aa1590f..759b24256e06 100644 --- a/packages/devextreme/js/__internal/grids/data_grid/grouping/extenders/grouping_data_controller.ts +++ b/packages/devextreme/js/__internal/grids/data_grid/grouping/extenders/grouping_data_controller.ts @@ -4,6 +4,7 @@ import type { Properties } from '@js/ui/data_grid'; import type { DataController } from '@ts/grids/grid_core/data_controller/data_controller'; import type { ItemProcessingOptions, ProcessedItem } from '@ts/grids/grid_core/data_controller/types'; import { countRowsBefore } from '@ts/grids/grid_core/data_controller/utils/row_changes'; +import type { DataSourceController } from '@ts/grids/grid_core/data_source/data_source_controller'; import type { RawItemData } from '@ts/grids/grid_core/data_source_adapter/types'; import type { ModuleType, @@ -23,7 +24,7 @@ import { export const groupingDataControllerExtender = ( Base: ModuleType, ): ModuleType => class GroupingDataControllerExtender extends Base { - public declare _dataSource?: GroupingDataSourceAdapter | null; + protected declare dataSourceController: DataSourceController; public init(): void { super.init(); @@ -170,18 +171,18 @@ export const groupingDataControllerExtender = ( } private collapseAll(groupIndex: number): void { - const dataSource = this._dataSource; - if (dataSource?.collapseAll(groupIndex)) { - dataSource?.pageIndex(0); - dataSource?.reload(); + const dataSourceAdapter = this.dataSourceController.getAdapter(); + if (dataSourceAdapter?.collapseAll(groupIndex)) { + dataSourceAdapter.pageIndex(0); + dataSourceAdapter.reload(); } } private expandAll(groupIndex: number): void { - const dataSource = this._dataSource; - if (dataSource?.expandAll(groupIndex)) { - dataSource?.pageIndex(0); - dataSource?.reload(); + const dataSourceAdapter = this.dataSourceController.getAdapter(); + if (dataSourceAdapter?.expandAll(groupIndex)) { + dataSourceAdapter.pageIndex(0); + dataSourceAdapter.reload(); } } @@ -205,13 +206,13 @@ export const groupingDataControllerExtender = ( } protected changeRowExpandCore(key: RowKey): DeferredObj { - const dataSource = this._dataSource; + const dataSourceAdapter = this.dataSourceController.getAdapter(); const d = Deferred(); - if (!dataSource) { + if (!dataSourceAdapter) { d.resolve(); } else { - when(dataSource.changeRowExpand(key)).done(() => { + when(dataSourceAdapter.changeRowExpand(key)).done(() => { // eslint-disable-next-line @typescript-eslint/no-misused-promises this.load().done(d.resolve).fail(d.reject); // eslint-disable-next-line @typescript-eslint/no-misused-promises @@ -222,7 +223,7 @@ export const groupingDataControllerExtender = ( } private isRowExpanded(key: RowKey): boolean { - return !!this._dataSource?.isRowExpanded(key); + return !!this.dataSourceController.getAdapter()?.isRowExpanded(key); } private expandRow(key: RowKey): DeferredObj { diff --git a/packages/devextreme/js/__internal/grids/data_grid/summary/extenders/__tests__/summary_data_controller.total_summary.integration.test.ts b/packages/devextreme/js/__internal/grids/data_grid/summary/extenders/__tests__/summary_data_controller.total_summary.integration.test.ts new file mode 100644 index 000000000000..352d22bade7c --- /dev/null +++ b/packages/devextreme/js/__internal/grids/data_grid/summary/extenders/__tests__/summary_data_controller.total_summary.integration.test.ts @@ -0,0 +1,107 @@ +import { + afterEach, beforeEach, describe, expect, it, +} from '@jest/globals'; +import type { DataGridInstance } from '@ts/grids/grid_core/__tests__/__mock__/helpers/utils'; +import { + afterTest, + beforeTest, + createDataGrid, + flushAsync, +} from '@ts/grids/grid_core/__tests__/__mock__/helpers/utils'; + +import type { FooterItem } from '../../types'; + +// `footerItems` is added by this extender, so it is not on the registered controller's type. +const footerItemsOf = (instance: DataGridInstance): FooterItem[] => ( + instance.getController('data') as unknown as { footerItems: () => FooterItem[] } +).footerItems(); + +const DATA = [ + { id: 1, value: 10 }, + { id: 2, value: 20 }, + { id: 3, value: 30 }, +]; + +const TOTAL_ITEMS = [ + { name: 'valueSum', column: 'value', summaryType: 'sum' as const }, + { name: 'valueMax', column: 'value', summaryType: 'max' as const }, +]; + +describe('Summary data controller — total summary', () => { + beforeEach(beforeTest); + afterEach(afterTest); + + describe('with a data source', () => { + it('answers getTotalSummaryValue from the aggregates', async () => { + const { instance } = await createDataGrid({ + dataSource: DATA, + summary: { totalItems: TOTAL_ITEMS }, + }); + await flushAsync(); + + expect(instance.getTotalSummaryValue('valueSum')).toBe(60); + expect(instance.getTotalSummaryValue('valueMax')).toBe(30); + }); + + it('returns undefined for a summary item that does not exist', async () => { + const { instance } = await createDataGrid({ + dataSource: DATA, + summary: { totalItems: TOTAL_ITEMS }, + }); + await flushAsync(); + + expect(instance.getTotalSummaryValue('missing')).toBeUndefined(); + }); + + it('builds a footer row cell per total item', async () => { + const { instance } = await createDataGrid({ + dataSource: DATA, + columns: ['value'], + summary: { totalItems: TOTAL_ITEMS }, + }); + await flushAsync(); + + const [footerItem] = footerItemsOf(instance); + + expect(footerItem.summaryCells[0].map((cell) => cell.value)).toEqual([60, 30]); + }); + + it('recomputes the footer row after the data changes', async () => { + const { instance } = await createDataGrid({ + dataSource: DATA, + columns: ['value'], + summary: { totalItems: TOTAL_ITEMS }, + }); + await flushAsync(); + + instance.option('dataSource', [...DATA, { id: 4, value: 40 }]); + await flushAsync(); + + expect(instance.getTotalSummaryValue('valueSum')).toBe(100); + + const [footerItem] = footerItemsOf(instance); + + expect(footerItem.summaryCells[0].map((cell) => cell.value)).toEqual([100, 40]); + }); + }); + + describe('with no data source', () => { + it('returns undefined from getTotalSummaryValue', async () => { + const { instance } = await createDataGrid({ + summary: { totalItems: TOTAL_ITEMS }, + }); + await flushAsync(); + + expect(instance.getTotalSummaryValue('valueSum')).toBeUndefined(); + }); + + it('builds no footer row', async () => { + const { instance } = await createDataGrid({ + summary: { totalItems: TOTAL_ITEMS }, + }); + await flushAsync(); + + expect(footerItemsOf(instance)).toEqual([]); + }); + }); +}); diff --git a/packages/devextreme/js/__internal/grids/data_grid/summary/extenders/summary_data_controller.ts b/packages/devextreme/js/__internal/grids/data_grid/summary/extenders/summary_data_controller.ts index fa06dc0332d0..01de7e12fc89 100644 --- a/packages/devextreme/js/__internal/grids/data_grid/summary/extenders/summary_data_controller.ts +++ b/packages/devextreme/js/__internal/grids/data_grid/summary/extenders/summary_data_controller.ts @@ -9,6 +9,7 @@ import type { DataController } from '@ts/grids/grid_core/data_controller/data_co import type { DataChange, ItemProcessingOptions, LoadAllItemsDeferred, ProcessedItem, } from '@ts/grids/grid_core/data_controller/types'; +import type { DataSourceController } from '@ts/grids/grid_core/data_source/data_source_controller'; import type { CustomLoadResult } from '@ts/grids/grid_core/data_source_adapter/custom_loader'; import type { RawItemData } from '@ts/grids/grid_core/data_source_adapter/types'; import type { ModuleType, OptionChanged } from '@ts/grids/grid_core/m_types'; @@ -30,7 +31,7 @@ import { getSummaryItemIndex } from '../utils/get_summary_item_index'; export const summaryDataControllerExtender = ( Base: ModuleType, ): ModuleType => class SummaryDataControllerExtender extends Base { - public declare _dataSource?: SummaryDataSourceAdapter | null; + protected declare dataSourceController: DataSourceController; private _footerItems!: FooterItem[]; @@ -49,7 +50,7 @@ export const summaryDataControllerExtender = ( public getTotalSummaryValue(summaryItemName?: string | number | null): unknown { const summaryItemIndex = getSummaryItemIndex(this.option('summary.totalItems'), summaryItemName); - const aggregates = this._dataSource?.totalAggregates() ?? []; + const aggregates = this.dataSourceController.getAdapter()?.totalAggregates() ?? []; if (aggregates.length && summaryItemIndex > -1) { return aggregates[summaryItemIndex]; @@ -312,14 +313,14 @@ export const summaryDataControllerExtender = ( } protected _updateItemsCore(change: DataChange): void { - const dataSource = this._dataSource; + const dataSourceAdapter = this.dataSourceController.getAdapter(); const summaryTotalItems = this.option('summary.totalItems'); const oldSummaryCells = this._footerItems?.[0]?.summaryCells; this._footerItems = []; - if (dataSource && summaryTotalItems?.length) { - const totalAggregates = dataSource.totalAggregates(); + if (dataSourceAdapter && summaryTotalItems?.length) { + const totalAggregates = dataSourceAdapter.totalAggregates(); const summaryCells = this._getSummaryCells(summaryTotalItems, totalAggregates); if (change?.repaintChangesOnly && oldSummaryCells) { diff --git a/packages/devextreme/js/__internal/grids/grid_core/__tests__/__mock__/helpers/utils.ts b/packages/devextreme/js/__internal/grids/grid_core/__tests__/__mock__/helpers/utils.ts index d040c7d964aa..e316924f0250 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/__tests__/__mock__/helpers/utils.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/__tests__/__mock__/helpers/utils.ts @@ -20,12 +20,6 @@ export type OptionSpy = jest.Mock<(optionName: string, optionValue?: unknown) => export const spyOnOption = (instance: InternalGrid): OptionSpy => jest .spyOn(instance, 'option') as unknown as OptionSpy; -// Reads the DataController._dataSource mirror. Assertions that pin the mirror have to read the -// protected field itself rather than a delegating method. Goes away with the field, in Task B4. -export const getMirroredAdapter = ( - instance: { getController: (name: 'data') => unknown }, -): unknown => (instance.getController('data') as { _dataSource?: unknown })._dataSource; - export const SELECTORS = { gridContainer: '#gridContainer', }; diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/__tests__/data_controller.loading.test.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/__tests__/data_controller.loading.test.ts new file mode 100644 index 000000000000..7f423a44bdcf --- /dev/null +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/__tests__/data_controller.loading.test.ts @@ -0,0 +1,121 @@ +import { + afterEach, beforeEach, describe, expect, it, +} from '@jest/globals'; +import { + afterTest, + beforeTest, + createDataGrid, + flushAsync, +} from '@ts/grids/grid_core/__tests__/__mock__/helpers/utils'; +import type { DataController } from '@ts/grids/grid_core/data_controller/data_controller'; + +const DATA = [ + { id: 1, value: 'a' }, + { id: 2, value: 'b' }, + { id: 3, value: 'c' }, +]; + +type Instance = Awaited>['instance']; + +const dataControllerOf = (instance: Instance): DataController => instance.getController('data'); + +describe('DataController — loading state', () => { + beforeEach(beforeTest); + afterEach(afterTest); + + describe('with a data source', () => { + it('reports loaded after the first load', async () => { + const { instance } = await createDataGrid({ dataSource: DATA }); + await flushAsync(); + + expect(dataControllerOf(instance).isLoaded()).toBe(true); + expect(dataControllerOf(instance).isLoading()).toBe(false); + expect(instance.getVisibleRows()).toHaveLength(DATA.length); + }); + + it('reloads the rows', async () => { + const { instance } = await createDataGrid({ dataSource: DATA }); + await flushAsync(); + + const reloading = dataControllerOf(instance).reload(true); + await flushAsync(); + await reloading; + + expect(instance.getVisibleRows()).toHaveLength(DATA.length); + expect(dataControllerOf(instance).isLoaded()).toBe(true); + }); + + it('loads all items regardless of paging', async () => { + const { instance } = await createDataGrid({ dataSource: DATA, paging: { pageSize: 2 } }); + await flushAsync(); + + expect(instance.getVisibleRows()).toHaveLength(2); + + let allItems: unknown[] = []; + dataControllerOf(instance).loadAllItems().done((items: unknown[]) => { allItems = items; }); + await flushAsync(); + + expect(allItems).toHaveLength(DATA.length); + }); + + it('is not custom loading until beginCustomLoading is called', async () => { + const { instance } = await createDataGrid({ dataSource: DATA }); + await flushAsync(); + + expect(dataControllerOf(instance).isCustomLoading()).toBe(false); + + instance.beginCustomLoading('working'); + + expect(dataControllerOf(instance).isCustomLoading()).toBe(true); + + instance.endCustomLoading(); + await flushAsync(); + + expect(dataControllerOf(instance).isCustomLoading()).toBe(false); + }); + + // The adapter keeps the last operation's flags, so a completed load still reports one. + it('reports the load operation that produced the current rows', async () => { + const { instance } = await createDataGrid({ dataSource: DATA }); + await flushAsync(); + + expect(dataControllerOf(instance).hasLoadOperation()).toBe(true); + }); + + it('pushes sorting down to the data source when a column changes', async () => { + const { instance } = await createDataGrid({ + dataSource: DATA, + columns: [{ dataField: 'id' }, { dataField: 'value' }], + }); + await flushAsync(); + + instance.columnOption('value', 'sortOrder', 'desc'); + await flushAsync(); + + expect(instance.getVisibleRows().map((row) => row.key)).toEqual([3, 2, 1]); + }); + }); + + describe('with no data source', () => { + it('reports loaded', async () => { + const { instance } = await createDataGrid({}); + await flushAsync(); + + expect(dataControllerOf(instance).isLoaded()).toBe(true); + expect(dataControllerOf(instance).hasLoadOperation()).toBe(false); + expect(dataControllerOf(instance).isCustomLoading()).toBe(false); + }); + + it('resolves loadAllItems with nothing', async () => { + const { instance } = await createDataGrid({}); + await flushAsync(); + + const resolved: unknown[][] = []; + dataControllerOf(instance).loadAllItems() + .done((items: unknown[]) => { resolved.push(items); }); + await flushAsync(); + + expect(resolved[0]).toEqual([]); + }); + }); +}); 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 5057cd96c2b0..aed02e5cd726 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 @@ -65,8 +65,6 @@ import { import { generateRowValues } from './utils/row_values'; export class DataController extends modules.Controller { - protected _dataSource?: DataSourceAdapter | null; - protected _items!: ProcessedItem[]; private _cachedProcessedItems!: ProcessedItem[] | null; @@ -159,7 +157,7 @@ export class DataController extends modules.Controller { */ protected _getPagingOptionValue(optionName: PagingOptionName): number { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return this._dataSource![optionName](); + return this.dataSourceController.getAdapter()![optionName](); } protected callbackNames(): string[] { @@ -176,7 +174,6 @@ export class DataController extends modules.Controller { public publicMethods(): string[] { return [ - '_disposeDataSource', 'beginCustomLoading', 'byKey', 'clearFilter', @@ -314,11 +311,13 @@ export class DataController extends modules.Controller { returnDataField?: boolean, excludedColumn: Column | null = null, ): DataFilter { - if (!this._dataSource) { + const dataSourceAdapter = this.dataSourceController.getAdapter(); + + if (!dataSourceAdapter) { return filter; } - let combined: DataFilter = filter ?? this._dataSource.filter(); + let combined: DataFilter = filter ?? dataSourceAdapter.filter(); const isColumnsTypesDefined = this._columnsController.isDataSourceApplied() || this._columnsController.isAllDataTypesDefined(); @@ -331,7 +330,7 @@ export class DataController extends modules.Controller { : combined; } - const isRemoteFiltering = this._dataSource.remoteOperations().filtering || returnDataField; + const isRemoteFiltering = dataSourceAdapter.remoteOperations().filtering || returnDataField; combined = this._columnsController.updateFilter(combined, isRemoteFiltering); @@ -373,8 +372,8 @@ export class DataController extends modules.Controller { // Handlers private readonly customizeStoreLoadOptionsHandler = (e: LoadOperation): void => { const columnsController = this._columnsController; - const dataSource = this._dataSource; - if (!dataSource) { + const dataSourceAdapter = this.dataSourceController.getAdapter(); + if (!dataSourceAdapter) { return; } const { storeLoadOptions } = e; @@ -394,25 +393,26 @@ export class DataController extends modules.Controller { } if (!columnsController.isDataSourceApplied()) { - columnsController.updateColumnDataTypes(dataSource); + columnsController.updateColumnDataTypes(dataSourceAdapter); } this._columnsUpdating = true; try { - columnsController.updateSortingGrouping(dataSource, !this._useSortingGroupingFromColumns); + columnsController + .updateSortingGrouping(dataSourceAdapter, !this._useSortingGroupingFromColumns); } finally { this._columnsUpdating = false; } storeLoadOptions.sort = columnsController.getSortDataSourceParameters(); storeLoadOptions.group = columnsController.getGroupDataSourceParameters(); - dataSource.sort(storeLoadOptions.sort); - dataSource.group(storeLoadOptions.group); + dataSourceAdapter.sort(storeLoadOptions.sort); + dataSourceAdapter.group(storeLoadOptions.group); storeLoadOptions.sort = columnsController - .getSortDataSourceParameters(!dataSource.remoteOperations().sorting); + .getSortDataSourceParameters(!dataSourceAdapter.remoteOperations().sorting); e.group = columnsController - .getGroupDataSourceParameters(!dataSource.remoteOperations().grouping); + .getGroupDataSourceParameters(!dataSourceAdapter.remoteOperations().grouping); }; private updateItemsAfterColumnsChanged(): void { @@ -481,9 +481,11 @@ export class DataController extends modules.Controller { let filterApplied = false; if (changeTypes.sorting || changeTypes.grouping) { - if (this._dataSource && !this._columnsUpdating) { - this._dataSource.group(this._columnsController.getGroupDataSourceParameters()); - this._dataSource.sort(this._columnsController.getSortDataSourceParameters()); + const dataSourceAdapter = this.dataSourceController.getAdapter(); + + if (dataSourceAdapter && !this._columnsUpdating) { + dataSourceAdapter.group(this._columnsController.getGroupDataSourceParameters()); + dataSourceAdapter.sort(this._columnsController.getSortDataSourceParameters()); this.reload(); } } else if (changeTypes.columns) { @@ -516,15 +518,15 @@ export class DataController extends modules.Controller { * @extended: selection */ protected dataChangedHandler(e?: ChangedEvent): void { - const dataSource = this._dataSource; + const dataSourceAdapter = this.dataSourceController.getAdapter(); let isAsyncDataSourceApplying = false; this._useSortingGroupingFromColumns = false; - if (dataSource && !this._isDataSourceApplying) { + if (dataSourceAdapter && !this._isDataSourceApplying) { this._isDataSourceApplying = true; - when(this._columnsController.applyDataSource(dataSource)).done(() => { + when(this._columnsController.applyDataSource(dataSourceAdapter)).done(() => { if (this._isLoading) { this.loadingChangedHandler(false); } @@ -549,7 +551,7 @@ export class DataController extends modules.Controller { errors.log('W1005', this.component.NAME); this.applyFilter(); } else { - this._currentOperationTypes = dataSource.operationTypes(); + this._currentOperationTypes = dataSourceAdapter.operationTypes(); const change: DataChange = isDefined(e) ? { @@ -621,16 +623,16 @@ export class DataController extends modules.Controller { * @extended: state_storing, virtual_scrolling */ protected resetDataSource(): DeferredObj | undefined { - this._initDataSource(); - this._loadDataSource(); + this.rebuildDataSource(); + this.loadDataSourceAdapter(); return undefined; } - protected _initDataSource(): void { - const hadDataSource = !!this._dataSource; + protected rebuildDataSource(): void { + const hadDataSourceAdapter = this.dataSourceController.hasAdapter(); - this._disposeDataSource(); + this.disposeDataSourceAdapter(); const dataSource = this.dataSourceController.createDataSource(); this._useSortingGroupingFromColumns = true; @@ -640,8 +642,8 @@ export class DataController extends modules.Controller { const { isPageIndexChanged } = this.applyPagingOptions(dataSource); this._isPaging = isPageIndexChanged; - this.setDataSource(dataSource); - } else if (hadDataSource) { + this.initDataSourceAdapter(dataSource); + } else if (hadDataSourceAdapter) { this.updateItems(); } } @@ -649,13 +651,13 @@ export class DataController extends modules.Controller { /** * @extended: selection, virtual_scrolling */ - protected _loadDataSource(): DeferredObj { - const dataSource = this._dataSource; + protected loadDataSourceAdapter(): DeferredObj { + const dataSourceAdapter = this.dataSourceController.getAdapter(); const result: DeferredObj = Deferred(); when(this._columnsController.refresh(true)).always(() => { - if (dataSource) { - dataSource.load().done((...args: unknown[]) => { + if (dataSourceAdapter) { + dataSourceAdapter.load().done((...args: unknown[]) => { this._isPaging = false; result.resolve(...args); }).fail((...args: unknown[]) => { result.reject(...args); }); @@ -1050,7 +1052,7 @@ export class DataController extends modules.Controller { change.operationTypes ??= this._currentOperationTypes; this._currentOperationTypes = null; - if (!this._dataSource) { + if (!this.dataSourceController.hasAdapter()) { this._items = []; return; } @@ -1081,7 +1083,8 @@ export class DataController extends modules.Controller { // change.items at this stage is defined only if virtualScrolling // + legacyScrollingMode enabled // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const items = (change.items ?? this._dataSource!.items()) as RawItemData[]; + const adapterItems = this.dataSourceController.getAdapter()!.items(); + const items = (change.items ?? adapterItems) as RawItemData[]; const dataItems = this._beforeProcessItems(items); const processedItems = this._processItems(dataItems, change); @@ -1187,10 +1190,10 @@ export class DataController extends modules.Controller { * @extended: filter_sync, virtual_scrolling */ protected applyFilter(): DeferredObj { - const dataSource = this._dataSource; + const dataSourceAdapter = this.dataSourceController.getAdapter(); - if (dataSource) { - dataSource.pageIndex(0); + if (dataSourceAdapter) { + dataSourceAdapter.pageIndex(0); if (this.option('paging.pageIndex')) { this._silentOption('paging.pageIndex', 0); } @@ -1214,8 +1217,9 @@ export class DataController extends modules.Controller { private filter(filterExpr: DataFilter): void; private filter(...binaryFilterExpr: BinaryDataFilterExpression): void; private filter(...filterArgs: [] | [DataFilter] | BinaryDataFilterExpression): DataFilter | void { - const filter: DataFilter = this._dataSource?.filter(); - const langParams = this._dataSource?.loadOptions?.()?.langParams; + const dataSourceAdapter = this.dataSourceController.getAdapter(); + const filter: DataFilter = dataSourceAdapter?.filter(); + const langParams = dataSourceAdapter?.loadOptions?.()?.langParams; if (filterArgs.length === 0) { return filter; @@ -1227,7 +1231,7 @@ export class DataController extends modules.Controller { return undefined; } - this._dataSource?.filter(filterExpr); + dataSourceAdapter?.filter(filterExpr); this.applyFilter(); return undefined; @@ -1283,7 +1287,7 @@ export class DataController extends modules.Controller { this.dataSourceChanged.fire(); }; - private subscribeToDataSource(dataSourceAdapter: DataSourceAdapter): void { + private subscribeToDataSourceAdapter(dataSourceAdapter: DataSourceAdapter): void { dataSourceAdapter.changed.add(this.dataChangedHandlerProxy); dataSourceAdapter.loadingChanged.add(this.loadingChangedHandler); dataSourceAdapter.loadError.add(this.loadErrorHandlerProxy); @@ -1291,7 +1295,7 @@ export class DataController extends modules.Controller { dataSourceAdapter.changing.add(this.changingHandler); } - private unsubscribeFromDataSource(dataSourceAdapter: DataSourceAdapter): void { + private unsubscribeFromDataSourceAdapter(dataSourceAdapter: DataSourceAdapter): void { dataSourceAdapter.changed.remove(this.dataChangedHandlerProxy); dataSourceAdapter.loadingChanged.remove(this.loadingChangedHandler); dataSourceAdapter.loadError.remove(this.loadErrorHandlerProxy); @@ -1299,17 +1303,15 @@ export class DataController extends modules.Controller { dataSourceAdapter.changing.remove(this.changingHandler); } - private setDataSource(dataSource: DataSource): void { + private initDataSourceAdapter(dataSource: DataSource): void { const dataSourceAdapter = this.dataSourceController.createAdapter(dataSource); - this._dataSource = dataSourceAdapter; - this._isLoading = !dataSourceAdapter.isLoaded(); this._needApplyFilter = true; this._isAllDataTypesDefined = this._columnsController.isAllDataTypesDefined(); this.changed.add(this.fireDataSourceChanged); - this.subscribeToDataSource(dataSourceAdapter); + this.subscribeToDataSourceAdapter(dataSourceAdapter); } /** @@ -1332,9 +1334,9 @@ export class DataController extends modules.Controller { skipFilter = false, ): LoadAllItemsDeferred { const d = Deferred() as LoadAllItemsDeferred; - const dataSource = this._dataSource; + const dataSourceAdapter = this.dataSourceController.getAdapter(); - if (!dataSource) { + if (!dataSourceAdapter) { d.resolve([]); return d; } @@ -1344,15 +1346,15 @@ export class DataController extends modules.Controller { }; if (data) { - dataSource.customLoader.processLoadedData(data, { + dataSourceAdapter.customLoader.processLoadedData(data, { filter: skipFilter ? null : this.getCombinedFilter(), - group: dataSource.group(), - sort: dataSource.sort(), + group: dataSourceAdapter.group(), + sort: dataSourceAdapter.sort(), }) .done(resolveLoaded) .fail(d.reject as (...args: unknown[]) => void); - } else if (!dataSource.isLoading()) { - dataSource.customLoader.loadAll() + } else if (!dataSourceAdapter.isLoading()) { + dataSourceAdapter.customLoader.loadAll() .done(resolveLoaded) .fail(d.reject as (...args: unknown[]) => void); } else { @@ -1426,16 +1428,16 @@ export class DataController extends modules.Controller { } private changePaging(optionName: PagingOptionName, value?: number): PagingResult { - const dataSource = this._dataSource; + const dataSourceAdapter = this.dataSourceController.getAdapter(); - if (!dataSource) { + if (!dataSourceAdapter) { return optionName === 'pageIndex' && value !== undefined ? Deferred().resolve().promise() : 0; } if (value === undefined) { - return dataSource[optionName](); + return dataSourceAdapter[optionName](); } const oldValue = this._getPagingOptionValue(optionName); @@ -1446,19 +1448,19 @@ export class DataController extends modules.Controller { this._skipProcessingPagingChange = true; try { if (optionName === 'pageSize' && value === 0) { - dataSource.pageIndex(0); + dataSourceAdapter.pageIndex(0); this.option('paging.pageIndex', 0); } - dataSource[optionName](value); + dataSourceAdapter[optionName](value); this.option(`paging.${optionName}`, value); } finally { this._skipProcessingPagingChange = false; } - const pageIndex = dataSource.pageIndex(); + const pageIndex = dataSourceAdapter.pageIndex(); this._isPaging = optionName === 'pageIndex'; - const loadResult: DeferredObj = dataSource[optionName === 'pageIndex' ? 'load' : 'reload'](); + const loadResult: DeferredObj = dataSourceAdapter[optionName === 'pageIndex' ? 'load' : 'reload'](); return loadResult.done(() => { this._isPaging = false; @@ -1482,7 +1484,9 @@ export class DataController extends modules.Controller { } public isCustomLoading(): boolean { - return this._isCustomLoading || !!this._dataSource?.customLoader.isLoading(); + const customLoader = this.dataSourceController.getAdapter()?.customLoader; + + return this._isCustomLoading || !!customLoader?.isLoading(); } public beginCustomLoading(messageText?: string): void { @@ -1538,21 +1542,20 @@ export class DataController extends modules.Controller { return this.items(); } - protected _disposeDataSource(): void { - const oldDataSource = this._dataSource; + protected disposeDataSourceAdapter(): void { + const dataSourceAdapter = this.dataSourceController.getAdapter(); - if (oldDataSource) { + if (dataSourceAdapter) { // Before unsubscribing: cancelling in-flight loads still notifies this controller. - oldDataSource.cancelAll(); - this.unsubscribeFromDataSource(oldDataSource); + dataSourceAdapter.cancelAll(); + this.unsubscribeFromDataSourceAdapter(dataSourceAdapter); } - this._dataSource = null; this.dataSourceController.disposeAdapter(); } public dispose(): void { - this._disposeDataSource(); + this.disposeDataSourceAdapter(); super.dispose(); } @@ -1598,7 +1601,7 @@ export class DataController extends modules.Controller { } public load(): DeferredObj { - return this._dataSource?.load() as DeferredObj; + return this.dataSourceController.getAdapter()?.load() as DeferredObj; } /** @@ -1606,18 +1609,19 @@ export class DataController extends modules.Controller { */ public reload(reload?: boolean, changesOnly?: boolean): DeferredObj { - return this._dataSource?.reload(reload, changesOnly) as DeferredObj; + return this.dataSourceController.getAdapter() + ?.reload(reload, changesOnly) as DeferredObj; } /** * @extended: state_storing */ public isLoaded(): boolean { - return (this._dataSource ? this._dataSource.isLoaded() : true); + return this.dataSourceController.isLoaded(); } public hasLoadOperation(): boolean { - const operationTypes = this._dataSource?.operationTypes() ?? {}; + const operationTypes = this.dataSourceController.operationTypes() ?? {}; return Object.keys(operationTypes).some((type) => operationTypes[type]); } diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_source/__tests__/data_source_controller.integration.test.ts b/packages/devextreme/js/__internal/grids/grid_core/data_source/__tests__/data_source_controller.integration.test.ts index a90d6dafe66c..8e2d721dc6fe 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_source/__tests__/data_source_controller.integration.test.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_source/__tests__/data_source_controller.integration.test.ts @@ -13,7 +13,6 @@ import { beforeTest, createDataGrid, flushAsync, - getMirroredAdapter, } from '@ts/grids/grid_core/__tests__/__mock__/helpers/utils'; import type { InternalGrid } from '@ts/grids/grid_core/m_types'; @@ -84,14 +83,13 @@ describe('dataSource controller holds the adapter', () => { beforeEach(beforeTest); afterEach(afterTest); - it('holds the same adapter object as DataController', async () => { + it('holds an adapter once a data source is set', async () => { const { instance } = await createDataGrid({ dataSource: DATA }); const dataSourceController = instance.getController('dataSource'); - const adapter = getMirroredAdapter(instance); - expect(adapter).toBeTruthy(); expect(dataSourceController.hasAdapter()).toBe(true); - expect(dataSourceController.getAdapter()).toBe(adapter); + expect(dataSourceController.getAdapter()).toBeTruthy(); + expect(dataSourceController.store()).toBeTruthy(); }); it('follows the rebuilt adapter when the dataSource option changes', async () => { @@ -103,7 +101,7 @@ describe('dataSource controller holds the adapter', () => { await flushAsync(); expect(dataSourceController.getAdapter()).not.toBe(firstAdapter); - expect(dataSourceController.getAdapter()).toBe(getMirroredAdapter(instance)); + expect(instance.getVisibleRows()).toHaveLength(OTHER_DATA.length); }); it('releases the adapter when the dataSource option is cleared', async () => { @@ -115,7 +113,6 @@ describe('dataSource controller holds the adapter', () => { expect(dataSourceController.hasAdapter()).toBe(false); expect(dataSourceController.getAdapter()).toBeNull(); - expect(getMirroredAdapter(instance)).toBeNull(); expect(dataSourceController.getDataSource()).toBeNull(); expect(dataSourceController.store()).toBeUndefined(); }); @@ -130,19 +127,20 @@ describe('dataSource controller holds the adapter', () => { await flushAsync(); expect(dataSourceController.hasAdapter()).toBe(true); - expect(dataSourceController.getAdapter()).toBe(getMirroredAdapter(instance)); + expect(instance.getVisibleRows()).toHaveLength(OTHER_DATA.length); }); it('still holds the same adapter after a refresh', async () => { const { instance } = await createDataGrid({ dataSource: DATA }); const dataSourceController = instance.getController('dataSource'); + const adapterBefore = dataSourceController.getAdapter(); const refreshed = instance.refresh(); await flushAsync(); await refreshed; expect(dataSourceController.hasAdapter()).toBe(true); - expect(dataSourceController.getAdapter()).toBe(getMirroredAdapter(instance)); + expect(dataSourceController.getAdapter()).toBe(adapterBefore); }); it('releases the adapter on dispose', async () => { @@ -152,8 +150,8 @@ describe('dataSource controller holds the adapter', () => { instance.dispose(); $container.remove(); - expect(getMirroredAdapter(instance)).toBeNull(); expect(dataSourceController.hasAdapter()).toBe(false); + expect(dataSourceController.getAdapter()).toBeNull(); }); }); diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_source/data_source_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/data_source/data_source_controller.ts index 91cbb86c07b9..6569b800fe18 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_source/data_source_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_source/data_source_controller.ts @@ -157,6 +157,10 @@ export class DataSourceController< return this.adapter?.loadingOperationTypes() ?? {}; } + public isLoaded(): boolean { + return this.adapter ? this.adapter.isLoaded() : true; + } + public isLoading(): boolean { return this.adapter?.isLoading() ?? false; } diff --git a/packages/devextreme/js/__internal/grids/grid_core/focus/__tests__/focus_navigation.integration.test.ts b/packages/devextreme/js/__internal/grids/grid_core/focus/__tests__/focus_navigation.integration.test.ts new file mode 100644 index 000000000000..1375fef6e512 --- /dev/null +++ b/packages/devextreme/js/__internal/grids/grid_core/focus/__tests__/focus_navigation.integration.test.ts @@ -0,0 +1,119 @@ +import { + afterEach, beforeEach, describe, expect, it, +} from '@jest/globals'; + +import { + afterTest, + beforeTest, + createDataGrid, + flushAsync, +} from '../../__tests__/__mock__/helpers/utils'; + +const DATA = Array.from({ length: 12 }, (_, i) => ({ + id: i + 1, + group: i < 6 ? 'A' : 'B', + value: `row ${i + 1}`, +})); + +describe('Focus — navigating to the focused row', () => { + beforeEach(beforeTest); + afterEach(afterTest); + + describe('flat data', () => { + it('moves the page to the focused row', async () => { + const { instance } = await createDataGrid({ + dataSource: DATA, + focusedRowEnabled: true, + paging: { pageSize: 4 }, + }); + await flushAsync(); + + instance.option('focusedRowKey', 10); + await flushAsync(); + + expect(instance.pageIndex()).toBe(2); + expect(instance.option('focusedRowIndex')).toBe(1); + }); + + it('stays on the page that already holds the focused row', async () => { + const { instance } = await createDataGrid({ + dataSource: DATA, + focusedRowEnabled: true, + paging: { pageSize: 4 }, + }); + await flushAsync(); + + instance.option('focusedRowKey', 2); + await flushAsync(); + + expect(instance.pageIndex()).toBe(0); + expect(instance.option('focusedRowIndex')).toBe(1); + }); + + it('reports no focused row for a key that does not exist', async () => { + const { instance } = await createDataGrid({ + dataSource: DATA, + focusedRowEnabled: true, + paging: { pageSize: 4 }, + }); + await flushAsync(); + + instance.option('focusedRowKey', 999); + await flushAsync(); + + expect(instance.option('focusedRowIndex')).toBe(-1); + }); + + it('places the focused row by the applied sort order, not by data order', async () => { + const { instance } = await createDataGrid({ + dataSource: DATA, + focusedRowEnabled: true, + columns: [{ dataField: 'id', sortOrder: 'desc' }, 'value'], + paging: { pageSize: 4 }, + }); + await flushAsync(); + + // Descending, id 10 is the third row overall, so it sits on the first page. + instance.option('focusedRowKey', 10); + await flushAsync(); + + expect(instance.pageIndex()).toBe(0); + expect(instance.option('focusedRowIndex')).toBe(2); + }); + + it('honours a composite key when building the row filter', async () => { + const { instance } = await createDataGrid({ + dataSource: DATA, + keyExpr: ['group', 'id'], + focusedRowEnabled: true, + paging: { pageSize: 4 }, + }); + await flushAsync(); + + instance.option('focusedRowKey', { group: 'B', id: 10 }); + await flushAsync(); + + expect(instance.pageIndex()).toBe(2); + expect(instance.option('focusedRowIndex')).toBe(1); + }); + }); + + describe('grouped data', () => { + it('expands the group holding the focused row and moves to its page', async () => { + const { instance } = await createDataGrid({ + dataSource: DATA, + focusedRowEnabled: true, + columns: [{ dataField: 'group', groupIndex: 0 }, 'id', 'value'], + grouping: { autoExpandAll: false }, + paging: { pageSize: 4 }, + }); + await flushAsync(); + + instance.option('focusedRowKey', 10); + await flushAsync(); + + expect(instance.isRowExpanded(['B'])).toBe(true); + expect(instance.getVisibleRows().some((row) => row.key === 10)).toBe(true); + }); + }); +}); diff --git a/packages/devextreme/js/__internal/grids/grid_core/focus/m_focus.ts b/packages/devextreme/js/__internal/grids/grid_core/focus/m_focus.ts index 8d8cfca5fda1..1b0bf9024687 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/focus/m_focus.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/focus/m_focus.ts @@ -605,7 +605,7 @@ export const focusDataControllerExtender = ( const forceUpdateFocusedRow = this.dataSourceController.consumeDataPushed(); - if (this.option('focusedRowEnabled') && this._dataSource) { + if (this.option('focusedRowEnabled') && this.dataSourceController.hasAdapter()) { const isPartialUpdate = e.changeType === 'update' && e.repaintChangesOnly; const isPartialUpdateWithDeleting = isPartialUpdate && !!e.changeTypes && e.changeTypes.indexOf('remove') >= 0; const isRefreshWithItems = e.changeType === 'refresh' && !!e.items.length; @@ -722,7 +722,7 @@ export const focusDataControllerExtender = ( } private getGlobalRowIndexByKey(key) { - if (this._dataSource!.group()) { + if (this.dataSourceController.getAdapter()!.group()) { // @ts-expect-error return this._calculateGlobalRowIndexByGroupedData(key); } @@ -733,7 +733,7 @@ export const focusDataControllerExtender = ( protected _calculateGlobalRowIndexByFlatData(key, groupFilter, useGroup) { // @ts-expect-error const deferred = new Deferred(); - const dataSource = this._dataSource!; + const dataSourceAdapter = this.dataSourceController.getAdapter()!; if (Array.isArray(key) || isNewRowTempKey(key)) { return deferred.resolve(-1).promise(); @@ -741,25 +741,25 @@ export const focusDataControllerExtender = ( let filter = this._generateFilterByKey(key); - dataSource.customLoader.load({ + dataSourceAdapter.customLoader.load({ filter: this._concatWithCombinedFilter(filter), skip: 0, take: 1, }).done(({ data }) => { - if (this._dataSource !== dataSource) { + if (this.dataSourceController.getAdapter() !== dataSourceAdapter) { deferred.resolve(-1); return; } if (data.length > 0) { filter = this._generateOperationFilterByKey(key, data[0], useGroup); - dataSource.customLoader.load({ + dataSourceAdapter.customLoader.load({ filter: this._concatWithCombinedFilter(filter, groupFilter), skip: 0, take: 1, requireTotalCount: true, }).done(({ extra }) => { - if (this._dataSource !== dataSource) { + if (this.dataSourceController.getAdapter() !== dataSourceAdapter) { deferred.resolve(-1); return; } @@ -797,17 +797,17 @@ export const focusDataControllerExtender = ( // TODO Vinogradov: Move this method implementation to the UiGridCoreFocusUtils // and cover with unit tests. private _generateOperationFilterByKey(key, rowData, useGroup) { - const that = this; - const dateSerializationFormat = that.option('dateSerializationFormat'); - const isRemoteFiltering = that._dataSource!.remoteOperations().filtering; - const isRemoteSorting = that._dataSource!.remoteOperations().sorting; + const dateSerializationFormat = this.option('dateSerializationFormat'); + const remoteOperations = this.dataSourceController.remoteOperations(); + const isRemoteFiltering = remoteOperations.filtering; + const isRemoteSorting = remoteOperations.sorting; - let filter = that._generateFilterByKey(key, '<'); + let filter = this._generateFilterByKey(key, '<'); // @ts-expect-error - let sort = that._columnsController.getSortDataSourceParameters(!isRemoteFiltering, true); + let sort = this._columnsController.getSortDataSourceParameters(!isRemoteFiltering, true); if (useGroup) { - const group = that._columnsController.getGroupDataSourceParameters(!isRemoteFiltering); + const group = this._columnsController.getGroupDataSourceParameters(!isRemoteFiltering); if (group) { sort = sort ? group.concat(sort) : group; } @@ -822,14 +822,14 @@ export const focusDataControllerExtender = ( { isRemoteFiltering, dateSerializationFormat, - getSelector: (selector) => that._columnsController.columnOption(selector, 'selector'), + getSelector: (selector) => this._columnsController.columnOption(selector, 'selector'), }, ); filter = [[selector, '=', safeValue], 'and', filter]; if (rawValue === null || isBoolean(rawValue)) { - const booleanFilter = that._generateBooleanFilter(selector, safeValue, desc); + const booleanFilter = this._generateBooleanFilter(selector, safeValue, desc); if (booleanFilter) { filter = [booleanFilter, 'or', filter]; @@ -861,7 +861,7 @@ export const focusDataControllerExtender = ( } protected _generateFilterByKey(key, operation?) { - const dataSourceKey = this._dataSource!.key(); + const dataSourceKey = this.dataSourceController.getAdapter()!.key(); let filter: any = []; if (!operation) { diff --git a/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts b/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts index 971de5eadb7f..f583cd6ff114 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts @@ -352,15 +352,6 @@ export default { equalFilterParameters, - proxyMethod(instance, methodName, defaultResult?) { - if (!instance[methodName]) { - instance[methodName] = function () { - const dataSource = this._dataSource; - return dataSource ? dataSource[methodName].apply(dataSource, arguments) : defaultResult; - }; - } - }, - formatValue, getFormatOptionsByColumn(column, target) { diff --git a/packages/devextreme/js/__internal/grids/grid_core/selection/extenders/selection_data_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/selection/extenders/selection_data_controller.ts index 8941c783a30c..cf6e616bfbb3 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/selection/extenders/selection_data_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/selection/extenders/selection_data_controller.ts @@ -28,8 +28,8 @@ export const selectionDataControllerExtender = ( } } - protected _loadDataSource(): DeferredObj { - return super._loadDataSource().always(() => { + protected loadDataSourceAdapter(): DeferredObj { + return super.loadDataSourceAdapter().always(() => { this._selectionController.refresh(); }); } diff --git a/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/extenders/__tests__/virtual_scrolling_data_controller.viewport.test.ts b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/extenders/__tests__/virtual_scrolling_data_controller.viewport.test.ts new file mode 100644 index 000000000000..9cf566426739 --- /dev/null +++ b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/extenders/__tests__/virtual_scrolling_data_controller.viewport.test.ts @@ -0,0 +1,106 @@ +import { + afterEach, beforeEach, describe, expect, it, +} from '@jest/globals'; +import type { DataGridScrollMode, Properties } from '@js/ui/data_grid'; +import { + afterTest, + beforeTest, + createDataGrid, + flushAsync, +} from '@ts/grids/grid_core/__tests__/__mock__/helpers/utils'; + +const DATA = Array.from({ length: 40 }, (_, i) => ({ id: i + 1, value: `row ${i + 1}` })); + +const createVirtualGrid = async ( + mode: DataGridScrollMode = 'virtual', + options: Properties = {}, +): ReturnType => { + const grid = await createDataGrid({ + dataSource: DATA, + height: 200, + paging: { pageSize: 10 }, + scrolling: { mode }, + ...options, + }); + await flushAsync(); + return grid; +}; + +describe('Virtual scrolling data controller — viewport reads', () => { + beforeEach(beforeTest); + afterEach(afterTest); + + describe('virtual mode', () => { + it('starts at the first page with no row index offset', async () => { + const { instance } = await createVirtualGrid(); + + expect(instance.pageIndex()).toBe(0); + expect(instance.getController('data').getRowIndexOffset()).toBe(0); + expect(instance.totalCount()).toBe(DATA.length); + }); + + it('reports the loaded page count through the adapter', async () => { + const { instance } = await createVirtualGrid(); + const adapter = instance.getController('dataSource').getAdapter(); + + expect(adapter?.pageIndex()).toBe(0); + expect(instance.pageCount()).toBe(4); + }); + + it('accepts a page change', async () => { + const { instance } = await createVirtualGrid(); + + const paging = instance.pageIndex(2); + await flushAsync(); + await paging; + + expect(instance.pageIndex()).toBe(2); + }); + + it('still renders rows after a page change', async () => { + const { instance } = await createVirtualGrid(); + + const paging = instance.pageIndex(1); + await flushAsync(); + await paging; + + expect(instance.getVisibleRows().length).toBeGreaterThan(0); + expect(instance.totalCount()).toBe(DATA.length); + }); + }); + + describe('infinite mode', () => { + // Infinite mode counts only what it has loaded, so the total tracks the page size. + it('loads the first page only', async () => { + const { instance } = await createVirtualGrid('infinite'); + + expect(instance.pageIndex()).toBe(0); + expect(instance.totalCount()).toBe(10); + expect(instance.getVisibleRows().length).toBeGreaterThan(0); + }); + + it('refreshes without losing the data source', async () => { + const { instance } = await createVirtualGrid('infinite'); + + const refreshing = instance.refresh(); + await flushAsync(); + await refreshing; + + expect(instance.totalCount()).toBe(10); + expect(instance.getVisibleRows().length).toBeGreaterThan(0); + }); + }); + + describe('standard mode is unaffected', () => { + it('pages normally', async () => { + const { instance } = await createVirtualGrid('standard'); + + const paging = instance.pageIndex(3); + await flushAsync(); + await paging; + + expect(instance.pageIndex()).toBe(3); + expect(instance.getVisibleRows().map((row) => row.key)[0]).toBe(31); + }); + }); +}); diff --git a/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/extenders/virtual_scrolling_data_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/extenders/virtual_scrolling_data_controller.ts index 5e15798cddda..ef32b4295ccb 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/extenders/virtual_scrolling_data_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/extenders/virtual_scrolling_data_controller.ts @@ -24,6 +24,7 @@ import type { DataController } from '@ts/grids/grid_core/data_controller/data_co import type { DataChange, PagingOptionName, PagingResult, ProcessedItem, RefreshOptions, } from '@ts/grids/grid_core/data_controller/types'; +import type { DataSourceController } from '@ts/grids/grid_core/data_source/data_source_controller'; import type { RawItemData } from '@ts/grids/grid_core/data_source_adapter/types'; import type { ModuleType, OptionChanged } from '@ts/grids/grid_core/m_types'; import type { VirtualItemsCount } from '@ts/grids/grid_core/virtual_data_loader/types'; @@ -37,7 +38,6 @@ import { import type { VirtualScrollingDataSourceAdapter } from '../m_virtual_scrolling'; import { VirtualScrollController } from '../m_virtual_scrolling_core'; import type { ChangedLoadParams } from '../types'; -import type { GroupCountableDataSource } from '../utils/items'; import { correctCount, isItemCountableByDataSource, @@ -60,7 +60,7 @@ export const virtualScrollingDataControllerExtender = ( ): ModuleType< DataController & VirtualScrollingDataControllerExtension > => class VirtualScrollingDataControllerExtender extends Base { - public declare _dataSource?: VirtualScrollingDataSourceAdapter | null; + protected declare dataSourceController: DataSourceController; // TODO public controller public _rowsScrollController?: VirtualScrollController | null; @@ -95,14 +95,14 @@ export const virtualScrollingDataControllerExtender = ( return baseResult; } - protected _loadDataSource(): DeferredObj { + protected loadDataSourceAdapter(): DeferredObj { if (this._rowsScrollController && isVirtualPaging(this)) { const { loadPageCount } = isDefined(this._loadViewportParams) ? this.getLoadPageParams() : { loadPageCount: 0 }; - loadPageCount >= 1 && this._dataSource?.loadPageCount(loadPageCount); + loadPageCount >= 1 && this.dataSourceController.getAdapter()?.loadPageCount(loadPageCount); } - return super._loadDataSource.apply(this, arguments as any); + return super.loadDataSourceAdapter.apply(this, arguments as any); } private getRowPageSize() { @@ -114,7 +114,7 @@ export const virtualScrollingDataControllerExtender = ( // eslint-disable-next-line @typescript-eslint/no-unused-vars public reload(reload?: boolean, changesOnly?: boolean): DeferredObj { - const rowsScrollController = this._rowsScrollController || this._dataSource; + const rowsScrollController = this._rowsScrollController || this.dataSourceController.getAdapter(); const itemIndex = rowsScrollController?.getItemIndexByPosition(); const result = super.reload.apply(this, arguments as any); return result?.done(() => { @@ -170,7 +170,7 @@ export const virtualScrollingDataControllerExtender = ( this._viewportChanging = false; return; } - this._dataSource?.setViewportItemIndex(this._rowsScrollController!.getViewportItemIndex()); + this.dataSourceController.getAdapter()?.setViewportItemIndex(this._rowsScrollController!.getViewportItemIndex()); }); } @@ -186,7 +186,7 @@ export const virtualScrollingDataControllerExtender = ( private _getRowsScrollDataOptions() { const that = this; const isItemCountable = function (item) { - return isItemCountableByDataSource(item, that._dataSource as unknown as GroupCountableDataSource); + return isItemCountableByDataSource(item, that.dataSourceController.getAdapter()); }; return { @@ -194,7 +194,7 @@ export const virtualScrollingDataControllerExtender = ( return that.getRowPageSize(); }, loadedOffset() { - return isVirtualMode(that) && that._dataSource?.lastLoadOptions().skip || 0; + return isVirtualMode(that) && that.dataSourceController.getAdapter()?.lastLoadOptions().skip || 0; }, loadedItemCount() { return that._itemCount; @@ -263,8 +263,8 @@ export const virtualScrollingDataControllerExtender = ( let result = that._items; if (that.option(LEGACY_SCROLLING_MODE)) { - const dataSource = that._dataSource; - const virtualItemsCount = dataSource?.virtualItemsCount(); + const dataSourceAdapter = that.dataSourceController.getAdapter(); + const virtualItemsCount = dataSourceAdapter?.virtualItemsCount(); const begin = virtualItemsCount ? virtualItemsCount.begin : 0; const rowPageSize = that.getRowPageSize(); @@ -296,13 +296,13 @@ export const virtualScrollingDataControllerExtender = ( onChanged() { }, changingDuration() { - const dataSource = that._dataSource; + const dataSourceAdapter = that.dataSourceController.getAdapter(); - if (dataSource?.isLoading() && that.option(LEGACY_SCROLLING_MODE) !== false) { + if (dataSourceAdapter?.isLoading() && that.option(LEGACY_SCROLLING_MODE) !== false) { return LOAD_TIMEOUT; } - return dataSource?._renderTime || 0; + return dataSourceAdapter?._renderTime || 0; }, }; } @@ -370,8 +370,8 @@ export const virtualScrollingDataControllerExtender = ( const processedItems = super._processItems(items, change); if (this.option(LEGACY_SCROLLING_MODE) === false) { - const dataSource = this._dataSource; - let currentIndex = dataSource?.lastLoadOptions().skip ?? 0; + const dataSourceAdapter = this.dataSourceController.getAdapter(); + let currentIndex = dataSourceAdapter?.lastLoadOptions().skip ?? 0; let prevCountable; let prevRowType; let isPrevRowNew; @@ -380,7 +380,7 @@ export const virtualScrollingDataControllerExtender = ( processedItems.forEach((item) => { const { rowType } = item; - const itemCountable = isItemCountableByDataSource(item, dataSource as unknown as GroupCountableDataSource); + const itemCountable = isItemCountableByDataSource(item, dataSourceAdapter); const isNextGroupItem = rowType === 'group' && (prevCountable || (prevRowType !== 'group' && currentIndex > 0)); const isNextDataItem = rowType === 'data' && itemCountable && (prevCountable || prevRowType !== 'group'); @@ -414,7 +414,9 @@ export const virtualScrollingDataControllerExtender = ( } protected _afterProcessItems(processedItems: ProcessedItem[]): ProcessedItem[] { - this._itemCount = processedItems.filter((item) => isItemCountableByDataSource(item, this._dataSource as unknown as GroupCountableDataSource)).length; + const dataSourceAdapter = this.dataSourceController.getAdapter(); + + this._itemCount = processedItems.filter((item) => isItemCountableByDataSource(item, dataSourceAdapter)).length; if (isDefined(this._loadViewportParams)) { this._updateLoadViewportParams(); @@ -449,7 +451,9 @@ export const virtualScrollingDataControllerExtender = ( if (removeCount) { const fromEnd = changeType === 'prepend'; - removeCount = correctCount(that._items, removeCount, fromEnd, (item, isNextAfterLast) => isItemCountableByDataSource(item, that._dataSource as unknown as GroupCountableDataSource) || (item.rowType === 'group' && isNextAfterLast)); + const dataSourceAdapter = that.dataSourceController.getAdapter(); + + removeCount = correctCount(that._items, removeCount, fromEnd, (item, isNextAfterLast) => isItemCountableByDataSource(item, dataSourceAdapter) || (item.rowType === 'group' && isNextAfterLast)); change.removeCount = removeCount; } @@ -493,7 +497,7 @@ export const virtualScrollingDataControllerExtender = ( public getRowIndexOffset(byLoadedRows?, needGroupOffset?) { let offset = 0; - const dataSource = this._dataSource; + const dataSourceAdapter = this.dataSourceController.getAdapter(); const rowsScrollController = this._rowsScrollController; const newMode = this.option(LEGACY_SCROLLING_MODE) === false; const virtualPaging = isVirtualPaging(this); @@ -510,8 +514,8 @@ export const virtualScrollingDataControllerExtender = ( } else { offset = rowsScrollController.beginPageIndex() * rowsScrollController.pageSize(); } - } else if (virtualPaging && newMode && dataSource) { - const lastLoadOptions = dataSource.lastLoadOptions(); + } else if (virtualPaging && newMode && dataSourceAdapter) { + const lastLoadOptions = dataSourceAdapter.lastLoadOptions(); const { skips } = lastLoadOptions as { skips?: number[] }; if (needGroupOffset && skips?.length) { @@ -519,8 +523,8 @@ export const virtualScrollingDataControllerExtender = ( } else { offset = lastLoadOptions.skip ?? 0; } - } else if (isVirtualMode(this) && dataSource) { - offset = dataSource.beginPageIndex() * dataSource.pageSize(); + } else if (isVirtualMode(this) && dataSourceAdapter) { + offset = dataSourceAdapter.beginPageIndex() * dataSourceAdapter.pageSize(); } return offset; @@ -541,7 +545,7 @@ export const virtualScrollingDataControllerExtender = ( private viewportSize(size?) { const rowsScrollController = this._rowsScrollController; - const dataSource = this._dataSource; + const dataSourceAdapter = this.dataSourceController.getAdapter(); // @ts-expect-error const result = rowsScrollController?.viewportSize(size); @@ -549,7 +553,7 @@ export const virtualScrollingDataControllerExtender = ( return result; } - return dataSource?.viewportSize(size); + return dataSourceAdapter?.viewportSize(size); } private viewportHeight(height, scrollTop) { @@ -558,7 +562,7 @@ export const virtualScrollingDataControllerExtender = ( private viewportItemSize(size?) { const rowsScrollController = this._rowsScrollController; - const dataSource = this._dataSource; + const dataSourceAdapter = this.dataSourceController.getAdapter(); // @ts-expect-error const result = rowsScrollController?.viewportItemSize(size); @@ -566,32 +570,32 @@ export const virtualScrollingDataControllerExtender = ( return result; } - return dataSource?.viewportItemSize(size); + return dataSourceAdapter?.viewportItemSize(size); } private setViewportPosition(position?) { const rowsScrollController = this._rowsScrollController; - const dataSource = this._dataSource; + const dataSourceAdapter = this.dataSourceController.getAdapter(); this._isPaging = false; if (rowsScrollController) { // @ts-expect-error rowsScrollController.setViewportPosition(position); } else { - dataSource?.setViewportPosition(position); + dataSourceAdapter?.setViewportPosition(position); } } private setContentItemSizes(sizes) { const rowsScrollController = this._rowsScrollController; - const dataSource = this._dataSource; + const dataSourceAdapter = this.dataSourceController.getAdapter(); const result = rowsScrollController?.setContentItemSizes(sizes); if (this.option(LEGACY_SCROLLING_MODE) === false) { return result; } - return dataSource?.setContentItemSizes(sizes); + return dataSourceAdapter?.setContentItemSizes(sizes); } private getPreloadedRowCount() { @@ -610,14 +614,14 @@ export const virtualScrollingDataControllerExtender = ( private getLoadPageParams(byLoadedPage?) { const pageSize = this.pageSize(); const viewportParams = this._loadViewportParams; - const lastLoadOptions = this._dataSource?.lastLoadOptions(); + const lastLoadOptions = this.dataSourceController.getAdapter()?.lastLoadOptions(); const loadedPageIndex = lastLoadOptions?.pageIndex || 0; const loadedTake = lastLoadOptions?.take || 0; const isScrollingBack = this._rowsScrollController!.isScrollingBack(); const topPreloadCount = isScrollingBack ? this.getPreloadedRowCount() : 0; const bottomPreloadCount = isScrollingBack ? 0 : this.getPreloadedRowCount(); - const totalCountCorrection = this._dataSource?.totalCountCorrection() || 0; + const totalCountCorrection = this.dataSourceController.getAdapter()?.totalCountCorrection() || 0; const skipWithPreload = Math.max(0, viewportParams.skip - topPreloadCount); const pageIndex = byLoadedPage ? loadedPageIndex : Math.floor(pageSize ? skipWithPreload / pageSize : 0); const pageOffset = pageIndex * pageSize; @@ -681,7 +685,7 @@ export const virtualScrollingDataControllerExtender = ( } private isAllLoadedInAppendMode(): boolean { - const loadedItemCount = this.pageSize() * (this._dataSource?.loadPageCount() ?? 0); + const loadedItemCount = this.pageSize() * (this.dataSourceController.getAdapter()?.loadPageCount() ?? 0); return isInfiniteMode(this) && this.dataSourceController.totalItemsCount() < loadedItemCount; } @@ -689,7 +693,7 @@ export const virtualScrollingDataControllerExtender = ( // T1326786: the grid is scrolled to paging.pageIndex on the first resize only, // until then the viewport is at the top and the loaded page is below it private isScrollToPagePending(changedParams: ChangedLoadParams): boolean { - const loadedPageIndex = this._dataSource?.pageIndex() ?? 0; + const loadedPageIndex = this.dataSourceController.getAdapter()?.pageIndex() ?? 0; const viewportIsAtTop = this._rowsScrollController?.getViewportItemIndex() === 0; const pageIndexAfterViewport = changedParams.pageIndex + changedParams.loadPageCount; @@ -710,7 +714,7 @@ export const virtualScrollingDataControllerExtender = ( return false; } - const loadedPageIndex = this._dataSource?.pageIndex() ?? 0; + const loadedPageIndex = this.dataSourceController.getAdapter()?.pageIndex() ?? 0; if (changedParams.pageIndex > loadedPageIndex) { // T1049853 return true; @@ -724,7 +728,7 @@ export const virtualScrollingDataControllerExtender = ( } private _loadItems(checkLoading: boolean, viewportIsFilled: boolean): boolean { - if (!this._dataSource) { + if (!this.dataSourceController.getAdapter()) { return false; } @@ -750,8 +754,10 @@ export const virtualScrollingDataControllerExtender = ( } private loadPages(changedParams: ChangedLoadParams): void { - this._dataSource!.pageIndex(changedParams.pageIndex); - this._dataSource!.loadPageCount(changedParams.loadPageCount); + const dataSourceAdapter = this.dataSourceController.getAdapter()!; + + dataSourceAdapter.pageIndex(changedParams.pageIndex); + dataSourceAdapter.loadPageCount(changedParams.loadPageCount); this._repaintChangesOnly = true; this._needUpdateDimensions = true; @@ -787,7 +793,7 @@ export const virtualScrollingDataControllerExtender = ( this._updateLoadViewportParams(); const loadingItemsStarted = this._loadItems(checkLoading, !viewportIsNotFilled); - const isCustomLoading = this._dataSource?.customLoader.isLoading(); + const isCustomLoading = this.dataSourceController.getAdapter()?.customLoader.isLoading(); const isLoading = checkLoading && !isCustomLoading && this._isLoading; const needToUpdateItems = !(loadingItemsStarted || isLoading @@ -831,8 +837,8 @@ export const virtualScrollingDataControllerExtender = ( const rowsScrollController = this._rowsScrollController; rowsScrollController?.loadIfNeed(); - const dataSource = this._dataSource; - return dataSource?.loadIfNeed(); + const dataSourceAdapter = this.dataSourceController.getAdapter(); + return dataSourceAdapter?.loadIfNeed(); } private getItemSize() { @@ -843,8 +849,8 @@ export const virtualScrollingDataControllerExtender = ( return rowsScrollController.getItemSize(); } - const dataSource = this._dataSource; - return dataSource?.getItemSize(); + const dataSourceAdapter = this.dataSourceController.getAdapter(); + return dataSourceAdapter?.getItemSize(); } private getItemSizes() { @@ -855,8 +861,8 @@ export const virtualScrollingDataControllerExtender = ( return rowsScrollController.getItemSizes(); } - const dataSource = this._dataSource; - return dataSource?.getItemSizes(); + const dataSourceAdapter = this.dataSourceController.getAdapter(); + return dataSourceAdapter?.getItemSizes(); } private getContentOffset(type?) { @@ -867,15 +873,15 @@ export const virtualScrollingDataControllerExtender = ( return rowsScrollController.getContentOffset(type); } - const dataSource = this._dataSource; - return dataSource?.getContentOffset(type); + const dataSourceAdapter = this.dataSourceController.getAdapter(); + return dataSourceAdapter?.getContentOffset(type); } public refresh(options?: boolean | RefreshOptions): DeferredObj { - const dataSource = this._dataSource; + const dataSourceAdapter = this.dataSourceController.getAdapter(); - if (dataSource && typeof options !== 'boolean' && options?.load && isInfiniteMode(this)) { - dataSource.resetCurrentTotalCount(); + if (dataSourceAdapter && typeof options !== 'boolean' && options?.load && isInfiniteMode(this)) { + dataSourceAdapter.resetCurrentTotalCount(); } return super.refresh.apply(this, arguments as any); @@ -897,7 +903,7 @@ export const virtualScrollingDataControllerExtender = ( return rowsScrollController.virtualItemsCount(); } - return this._dataSource?.virtualItemsCount() as VirtualItemsCount | undefined; + return this.dataSourceController.getAdapter()?.virtualItemsCount() as VirtualItemsCount | undefined; } public pageIndex(): number; @@ -921,7 +927,7 @@ export const virtualScrollingDataControllerExtender = ( const { fullReload, pageIndex } = operationTypes; if (e.isDataChanged && !fullReload && pageIndex) { - this._updateVisiblePageIndex(this._dataSource!.pageIndex()); + this._updateVisiblePageIndex(this.dataSourceController.getAdapter()!.pageIndex()); } } } @@ -984,17 +990,19 @@ export const virtualScrollingDataControllerExtender = ( } protected applyFilter(): DeferredObj { - this._dataSource?.loadPageCount(1); + this.dataSourceController.getAdapter()?.loadPageCount(1); return super.applyFilter(); } private getVirtualContentSize() { - return this._dataSource?.getVirtualContentSize.apply(this._dataSource, arguments as any); + const dataSourceAdapter = this.dataSourceController.getAdapter(); + return dataSourceAdapter?.getVirtualContentSize.apply(dataSourceAdapter, arguments as any); } private setViewportItemIndex() { - return this._dataSource?.setViewportItemIndex.apply(this._dataSource, arguments as any); + const dataSourceAdapter = this.dataSourceController.getAdapter(); + return dataSourceAdapter?.setViewportItemIndex.apply(dataSourceAdapter, arguments as any); } public isViewportChanging(): boolean { 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 f1aa08a5c890..4c92fed9d8de 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 @@ -34,7 +34,6 @@ import { VIRTUAL_ROW_CLASS, } from './const'; import { subscribeToExternalScrollers, VirtualScrollController } from './m_virtual_scrolling_core'; -import type { GroupCountableDataSource } from './utils/items'; import { isItemCountableByDataSource } from './utils/items'; import { isInfiniteMode, isVirtualMode, isVirtualPaging } from './utils/scrolling_mode'; @@ -734,7 +733,7 @@ export const rowsView = (Base: ModuleType) => class VirtualScrollingRo itemSize = 0; } lastLoadIndex = currentItem.loadIndex; - } else if (isItemCountableByDataSource(currentItem, dataSourceAdapter as unknown as GroupCountableDataSource)) { + } else if (isItemCountableByDataSource(currentItem, dataSourceAdapter)) { if (firstCountableItem) { firstCountableItem = false; } else { diff --git a/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/utils/__tests__/items.test.ts b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/utils/__tests__/items.test.ts index ac6f3d39f1a3..da49e881c5f3 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/utils/__tests__/items.test.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/utils/__tests__/items.test.ts @@ -101,6 +101,14 @@ describe('isItemCountableByDataSource', () => { expect(isItemCountableByDataSource(asItem({ rowType: 'group', data: 'countable' }), null)).toBe(false); }); + it('should not count a group row when the data source cannot answer the question', () => { + expect(isItemCountableByDataSource(asItem({ rowType: 'group', data: 'countable' }), { store: () => undefined })).toBe(false); + }); + + it('should not count a group row when the member is not callable', () => { + expect(isItemCountableByDataSource(asItem({ rowType: 'group', data: 'countable' }), { isGroupItemCountable: true })).toBe(false); + }); + it('should still count a data row when there is no data source', () => { expect(isItemCountableByDataSource(asItem({ rowType: 'data', isNewRow: false }), null)).toBe(true); }); diff --git a/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/utils/items.ts b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/utils/items.ts index 17612088f962..3eccfc8e30a9 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/utils/items.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/utils/items.ts @@ -1,6 +1,6 @@ import type { ProcessedItem } from '@ts/grids/grid_core/data_controller/types'; -export interface GroupCountableDataSource { +export interface GroupCountableDataSourceAdapter { isGroupItemCountable: (data: unknown) => boolean; } @@ -28,11 +28,22 @@ export const correctCount = ( return result; }; +// `isGroupItemCountable` is installed on the adapter by the DataGrid grouping module through +// `provider.extend()`, so no adapter type declares it. This is the one place that checks for it. +const asGroupCountableAdapter = ( + dataSourceAdapter: unknown, +): GroupCountableDataSourceAdapter | undefined => ( + typeof (dataSourceAdapter as GroupCountableDataSourceAdapter | undefined)?.isGroupItemCountable === 'function' + ? dataSourceAdapter as GroupCountableDataSourceAdapter + : undefined +); + export const isItemCountableByDataSource = ( item: ProcessedItem, - dataSourceAdapter: GroupCountableDataSource | null | undefined, + dataSourceAdapter: unknown, ): boolean => (item.rowType === 'data' && !item.isNewRow) - || (item.rowType === 'group' && (dataSourceAdapter?.isGroupItemCountable(item.data) ?? false)); + || (item.rowType === 'group' + && (asGroupCountableAdapter(dataSourceAdapter)?.isGroupItemCountable(item.data) ?? false)); export const updateItemIndices = (items: ProcessedItem[]): ProcessedItem[] => { items.forEach((item, index) => { diff --git a/packages/devextreme/js/__internal/grids/tree_list/__tests__/focus_navigation.integration.test.ts b/packages/devextreme/js/__internal/grids/tree_list/__tests__/focus_navigation.integration.test.ts new file mode 100644 index 000000000000..ff4ea488369f --- /dev/null +++ b/packages/devextreme/js/__internal/grids/tree_list/__tests__/focus_navigation.integration.test.ts @@ -0,0 +1,91 @@ +import { + afterEach, beforeEach, describe, expect, it, +} from '@jest/globals'; + +import { flushAsync } from '../../grid_core/__tests__/__mock__/helpers/utils'; +import { + afterTest, + beforeTest, + createTreeList, +} from './__mock__/helpers/utils'; + +// A three-level tree: 1 > 11 > 111, and a sibling branch 2 > 21. +const DATA = [ + { id: 1, parentId: 0, name: 'root A' }, + { id: 11, parentId: 1, name: 'child A1' }, + { id: 111, parentId: 11, name: 'leaf A1a' }, + { id: 2, parentId: 0, name: 'root B' }, + { id: 21, parentId: 2, name: 'child B1' }, +]; + +const visibleKeys = ( + instance: Awaited>['instance'], +): unknown[] => instance.getVisibleRows().map((row) => row.key); + +describe('TreeList focus — navigating to the focused row', () => { + beforeEach(beforeTest); + afterEach(afterTest); + + it('expands every ancestor of the focused row', async () => { + const { instance } = await createTreeList({ + dataSource: DATA, + focusedRowEnabled: true, + autoExpandAll: false, + }); + await flushAsync(); + + expect(visibleKeys(instance)).toEqual([1, 2]); + + instance.option('focusedRowKey', 111); + await flushAsync(); + + expect(instance.isRowExpanded(1)).toBe(true); + expect(instance.isRowExpanded(11)).toBe(true); + expect(visibleKeys(instance)).toContain(111); + }); + + it('leaves an unrelated branch collapsed', async () => { + const { instance } = await createTreeList({ + dataSource: DATA, + focusedRowEnabled: true, + autoExpandAll: false, + }); + await flushAsync(); + + instance.option('focusedRowKey', 111); + await flushAsync(); + + expect(instance.isRowExpanded(2)).toBe(false); + expect(visibleKeys(instance)).not.toContain(21); + }); + + it('reports no focused row for a key that does not exist', async () => { + const { instance } = await createTreeList({ + dataSource: DATA, + focusedRowEnabled: true, + autoExpandAll: false, + }); + await flushAsync(); + + instance.option('focusedRowKey', 999); + await flushAsync(); + + expect(instance.option('focusedRowIndex')).toBe(-1); + }); + + it('focuses a root row without expanding anything', async () => { + const { instance } = await createTreeList({ + dataSource: DATA, + focusedRowEnabled: true, + autoExpandAll: false, + }); + await flushAsync(); + + instance.option('focusedRowKey', 2); + await flushAsync(); + + expect(instance.isRowExpanded(1)).toBe(false); + expect(instance.isRowExpanded(2)).toBe(false); + expect(instance.option('focusedRowIndex')).toBe(1); + }); +}); diff --git a/packages/devextreme/js/__internal/grids/tree_list/data_controller/__tests__/data_controller.node_access.integration.test.ts b/packages/devextreme/js/__internal/grids/tree_list/data_controller/__tests__/data_controller.node_access.integration.test.ts new file mode 100644 index 000000000000..774c9a2e841f --- /dev/null +++ b/packages/devextreme/js/__internal/grids/tree_list/data_controller/__tests__/data_controller.node_access.integration.test.ts @@ -0,0 +1,122 @@ +import { + afterEach, beforeEach, describe, expect, it, +} from '@jest/globals'; + +import { flushAsync } from '../../../grid_core/__tests__/__mock__/helpers/utils'; +import { + afterTest, + beforeTest, + createTreeList, +} from '../../__tests__/__mock__/helpers/utils'; + +// 1 > 11 > 111, and 2 > 21. +const DATA = [ + { id: 1, parentId: 0, name: 'root A' }, + { id: 11, parentId: 1, name: 'child A1' }, + { id: 111, parentId: 11, name: 'leaf A1a' }, + { id: 2, parentId: 0, name: 'root B' }, + { id: 21, parentId: 2, name: 'child B1' }, +]; + +type Instance = Awaited>['instance']; + +const createTree = async ( + options = {}, +): Promise => { + const { instance } = await createTreeList({ dataSource: DATA, autoExpandAll: false, ...options }); + await flushAsync(); + return instance; +}; + +const visibleKeys = (instance: Instance): unknown[] => instance + .getVisibleRows().map((row) => row.key); + +describe('TreeList data controller — node access', () => { + beforeEach(beforeTest); + afterEach(afterTest); + + describe('with a data source', () => { + it('finds a loaded node by key', async () => { + const instance = await createTree({ autoExpandAll: true }); + + expect(instance.getNodeByKey(11)?.data).toEqual(DATA[1]); + expect(instance.getNodeByKey(111)?.parent?.key).toBe(11); + }); + + it('returns undefined for a key that does not exist', async () => { + const instance = await createTree({ autoExpandAll: true }); + + expect(instance.getNodeByKey(999)).toBeUndefined(); + }); + + it('returns the root node with its top-level children', async () => { + const instance = await createTree(); + + expect(instance.getRootNode()?.children?.map((node) => node.key)).toEqual([1, 2]); + }); + + it('walks every loaded node with forEachNode', async () => { + const instance = await createTree({ autoExpandAll: true }); + const keys: unknown[] = []; + + instance.forEachNode((node) => { keys.push(node.key); }); + + expect(keys).toEqual([1, 11, 111, 2, 21]); + }); + + it('reports expansion state and changes it through expandRow/collapseRow', async () => { + const instance = await createTree(); + + expect(instance.isRowExpanded(1)).toBe(false); + expect(visibleKeys(instance)).toEqual([1, 2]); + + const expanding = instance.expandRow(1); + await flushAsync(); + await expanding; + + expect(instance.isRowExpanded(1)).toBe(true); + expect(visibleKeys(instance)).toEqual([1, 11, 2]); + + const collapsing = instance.collapseRow(1); + await flushAsync(); + await collapsing; + + expect(instance.isRowExpanded(1)).toBe(false); + expect(visibleKeys(instance)).toEqual([1, 2]); + }); + + it('loads descendants of a node', async () => { + const instance = await createTree(); + + const loading = instance.loadDescendants([1]); + + // A shallow gate: an array store has the whole tree loaded already, so the only + // observable contract is that the call forwards and hands back the adapter's deferred. + expect(loading).toBeDefined(); + + await flushAsync(); + await loading; + + expect(instance.getNodeByKey(111)).toBeDefined(); + }); + + it('reloads when expandedRowKeys changes', async () => { + const instance = await createTree(); + + instance.option('expandedRowKeys', [1, 11]); + await flushAsync(); + + expect(visibleKeys(instance)).toEqual([1, 11, 111, 2]); + }); + }); + + describe('with no data source', () => { + it('answers node lookups with undefined', async () => { + const { instance } = await createTreeList({}); + await flushAsync(); + + expect(instance.getNodeByKey(1)).toBeUndefined(); + expect(instance.getRootNode()).toBeFalsy(); + }); + }); +}); diff --git a/packages/devextreme/js/__internal/grids/tree_list/data_controller/m_data_controller.ts b/packages/devextreme/js/__internal/grids/tree_list/data_controller/m_data_controller.ts index 6120980c9b71..b4f39bb0b63b 100644 --- a/packages/devextreme/js/__internal/grids/tree_list/data_controller/m_data_controller.ts +++ b/packages/devextreme/js/__internal/grids/tree_list/data_controller/m_data_controller.ts @@ -4,11 +4,11 @@ import { extend } from '@js/core/utils/extend'; import { DataController, dataControllerModule } from '@ts/grids/grid_core/data_controller/data_controller'; import type { RowKey } from '@ts/grids/grid_core/m_types'; -import type { DataSourceAdapterTreeList } from '../data_source_adapter/m_data_source_adapter'; +import type { TreeListDataSourceController } from '../data_source/data_source_controller'; import treeListCore from '../m_core'; export class TreeListDataController extends DataController { - public declare _dataSource?: DataSourceAdapterTreeList | null; + protected declare dataSourceController: TreeListDataSourceController; private _getNodeLevel(node) { let level = -1; @@ -33,7 +33,7 @@ export class TreeListDataController extends DataController { } private _loadOnOptionChange() { - this._dataSource!.load(); + this.dataSourceController.getAdapter()!.load(); } protected isSameRowState(item1, item2): boolean { @@ -84,7 +84,9 @@ export class TreeListDataController extends DataController { } private changeRowExpand(key) { - if (this._dataSource) { + const dataSourceAdapter = this.dataSourceController.getAdapter(); + + if (dataSourceAdapter) { const args: any = { key, }; @@ -93,7 +95,7 @@ export class TreeListDataController extends DataController { this.executeAction(isExpanded ? 'onRowCollapsing' : 'onRowExpanding', args); if (!args.cancel) { - return this._dataSource.changeRowExpand(key).done(() => { + return dataSourceAdapter.changeRowExpand(key).done(() => { this.executeAction(isExpanded ? 'onRowCollapsed' : 'onRowExpanded', args); }); } @@ -104,7 +106,7 @@ export class TreeListDataController extends DataController { } private isRowExpanded(key, cache?) { - return this._dataSource && this._dataSource.isRowExpanded(key, cache); + return this.dataSourceController.getAdapter()?.isRowExpanded(key, cache); } private expandRow(key) { @@ -124,7 +126,7 @@ export class TreeListDataController extends DataController { } private getRootNode() { - return this._dataSource && this._dataSource.getRootNode(); + return this.dataSourceController.getAdapter()?.getRootNode(); } public optionChanged(args) { @@ -143,12 +145,15 @@ export class TreeListDataController extends DataController { args.handled = true; break; case 'expandedRowKeys': - case 'onNodesInitialized': - if (this._dataSource && !this._dataSource._isNodesInitializing && !equalByValue(args.value, args.previousValue)) { + case 'onNodesInitialized': { + const dataSourceAdapter = this.dataSourceController.getAdapter(); + + if (dataSourceAdapter && !dataSourceAdapter._isNodesInitializing && !equalByValue(args.value, args.previousValue)) { this._loadOnOptionChange(); } args.handled = true; break; + } case 'maxFilterLengthInRequest': args.handled = true; break; @@ -158,31 +163,19 @@ export class TreeListDataController extends DataController { } private getNodeByKey(key) { - if (!this._dataSource) { - return; - } - - return this._dataSource.getNodeByKey(key); + return this.dataSourceController.getAdapter()?.getNodeByKey(key); } private getChildNodeKeys(parentKey) { - if (!this._dataSource) { - return; - } - - return this._dataSource.getChildNodeKeys(parentKey); + return this.dataSourceController.getAdapter()?.getChildNodeKeys(parentKey); } private loadDescendants(keys, childrenOnly) { - if (!this._dataSource) { - return; - } - - return this._dataSource.loadDescendants(keys, childrenOnly); + return this.dataSourceController.getAdapter()?.loadDescendants(keys, childrenOnly); } private forEachNode() { - this._dataSource!.forEachNode.apply(this, arguments as any); + this.dataSourceController.getAdapter()!.forEachNode.apply(this, arguments as any); } // Collect keys by walking the loaded node tree (depth-first, parent before @@ -190,7 +183,7 @@ export class TreeListDataController extends DataController { public getAllDataRowKeys(): Promise { const keys: RowKey[] = []; - this._dataSource?.forEachNode((node) => { + this.dataSourceController.getAdapter()?.forEachNode((node) => { keys.push(node.key); }); diff --git a/packages/devextreme/js/__internal/grids/tree_list/data_source/__tests__/data_source_controller.integration.test.ts b/packages/devextreme/js/__internal/grids/tree_list/data_source/__tests__/data_source_controller.integration.test.ts index 329df8827c2e..b455c7a6054e 100644 --- a/packages/devextreme/js/__internal/grids/tree_list/data_source/__tests__/data_source_controller.integration.test.ts +++ b/packages/devextreme/js/__internal/grids/tree_list/data_source/__tests__/data_source_controller.integration.test.ts @@ -2,7 +2,6 @@ import { afterEach, beforeEach, describe, expect, it, jest, } from '@jest/globals'; import errors from '@js/ui/widget/ui.errors'; -import { getMirroredAdapter } from '@ts/grids/grid_core/__tests__/__mock__/helpers/utils'; import { afterTest, @@ -35,12 +34,12 @@ describe('TreeList dataSource controller', () => { expect('forEachNode' in (adapter as object)).toBe(true); }); - it('holds the same adapter object as DataController', async () => { + it('holds the TreeList adapter once a data source is set', async () => { const { instance } = await createTreeList({ dataSource: DATA }); const dataSourceController = instance.getController('dataSource'); expect(dataSourceController.hasAdapter()).toBe(true); - expect(dataSourceController.getAdapter()).toBe(getMirroredAdapter(instance)); + expect(dataSourceController.key()).toBe('id'); }); it('does not warn W1011, because the override does not apply to TreeList', async () => { diff --git a/packages/devextreme/js/__internal/grids/tree_list/m_focus.ts b/packages/devextreme/js/__internal/grids/tree_list/m_focus.ts index d8163f931d51..42f7121734a1 100644 --- a/packages/devextreme/js/__internal/grids/tree_list/m_focus.ts +++ b/packages/devextreme/js/__internal/grids/tree_list/m_focus.ts @@ -2,8 +2,9 @@ import { Deferred } from '@js/core/utils/deferred'; import { focusModule } from '@ts/grids/grid_core/focus/focus_module'; import type { DataController } from '../grid_core/data_controller/data_controller'; +import type { FocusDataSourceControllerExtension } from '../grid_core/focus/extenders/focus_data_source_controller'; import type { ModuleType } from '../grid_core/m_types'; -import type { DataSourceAdapterTreeList } from './data_source_adapter/m_data_source_adapter'; +import type { TreeListDataSourceController } from './data_source/data_source_controller'; import core from './m_core'; function findIndex(items, callback) { @@ -21,7 +22,8 @@ function findIndex(items, callback) { const data = ( Base: ModuleType, ) => class TreeListDataControllerExtender extends focusModule.extenders.controllers.data(Base) { - public declare _dataSource?: DataSourceAdapterTreeList | null; + protected declare dataSourceController: TreeListDataSourceController + & FocusDataSourceControllerExtension; private changeRowExpand(key) { // @ts-expect-error @@ -54,7 +56,7 @@ const data = ( private getParentKey(key) { const that = this; - const dataSource = that._dataSource!; + const dataSourceAdapter = that.dataSourceController.getAdapter()!; // @ts-expect-error const node = that.getNodeByKey(key); // @ts-expect-error @@ -63,13 +65,13 @@ const data = ( if (node) { d.resolve(node.parent ? node.parent.key : undefined); } else { - dataSource.customLoader.load({ - filter: [dataSource.getKeyExpr(), '=', key], + dataSourceAdapter.customLoader.load({ + filter: [dataSourceAdapter.getKeyExpr(), '=', key], }).done((loadResult) => { const parentData = loadResult.data[0]; if (parentData) { - d.resolve(dataSource.parentKeyOf(parentData)); + d.resolve(dataSourceAdapter.parentKeyOf(parentData)); } else { d.resolve(); } @@ -81,16 +83,16 @@ const data = ( private expandAscendants(key) { const that = this; - const dataSource = that._dataSource; + const dataSourceAdapter = that.dataSourceController.getAdapter(); // @ts-expect-error const d = new Deferred(); that.getParentKey(key).done((parentKey) => { - if (dataSource && parentKey !== undefined && parentKey !== that.option('rootValue')) { - dataSource._isNodesInitializing = true; + if (dataSourceAdapter && parentKey !== undefined && parentKey !== that.option('rootValue')) { + dataSourceAdapter._isNodesInitializing = true; // @ts-expect-error that.expandRow(parentKey); - dataSource._isNodesInitializing = false; + dataSourceAdapter._isNodesInitializing = false; that.expandAscendants(parentKey).done(d.resolve).fail(d.reject); } else { d.resolve(); @@ -101,15 +103,15 @@ const data = ( } protected getPageIndexByKey(key) { - const dataSource = this._dataSource!; + const dataSourceAdapter = this.dataSourceController.getAdapter()!; // @ts-expect-error const d = new Deferred(); this.expandAscendants(key).done(() => { - dataSource.customLoader.load({ + dataSourceAdapter.customLoader.load({ parentIds: [], }).done(({ data: nodes }) => { - if (this._dataSource !== dataSource) { + if (this.dataSourceController.getAdapter() !== dataSourceAdapter) { d.resolve(-1); return; } diff --git a/packages/devextreme/js/__internal/grids/tree_list/m_virtual_scrolling.ts b/packages/devextreme/js/__internal/grids/tree_list/m_virtual_scrolling.ts index 5aa29b16ffc8..18a23b940b8f 100644 --- a/packages/devextreme/js/__internal/grids/tree_list/m_virtual_scrolling.ts +++ b/packages/devextreme/js/__internal/grids/tree_list/m_virtual_scrolling.ts @@ -1,6 +1,7 @@ /* eslint-disable max-classes-per-file */ import { extend } from '@js/core/utils/extend'; import type { DataController } from '@ts/grids/grid_core/data_controller/data_controller'; +import type { DataSourceController } from '@ts/grids/grid_core/data_source/data_source_controller'; import type DataSourceAdapter from '@ts/grids/grid_core/data_source_adapter/m_data_source_adapter'; import type { ModuleType } from '@ts/grids/grid_core/m_types'; import gridCoreUtils from '@ts/grids/grid_core/m_utils'; @@ -35,10 +36,10 @@ virtualScrollingModule.extenders.views.rowsView = (Base: ModuleType) = }; virtualScrollingModule.extenders.controllers.data = (Base: ModuleType) => class TreeListVirtualScrollingDataControllerExtender extends virtualScrollingDataControllerExtender(Base) { - public declare _dataSource?: VirtualScrollingDataSourceAdapter | null; + protected declare dataSourceController: DataSourceController; protected _loadOnOptionChange() { - const virtualScrollController = this._dataSource?._virtualScrollController; + const virtualScrollController = this.dataSourceController.getAdapter()?._virtualScrollController; virtualScrollController?.reset(); // @ts-expect-error diff --git a/packages/devextreme/testing/helpers/gridBaseMocks.js b/packages/devextreme/testing/helpers/gridBaseMocks.js index d2134434a152..29e20f41a89c 100644 --- a/packages/devextreme/testing/helpers/gridBaseMocks.js +++ b/packages/devextreme/testing/helpers/gridBaseMocks.js @@ -1231,9 +1231,30 @@ module.exports = function($, gridCore, columnResizingReordering, domUtils, commo }; + // processModules() bound the widget's public methods to the controllers it built. A + // `controllers` override swaps those out afterwards, leaving the widget calling into an + // orphan that never gets init()ed. Re-point each public method the replacement implements. + const replacedControllers = []; + options && options.controllers && $.each(options.controllers, function(name, replacement) { + const original = that._controllers[name]; + if(original && replacement && original !== replacement && original.publicMethods) { + replacedControllers.push({ original: original, replacement: replacement }); + } + }); + options && options.controllers && $.extend(that._controllers, options.controllers); options && options.views && $.extend(that._views, options.views); + $.each(replacedControllers, function(_, pair) { + $.each(pair.original.publicMethods(), function(__, methodName) { + if(typeof pair.replacement[methodName] === 'function') { + that[methodName] = function() { + return pair.replacement[methodName].apply(pair.replacement, arguments); + }; + } + }); + }); + const mockedDataController = options && options.controllers && options.controllers.data; if(mockedDataController && mockedDataController.mockOptions && that._controllers.dataSource) { that._controllers.dataSource.adapter = new exports.MockDataSourceAdapter( diff --git a/packages/devextreme/testing/tests/DevExpress.knockout/dataGrid.tests.js b/packages/devextreme/testing/tests/DevExpress.knockout/dataGrid.tests.js index 3bede41cdede..5a305741e148 100644 --- a/packages/devextreme/testing/tests/DevExpress.knockout/dataGrid.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.knockout/dataGrid.tests.js @@ -166,7 +166,7 @@ moduleWithoutCsp('initialization from dataSource', { { name: ko.observable('Tom'), age: ko.observable(18), birthDate: ko.observable(new Date(1992, 8, 14)) } ]; const dataSource = new DataSource(this.array); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.applyOptions({ diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/columnsHeadersView.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/columnsHeadersView.tests.js index 290c388247f1..6730771bf592 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/columnsHeadersView.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/columnsHeadersView.tests.js @@ -427,7 +427,7 @@ QUnit.module('Headers', { const dataSource = new DataSource([]); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); // act this.columnHeadersView.render(testElement); 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 246015e62d13..0ce178eab8e8 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 @@ -87,7 +87,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod const dataSource = createDataSource(array, { key: 'name' }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); assert.equal(this.dataController.items().length, 2); @@ -106,7 +106,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod const dataSource = createDataSource(array); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); assert.equal(this.dataController.items().length, 2); @@ -125,7 +125,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod this.applyOptions({ columns: ['name', { dataField: 'age', visible: false }, 'phone'] }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); assert.equal(this.dataController.items().length, 2); @@ -192,7 +192,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod assert.strictEqual(changedCount, 0); // act - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // assert @@ -218,7 +218,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod // assert assert.strictEqual(pushedSpy.callCount, 0, 'the pushed callback was not called'); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // act @@ -240,7 +240,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod let dataSource = createDataSource(array, { key: 'id' }); this.dataSourceController.dataPushedHandlerProxy = dataPushedHandlerSpy; - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource = this.dataSourceController.getAdapter(); dataSource.load(); @@ -274,7 +274,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod ]; const dataSource = createDataSource(array); - that.dataController.setDataSource(dataSource); + that.dataController.initDataSourceAdapter(dataSource); dataSource.load(); that.dataController.changed.add(function() { @@ -337,7 +337,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod QUnit.test('dataSource should be disposed after calling dispose method', function(assert) { const dataSource = createDataSource([]); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); // act this.dataController.dispose(); @@ -379,11 +379,11 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod onLoadingChanged: loadingChangedSpy }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // act - this.dataController._disposeDataSource(); + this.dataController.disposeDataSourceAdapter(); // assert assert.strictEqual(loadingChangedSpy.callCount, 2, 'loadingChanged call count'); @@ -403,7 +403,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); const rows = this.dataController.items(); @@ -434,7 +434,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod changedCount++; }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.clock.tick(10); @@ -466,7 +466,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod }); this.columnsController.setUserState([{ dataField: 'name', visible: true, sortOrder: 'desc', sortIndex: 0, index: 0 }, { dataField: 'age', visible: true, sortOrder: 'asc', sortIndex: 1, index: 1 }]); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // assert @@ -1981,7 +1981,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod sorting: { mode: 'single' } }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.columnsController.changeSortOrder(0, 'asc'); @@ -2004,7 +2004,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod const dataSource = createDataSource(array, { key: 'name' }, { sort: 'name' }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); assert.ok(this.dataSourceController.getAdapter().sort(), 'sort parameters'); @@ -2033,7 +2033,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod columns[1].groupIndex = 0; } }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); assert.ok(this.dataSourceController.getAdapter().group()); @@ -2055,7 +2055,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod this.columnsController.setUserState([{ dataField: 'name', visible: true, groupIndex: 0, index: 0 }, { dataField: 'age', visible: true, index: 1 }]); // act - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // assert @@ -2105,7 +2105,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod ]; const dataSource = createDataSource(array, { key: 'name' }, { group: 'name' }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); assert.ok(this.dataSourceController.getAdapter().group()); // act @@ -2129,7 +2129,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod commonColumnSettings: { allowSorting: true }, sorting: { mode: 'single' } }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.dataController.pageIndex(1); @@ -2159,7 +2159,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod }, 'age'], sorting: { mode: 'single' } }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); @@ -2196,7 +2196,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod }, 'age'], sorting: { mode: 'single' } }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // act @@ -2224,7 +2224,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod }, 'age'], sorting: { mode: 'single' } }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); @@ -2249,7 +2249,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod sorting: { mode: 'single' } }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.columnsController.changeSortOrder(0, 'asc'); @@ -2280,7 +2280,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod this.columnsController.columnsChanged.add(function(e) { columnsChangedArgs.push(e); }); - dataController.setDataSource(dataSource); + dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.clock.tick(10); @@ -2311,7 +2311,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod this.columnsController.columnsChanged.add(function(e) { columnsChangedArgs.push(e); }); - dataController.setDataSource(dataSource); + dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.clock.tick(10); @@ -2344,7 +2344,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod this.columnsController.columnsChanged.add(function(e) { columnsChangedArgs.push(e); }); - dataController.setDataSource(dataSource); + dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.clock.tick(10); @@ -2381,7 +2381,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod } }] }); - dataController.setDataSource(dataSource); + dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.clock.tick(10); @@ -2439,7 +2439,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod }] }); - dataController.setDataSource(dataSource); + dataController.initDataSourceAdapter(dataSource); dataSource.load(); dataController.changed.add(function(args) { @@ -2481,7 +2481,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod } }] }); - dataController.setDataSource(dataSource); + dataController.initDataSourceAdapter(dataSource); dataController.changed.add(function(args) { changedCount++; @@ -2521,7 +2521,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod } }] }); - dataController.setDataSource(dataSource); + dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.clock.tick(10); @@ -2554,7 +2554,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod const dataSource = createDataSource(array, { key: 'name' }, { pageSize: 2 }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // act @@ -2578,7 +2578,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod const dataSource = createDataSource(array, { key: 'name' }, { pageSize: 2 }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // act @@ -2602,7 +2602,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod const dataSource = createDataSource(array, { key: 'name' }, { pageSize: 2 }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // act @@ -2625,7 +2625,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod const dataSource = createDataSource(array, { key: 'name' }, { pageSize: 2 }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // act, assert @@ -2644,7 +2644,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod const dataSource = createDataSource(array, { key: 'name' }, { pageSize: 2 }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // act, assert @@ -2899,7 +2899,7 @@ QUnit.module('Loading', { beforeEach: setupModule, afterEach: teardownModule }, const dataSource = createDataSource([{ id: 1 }, { id: 2 }, { id: 3 }], {}, { pageSize: 2 }); - that.dataController.setDataSource(dataSource); + that.dataController.initDataSourceAdapter(dataSource); dataSource.load(); that.dataController.loadingChanged.add(function(isLoading) { @@ -2923,7 +2923,7 @@ QUnit.module('Loading', { beforeEach: setupModule, afterEach: teardownModule }, }); // act - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // assert @@ -2933,7 +2933,7 @@ QUnit.module('Loading', { beforeEach: setupModule, afterEach: teardownModule }, QUnit.test('begin custom loading', function(assert) { const loadingStates = []; const dataSource = createDataSource([]); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.dataController.loadingChanged.add(function(isLoading) { @@ -2953,7 +2953,7 @@ QUnit.module('Loading', { beforeEach: setupModule, afterEach: teardownModule }, QUnit.test('end custom loading', function(assert) { const loadingStates = []; const dataSource = createDataSource([]); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.dataController.loadingChanged.add(function(isLoading) { @@ -2998,7 +2998,7 @@ QUnit.module('Loading', { beforeEach: setupModule, afterEach: teardownModule }, this.dataController.loadingChanged.add(function(isLoading) { loadingStates.push(isLoading); }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // assert @@ -3026,7 +3026,7 @@ QUnit.module('Parsing values', { beforeEach: setupModule, afterEach: teardownMod columns: ['name', { dataField: 'birthday', dataType: 'date', format: 'shortDate' }] }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); assert.deepEqual(this.dataController.items()[0].values, ['Alex', null]); @@ -3043,7 +3043,7 @@ QUnit.module('Parsing values', { beforeEach: setupModule, afterEach: teardownMod columns: [{ calculateCellValue: function(data) { return data.firstName + ' ' + data.secondName; } }] }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); assert.equal(this.dataController.items().length, 2); @@ -3079,7 +3079,7 @@ const teardownPagingModule = function() { QUnit.module('Paging', { beforeEach: setupPagingModule, afterEach: teardownPagingModule }, () => { QUnit.test('PagesCount, TotalCount, Rows after initialization', function(assert) { - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); assert.equal(this.dataController.items().length, 5); @@ -3094,7 +3094,7 @@ QUnit.module('Paging', { beforeEach: setupPagingModule, afterEach: teardownPagin QUnit.test('PagesCount after filter dataSource', function(assert) { let changedCount = 0; - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.dataController.changed.add(function() { changedCount++; @@ -3117,7 +3117,7 @@ QUnit.module('Paging', { beforeEach: setupPagingModule, afterEach: teardownPagin // arrange let countCallPageChanged = 0; - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.dataController.pageChanged.add(function() { @@ -3137,7 +3137,7 @@ QUnit.module('Paging', { beforeEach: setupPagingModule, afterEach: teardownPagin }); QUnit.test('get pageIndex after change dataSource pageIndex', function(assert) { - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.dataSource.pageIndex(1); this.dataSource.reload(true); @@ -3148,7 +3148,7 @@ QUnit.module('Paging', { beforeEach: setupPagingModule, afterEach: teardownPagin // B233043 QUnit.test('change pageIndex to greater then pageCount', function(assert) { - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.dataController.pageIndex(5); @@ -3165,7 +3165,7 @@ QUnit.module('Paging', { beforeEach: setupPagingModule, afterEach: teardownPagin const loadingSpy = sinon.spy(); this.dataSource.store().on('loading', loadingSpy); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.pageIndex(1); this.dataSource.load(); @@ -3184,7 +3184,7 @@ QUnit.module('Paging', { beforeEach: setupPagingModule, afterEach: teardownPagin // arrange let countCallPageChanged = 0; - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.dataController.pageIndex(1); @@ -3254,7 +3254,7 @@ QUnit.module('Paging', { beforeEach: setupPagingModule, afterEach: teardownPagin QUnit.test('Rise changed on set pageSize', function(assert) { let changedCount = 0; - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.dataController.pageSize(10); @@ -3273,7 +3273,7 @@ QUnit.module('Paging', { beforeEach: setupPagingModule, afterEach: teardownPagin QUnit.test('Rise changed on set pageSize with changing pageCount', function(assert) { let changedCount = 0; - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.dataController.changed.add(function(controller) { changedCount++; @@ -3290,7 +3290,7 @@ QUnit.module('Paging', { beforeEach: setupPagingModule, afterEach: teardownPagin QUnit.test('Rise changed on set pageIndex', function(assert) { let changedCallCount = 0; const dataController = this.dataController; - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); dataController.changed.add(function() { changedCallCount++; @@ -3304,7 +3304,7 @@ QUnit.module('Paging', { beforeEach: setupPagingModule, afterEach: teardownPagin QUnit.test('Not Rise changed on get pageIndex', function(assert) { let changedCount = 0; - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.dataController.changed.add(function() { changedCount++; @@ -3315,7 +3315,7 @@ QUnit.module('Paging', { beforeEach: setupPagingModule, afterEach: teardownPagin QUnit.test('update pageCount after insert', function(assert) { let changedCount = 0; - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.dataController.changed.add(function() { changedCount++; @@ -3353,7 +3353,7 @@ QUnit.module('Paging', { beforeEach: setupPagingModule, afterEach: teardownPagin }); QUnit.test('Page size of data source is not changed for old value_T242652', function(assert) { - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); // act @@ -3395,7 +3395,7 @@ const setupVirtualScrollingModule = function() { this.array = array; - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); this.dataController.viewportSize(10); dataSource.load(); this.dataSource = dataSource; @@ -3416,7 +3416,7 @@ QUnit.module('Virtual scrolling', { beforeEach: setupVirtualScrollingModule, aft this.applyOptions({ scrolling: { mode: 'standard' } }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); assert.ok(!this.dataController.virtualItemsCount()); }); @@ -4257,7 +4257,7 @@ QUnit.module('Virtual scrolling (ScrollingDataSource)', { this.setupDataSource = function(options) { this.options.paging.pageSize = options.pageSize; this.dataSource = createDataSource(options.data || TEN_NUMBERS, {}, $.extend({ paginate: true }, options)); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.dataController.viewportSize(2); }; @@ -4576,7 +4576,7 @@ QUnit.module('Virtual scrolling (ScrollingDataSource)', { return loadResult || []; } }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); const dataController = this.dataController; let isLoadingByEvent; @@ -5677,7 +5677,7 @@ QUnit.module('Infinite scrolling', { scrolling: { mode: 'infinite' }, pager: { visible: 'auto' } }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); this.dataController.viewportSize(10); dataSource.load(); this.dataSource = dataSource; @@ -5905,7 +5905,7 @@ QUnit.module('Infinite scrolling (ScrollingDataSource)', { this.setupDataSource = function(options) { this.dataSource = createDataSource(options.data || TEN_NUMBERS, {}, $.extend({ paginate: true, requireTotalCount: false }, options)); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); }; @@ -6254,7 +6254,7 @@ QUnit.module('Filtering', { } }] }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); }; }, @@ -6267,7 +6267,7 @@ QUnit.module('Filtering', { { name: 'Dan', age: 19 } ], {}, { filter: ['name', 'Dan'] }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); // assert @@ -6311,7 +6311,7 @@ QUnit.module('Filtering', { }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); // act @@ -6401,7 +6401,7 @@ QUnit.module('Filtering', { { name: 'Alex', age: 15 }, { name: 'Dan', age: 19 } ], {}, { filter: ['name', 'Dan'] }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.dataController.pageChanged.add(function() { @@ -6428,7 +6428,7 @@ QUnit.module('Filtering', { { name: 'Alex', age: 15 }, { name: 'Dan', age: 19 } ]); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.dataController.pageChanged.add(function() { @@ -6452,7 +6452,7 @@ QUnit.module('Filtering', { { name: 'Alex', age: 15 }, { name: 'Dan', age: 19 } ], {}, { filter: ['name', 'Dan'] }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.dataController.pageChanged.add(function() { @@ -6473,7 +6473,7 @@ QUnit.module('Filtering', { { name: 'Alex', age: 15 }, { name: 'Dan', age: 19 } ], {}, { filter: ['name', 'Dan'] }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); @@ -6490,7 +6490,7 @@ QUnit.module('Filtering', { { name: 'Alex', age: 15 }, { name: 'Dan', age: 19 } ], { onLoading: function() { loadingCount++; } }, { filter: ['name', 'Dan'] }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); @@ -6530,7 +6530,7 @@ QUnit.module('Filtering', { } }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); @@ -6560,7 +6560,7 @@ QUnit.module('Filtering', { columns: [{ dataField: 'age', dataType: 'number', filterValue: 15 }], }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); assert.deepEqual(this.getCombinedFilter(true), ['age', '=', 15]); @@ -6593,7 +6593,7 @@ QUnit.module('Filtering', { } }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); // act @@ -6619,7 +6619,7 @@ QUnit.module('Filtering', { } }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); // act @@ -6646,7 +6646,7 @@ QUnit.module('Filtering', { } }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); @@ -6676,7 +6676,7 @@ QUnit.module('Filtering', { } }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); @@ -6715,7 +6715,7 @@ QUnit.module('Filtering', { } }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); @@ -6744,7 +6744,7 @@ QUnit.module('Filtering', { } }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); @@ -6774,7 +6774,7 @@ QUnit.module('Filtering', { columns: [{ dataField: 'age', dataType: 'number', filterValue: [15, 20], selectedFilterOperation: 'between', filterValues: [17] }] }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); @@ -6802,7 +6802,7 @@ QUnit.module('Filtering', { }); // act - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); // assert @@ -6831,7 +6831,7 @@ QUnit.module('Filtering', { }); // act - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); // assert @@ -6869,7 +6869,7 @@ QUnit.module('Filtering', { }); // act - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); // assert @@ -6899,7 +6899,7 @@ QUnit.module('Filtering', { }); // act - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); // assert @@ -6928,7 +6928,7 @@ QUnit.module('Filtering', { that.applyOptions({ columns: [{ dataField: 'name' }, { dataField: 'age', filterValue: 19 }] }); - that.dataController.setDataSource(that.dataSource); + that.dataController.initDataSourceAdapter(that.dataSource); that.dataSource.load(); that.clock.tick(10); @@ -6965,7 +6965,7 @@ QUnit.module('Filtering', { that.applyOptions({ columns: [{ dataField: 'name', filterValues: ['Alex', 'Dan', 'Bob'] }, { dataField: 'age', filterValue: 19 }] }); - that.dataController.setDataSource(that.dataSource); + that.dataController.initDataSourceAdapter(that.dataSource); that.dataSource.load(); that.dataController.searchByText('Bob'); that.clock.tick(10); @@ -7019,7 +7019,7 @@ QUnit.module('Filtering', { that.applyOptions({ columns: [{ dataField: 'name', filterValues: ['Alex', 'Dan', 'Bob', 'Bobbi'] }, { dataField: 'age', filterValue: 19 }] }); - that.dataController.setDataSource(that.dataSource); + that.dataController.initDataSourceAdapter(that.dataSource); that.dataSource.load(); that.dataController.searchByText('Bob'); @@ -7068,7 +7068,7 @@ QUnit.module('Filtering', { that.applyOptions({ columns: [{ dataField: 'name', filterValues: ['Alex', 'Dan', 'Bob'] }, { dataField: 'age', filterValue: 19 }] }); - that.dataController.setDataSource(that.dataSource); + that.dataController.initDataSourceAdapter(that.dataSource); that.dataSource.load(); that.dataController.searchByText('Bob'); @@ -7117,7 +7117,7 @@ QUnit.module('Filtering', { that.applyOptions({ columns: [{ dataField: 'name', filterValues: ['Alex', 'Dan', 'Bob'] }, { dataField: 'age', filterValue: 19 }] }); - that.dataController.setDataSource(that.dataSource); + that.dataController.initDataSourceAdapter(that.dataSource); that.dataSource.load(); that.dataController.searchByText('Bob'); @@ -7165,7 +7165,7 @@ QUnit.module('Filtering', { that.applyOptions({ columns: [{ dataField: 'name', filterValues: ['Alex', 'Dan', 'Bob'] }, { dataField: 'age', filterValue: 19 }] }); - that.dataController.setDataSource(that.dataSource); + that.dataController.initDataSourceAdapter(that.dataSource); that.dataSource.load(); that.dataController.searchByText('Bob'); @@ -7213,7 +7213,7 @@ QUnit.module('Filtering', { that.applyOptions({ columns: [{ dataField: 'name', filterValues: ['Alex', 'Dan', 'Bob'] }, { dataField: 'age', filterValue: 19 }] }); - that.dataController.setDataSource(that.dataSource); + that.dataController.initDataSourceAdapter(that.dataSource); that.dataSource.load(); that.dataController.searchByText('Bob'); @@ -7251,7 +7251,7 @@ QUnit.module('Filtering', { { name: 'Max', age: 21 } ], {}, { pageSize: 2, pageIndex: 1 }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); @@ -7301,7 +7301,7 @@ QUnit.module('Filtering', { { name: 'Dan', age: 19, birthDate: new Date(1996, 1, 20) } ], {}, { filter: ['age', '>', 16] }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); assert.equal(this.dataController.items().length, 2); @@ -7357,7 +7357,7 @@ QUnit.module('Filtering', { isDataSourceReloaded = true; }); - that.dataController.setDataSource(that.dataSource); + that.dataController.initDataSourceAdapter(that.dataSource); that.dataSource.load(); // assert assert.ok(isDataSourceReloaded); @@ -7381,7 +7381,7 @@ QUnit.module('Filtering', { ] }); - that.dataController.setDataSource(that.dataSource); + that.dataController.initDataSourceAdapter(that.dataSource); sinon.spy(errors, 'log'); @@ -7416,7 +7416,7 @@ QUnit.module('Filtering', { ] }); - that.dataController.setDataSource(that.dataSource); + that.dataController.initDataSourceAdapter(that.dataSource); sinon.spy(errors, 'log'); @@ -7453,7 +7453,7 @@ QUnit.module('Filtering', { loadCount++; }); - that.dataController.setDataSource(that.dataSource); + that.dataController.initDataSourceAdapter(that.dataSource); that.dataSource.load(); // assert @@ -7515,7 +7515,7 @@ QUnit.module('Filtering', { { name: 'Alla', age: 21, birthDate: new Date(1993, 5, 2), state: 0, processed: false }, { name: 'Dan', age: 19, birthDate: new Date(1996, 1, 20), state: 1, processed: true } ]); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.columnsController.columnOption('name', 'filterValue', 'Al'); @@ -7754,7 +7754,7 @@ QUnit.module('Filtering', { remoteOperations: { filtering: true } }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); let loadingCount = 0; this.dataSource.store().on('loading', function() { @@ -7947,7 +7947,7 @@ QUnit.module('Filtering', { }); // act - that.dataController.setDataSource(that.dataSource); + that.dataController.initDataSourceAdapter(that.dataSource); that.dataSource.load(); // assert @@ -7968,7 +7968,7 @@ QUnit.module('Filtering', { ], {}, { asyncLoadEnabled: true }); // act - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.clock.tick(10); @@ -7999,7 +7999,7 @@ QUnit.module('Filtering', { // act that.columnsController.reset(); - that.dataController.setDataSource(that.dataSource); + that.dataController.initDataSourceAdapter(that.dataSource); that.dataSource.load(); // assert @@ -8091,7 +8091,7 @@ QUnit.module('Filtering', { filterValue: new Date(1992, 7, 6, 12, 30, 21) }] }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); // act, assert @@ -8136,7 +8136,7 @@ QUnit.module('Grouping', { beforeEach: setupModule, afterEach: teardownModule }, }); // act - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); const rows = this.dataController.items(); @@ -8176,7 +8176,7 @@ QUnit.module('Grouping', { beforeEach: setupModule, afterEach: teardownModule }, }); // act - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); const rows = this.dataController.items(); @@ -8224,7 +8224,7 @@ QUnit.module('Grouping', { beforeEach: setupModule, afterEach: teardownModule }, }); // act - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); const rows = this.dataController.items(); @@ -8286,7 +8286,7 @@ QUnit.module('Grouping', { beforeEach: setupModule, afterEach: teardownModule }, }); // act - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.dataController.changeRowExpand(['1']); @@ -8323,7 +8323,7 @@ QUnit.module('Grouping', { beforeEach: setupModule, afterEach: teardownModule }, }); // act - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); const rows = this.dataController.items(); @@ -8363,7 +8363,7 @@ QUnit.module('Grouping', { beforeEach: setupModule, afterEach: teardownModule }, }); // act - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); const rows = this.dataController.items(); @@ -8402,7 +8402,7 @@ QUnit.module('Grouping', { beforeEach: setupModule, afterEach: teardownModule }, }); // act - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); const rows = this.dataController.items(); @@ -8448,7 +8448,7 @@ QUnit.module('Grouping', { beforeEach: setupModule, afterEach: teardownModule }, }); // act - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); const rows = this.dataController.items(); @@ -8490,7 +8490,7 @@ QUnit.module('Grouping', { beforeEach: setupModule, afterEach: teardownModule }, }); // act - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); const rows = this.dataController.items(); @@ -8530,7 +8530,7 @@ QUnit.module('Grouping', { beforeEach: setupModule, afterEach: teardownModule }, }); // act - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); const rows = this.dataController.items(); @@ -8575,7 +8575,7 @@ QUnit.module('Grouping', { beforeEach: setupModule, afterEach: teardownModule }, }); // act - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); const rows = this.dataController.items(); @@ -8628,7 +8628,7 @@ QUnit.module('Grouping', { beforeEach: setupModule, afterEach: teardownModule }, }); // act - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); const rows = this.dataController.items(); @@ -8655,7 +8655,7 @@ QUnit.module('Grouping', { beforeEach: setupModule, afterEach: teardownModule }, itemsCount: 1 }; - this.dataController.setDataSource(new MockGridDataSource(dataSourceOptions)); + this.dataController.initDataSourceAdapter(new MockGridDataSource(dataSourceOptions)); this.dataController.pageChanged.add(function() { countCallPageChanged++; @@ -8678,7 +8678,7 @@ QUnit.module('Grouping', { beforeEach: setupModule, afterEach: teardownModule }, pageIndex: 1 }; - this.dataController.setDataSource(new MockGridDataSource(dataSourceOptions)); + this.dataController.initDataSourceAdapter(new MockGridDataSource(dataSourceOptions)); // act this.dataController.collapseAll(1); @@ -8695,7 +8695,7 @@ QUnit.module('Grouping', { beforeEach: setupModule, afterEach: teardownModule }, pageIndex: 1 }; - this.dataController.setDataSource(new MockGridDataSource(dataSourceOptions)); + this.dataController.initDataSourceAdapter(new MockGridDataSource(dataSourceOptions)); // act this.dataController.expandAll(1); @@ -8711,7 +8711,7 @@ QUnit.module('Grouping', { beforeEach: setupModule, afterEach: teardownModule }, const that = this; const dataSource = createDataSource([]); - that.dataController.setDataSource(dataSource); + that.dataController.initDataSourceAdapter(dataSource); dataSource.load(); that.applyOptions({ columns: ['field1', 'field2', { dataField: 'field3', groupIndex: 0 }, { dataField: 'field4', groupIndex: 1 }, 'field5'] }); @@ -8748,7 +8748,7 @@ QUnit.module('Grouping', { beforeEach: setupModule, afterEach: teardownModule }, City: 'Bentonville' }]); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); // act dataSource.load(); @@ -8773,7 +8773,7 @@ QUnit.module('Editing', { beforeEach: function() { const dataSource = createDataSource(array, { key: 'name' }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // act @@ -8805,7 +8805,7 @@ QUnit.module('Editing', { beforeEach: function() { const dataSource = createDataSource(array, { key: 'name' }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // act @@ -8837,7 +8837,7 @@ QUnit.module('Editing', { beforeEach: function() { const dataSource = createDataSource(array, { key: 'name' }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // act @@ -8862,7 +8862,7 @@ QUnit.module('Editing', { beforeEach: function() { const dataSource = createDataSource(array, { key: 'name' }, { group: 'name' }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.expandAll(); @@ -8893,7 +8893,7 @@ QUnit.module('Editing', { beforeEach: function() { const dataSource = createDataSource(array, { key: 'name' }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.editingController.addRow(); // act @@ -8917,7 +8917,7 @@ QUnit.module('Editing', { beforeEach: function() { const dataSource = createDataSource(array, { key: 'name' }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.editingController.addRow(); @@ -8940,7 +8940,7 @@ QUnit.module('Editing', { beforeEach: function() { const dataSource = createDataSource(array, { key: 'name' }, { pageSize: 2 }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.editingController.addRow(); @@ -8965,7 +8965,7 @@ QUnit.module('Editing', { beforeEach: function() { const dataSource = createDataSource(array, { key: 'id' }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.expandRow(1); @@ -8994,7 +8994,7 @@ QUnit.module('Editing', { beforeEach: function() { const dataSource = createDataSource(array, { key: 'id' }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.expandRow(2); @@ -9030,7 +9030,7 @@ QUnit.module('Editing', { beforeEach: function() { remove: () => ++removeHandlerCallCount }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.applyOptions({ @@ -9074,7 +9074,7 @@ QUnit.module('Editing', { beforeEach: function() { } }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.applyOptions({ @@ -12979,7 +12979,7 @@ QUnit.module('Partial update', { { name: 'Bob', age: 20 } ]; that.dataSource = createDataSource(that.array, { key: 'name' }); - that.dataController.setDataSource(that.dataSource); + that.dataController.initDataSourceAdapter(that.dataSource); that.dataSource.load(); }; @@ -13385,7 +13385,7 @@ QUnit.module('Refresh changesOnly', { { id: 3, name: 'Bob', age: 20 } ]; that.dataSource = createDataSource(that.array, { key: 'id' }, options); - that.dataController.setDataSource(that.dataSource); + that.dataController.initDataSourceAdapter(that.dataSource); that.dataSource.load(); }; @@ -15361,7 +15361,7 @@ QUnit.module('Sorting', { beforeEach: setupModule, afterEach: teardownModule }, { name: 'Dan', age: 15 } ]); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); const calculateSortValue = function(data) { return data[this.dataField]; }; diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataGrid.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataGrid.tests.js index 56755d970cf8..c11b46396de3 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataGrid.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/dataGrid.tests.js @@ -1520,14 +1520,14 @@ QUnit.module('Assign options', baseModuleConfig, () => { dataSource: dataSource }); - const dataSourceInstance = dataGrid.getController('data')._dataSource; + const dataSourceInstance = dataGrid.getController('dataSource').getAdapter(); // act dataSource.push({ id: 2 }); dataGrid.option('dataSource', dataSource); // assert - assert.strictEqual(dataSourceInstance, dataGrid.getController('data')._dataSource, 'dataSource is not recreated'); + assert.strictEqual(dataSourceInstance, dataGrid.getController('dataSource').getAdapter(), 'dataSource is not recreated'); assert.strictEqual(dataGrid.getController('data').items().length, 2, 'data is updated'); }); @@ -1702,7 +1702,7 @@ QUnit.module('Assign options', baseModuleConfig, () => { store: [{ id: 1111 }] } }); - assert.equal(dataGrid.getController('data')._dataSource.pageSize(), 20); + assert.equal(dataGrid.getController('dataSource').getAdapter().pageSize(), 20); // act dataGrid.option('dataSource', { @@ -1711,7 +1711,7 @@ QUnit.module('Assign options', baseModuleConfig, () => { }); // assert - assert.equal(dataGrid.getController('data')._dataSource.pageSize(), 50); + assert.equal(dataGrid.getController('dataSource').getAdapter().pageSize(), 50); }); QUnit.test('columns change', function(assert) { diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/filterPanel.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/filterPanel.tests.js index ead18bfaf84e..20aabe75c24d 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/filterPanel.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/filterPanel.tests.js @@ -718,7 +718,7 @@ QUnit.module('Filter Panel', { assert.notOk(this.filterPanelView.element().hasClass(FILTER_PANEL_CLASS)); // act - this.dataController.setDataSource(new DataSource([])); + this.dataController.initDataSourceAdapter(new DataSource([])); this.dataController.dataSourceChanged.fire(); // assert diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/focus.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/focus.tests.js index e49b3f3ae333..8ed2bd12fd8d 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/focus.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/focus.tests.js @@ -5836,7 +5836,7 @@ QUnit.module('Focused row', getModuleConfig(true), () => { this.clock.tick(10); // act - this.getController('data')._dataSource.operationTypes = () => undefined; + this.getController('dataSource').getAdapter().operationTypes = () => undefined; try { this.option('focusedRowKey', 'Dan'); } catch(e) { diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/grouping.integration.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/grouping.integration.tests.js index 369c75c88123..fa5d33efb5d1 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/grouping.integration.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/grouping.integration.tests.js @@ -213,7 +213,7 @@ QUnit.module('Initialization', baseModuleConfig, () => { this.clock.tick(300); - assert.deepEqual(dataGrid.getController('data')._dataSource.group(), [{ selector: 'field2', desc: false, isExpanded: true }], 'datasource grouping is up to date'); + assert.deepEqual(dataGrid.getController('dataSource').getAdapter().group(), [{ selector: 'field2', desc: false, isExpanded: true }], 'datasource grouping is up to date'); assert.equal(dataGrid.columnOption('field2', 'groupIndex'), 0, 'Group by field2'); $(dataGrid.$element()) @@ -698,7 +698,7 @@ QUnit.module('Initialization', baseModuleConfig, () => { items.eq(3).trigger('dxclick'); - assert.deepEqual(dataGrid.getController('data')._dataSource.group(), [{ selector: 'field3', desc: false, isExpanded: true }], 'datasource grouping is up to date'); + assert.deepEqual(dataGrid.getController('dataSource').getAdapter().group(), [{ selector: 'field3', desc: false, isExpanded: true }], 'datasource grouping is up to date'); assert.equal(dataGrid.columnOption('field3', 'groupIndex'), 0, 'Group by field3'); }); diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/selection.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/selection.tests.js index b698e348456f..c8cce308d45d 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/selection.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/selection.tests.js @@ -55,7 +55,7 @@ const setupSelectionModule = function() { ]; this.dataSource = createDataSource(this.array); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); }; @@ -276,7 +276,7 @@ QUnit.module('Selection', { beforeEach: setupSelectionModule, afterEach: teardow ]; this.dataSource = createDataSource(array); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.applyOptions({ selection: { mode: 'single' } @@ -301,7 +301,7 @@ QUnit.module('Selection', { beforeEach: setupSelectionModule, afterEach: teardow ]; this.dataSource = createDataSource(array); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.applyOptions({ selection: { mode: 'single' } @@ -326,7 +326,7 @@ QUnit.module('Selection', { beforeEach: setupSelectionModule, afterEach: teardow ]; this.dataSource = createDataSource(array); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.applyOptions({ selection: { mode: 'single' } @@ -353,7 +353,7 @@ QUnit.module('Selection', { beforeEach: setupSelectionModule, afterEach: teardow const array = [item1, item2]; this.dataSource = createDataSource(array); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.applyOptions({ selection: { mode: 'single' } @@ -377,7 +377,7 @@ QUnit.module('Selection', { beforeEach: setupSelectionModule, afterEach: teardow ]; this.dataSource = createDataSource(array); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.applyOptions({ selection: { mode: 'single' } @@ -398,7 +398,7 @@ QUnit.module('Selection', { beforeEach: setupSelectionModule, afterEach: teardow const array = [{ name: 'Alex', address: { country: 'USA', city: 'New York' } }, {}, {}]; this.dataSource = createDataSource(array); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.applyOptions({ selection: { mode: 'single' } @@ -420,7 +420,7 @@ QUnit.module('Selection', { beforeEach: setupSelectionModule, afterEach: teardow const array = [{ name: 'Alex', address: { country: 'USA', city: 'New York' } }, {}, {}]; this.dataSource = createDataSource(array); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.applyOptions({ selection: { mode: 'single' } @@ -443,7 +443,7 @@ QUnit.module('Selection', { beforeEach: setupSelectionModule, afterEach: teardow { name: 'Dan', address: { country: 'USA', city: 'Chicago' } }]; this.dataSource = createDataSource(array); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.applyOptions({ selection: { mode: 'single' } @@ -608,7 +608,7 @@ QUnit.module('Selection', { beforeEach: setupSelectionModule, afterEach: teardow ]; this.dataSource = createDataSource(array, { key: ['prop1', 'prop2'] }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.applyOptions({ @@ -1254,7 +1254,7 @@ QUnit.module('Selection', { beforeEach: setupSelectionModule, afterEach: teardow paginate: true }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.selectionController.changeItemSelection(1); assert.deepEqual(this.selectionController.getSelectedRowsData(), [{ name: 'Dan1', pay: 151 }]); @@ -1289,7 +1289,7 @@ QUnit.module('Selection', { beforeEach: setupSelectionModule, afterEach: teardow }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); let loadingCount = 0; @@ -1425,7 +1425,7 @@ QUnit.module('Selection', { beforeEach: setupSelectionModule, afterEach: teardow } that.dataSource = createDataSource(that.array, { key: 'id' }); - that.dataController.setDataSource(that.dataSource); + that.dataController.initDataSourceAdapter(that.dataSource); that.dataSource.load(); // act @@ -1440,7 +1440,7 @@ QUnit.module('Selection', { beforeEach: setupSelectionModule, afterEach: teardow const that = this; that.dataSource = createDataSource(that.array, { key: ['name', 'age'] }); - that.dataController.setDataSource(that.dataSource); + that.dataController.initDataSourceAdapter(that.dataSource); that.dataSource.load(); // act @@ -1457,7 +1457,7 @@ QUnit.module('Selection', { beforeEach: setupSelectionModule, afterEach: teardow }); this.dataSource = createDataSource(this.array, {}, { pageSize: 5, filter: ['age', '>', 15] }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); // act @@ -1828,7 +1828,7 @@ const setupSelectionWithKeysModule = function() { ]; const dataSource = createDataSource(this.array, { key: 'id' }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); }; @@ -2105,7 +2105,7 @@ QUnit.module('Multiple selection. DataSource with key', { beforeEach: setupSelec dataSource.load(); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); this.selectionController.selectRows([2, 3]); assert.deepEqual(this.selectionController.getSelectedRowKeys(), [2, 3]); @@ -2459,7 +2459,7 @@ QUnit.module('Selection SelectAllMode', { ]; this.dataSource = createDataSource(this.array, { key: 'id' }, { pageSize: 4 }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); }, afterEach: teardownModule @@ -2976,7 +2976,7 @@ QUnit.module('Selection SelectAllMode', { QUnit.test('get isSelected rows after Select All when dataSource has complex key', function(assert) { this.dataSource = createDataSource(this.array, { key: ['id', 'value'] }, { pageSize: 4 }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.applyOptions({ selection: { @@ -3004,7 +3004,7 @@ QUnit.module('Selection SelectAllMode', { QUnit.test('get isSelected rows after Select All when dataSource has no key', function(assert) { this.dataSource = createDataSource(this.array, {}, { pageSize: 4 }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.applyOptions({ selection: { @@ -3043,7 +3043,7 @@ QUnit.module('Selection SelectAllMode', { const onSelectionChangedSpy = sinon.spy(); this.dataSource = createDataSource(data, { key: 'id' }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.applyOptions({ @@ -3117,7 +3117,7 @@ QUnit.module('Selection when grouping', { selection: { mode: 'single' } }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.selectionController.selectRows({ group: 'A', value: 2 }); @@ -3134,7 +3134,7 @@ QUnit.module('Selection when grouping', { selection: { mode: 'single' } }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.selectionController.changeItemSelection(0); @@ -3148,7 +3148,7 @@ QUnit.module('Selection when grouping', { selection: { mode: 'single' } }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.selectionController.changeItemSelection(1); @@ -3164,7 +3164,7 @@ QUnit.module('Selection when grouping', { selection: { mode: 'multiple' } }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.selectionController.changeItemSelection(1); @@ -3184,7 +3184,7 @@ QUnit.module('Selection when grouping', { selection: { mode: 'multiple' } }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.dataSource.reload(); @@ -3203,7 +3203,7 @@ QUnit.module('Selection when grouping', { selection: { mode: 'multiple' } }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); this.selectionController.changeItemSelection(1); @@ -3218,7 +3218,7 @@ QUnit.module('Selection when grouping', { }); QUnit.test('selectAll when remoteOperations enabled', function(assert) { - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); // act @@ -3243,7 +3243,7 @@ QUnit.module('Selection when grouping', { grouping: { autoExpandAll: false } }); - this.dataController.setDataSource(this.dataSource); + this.dataController.initDataSourceAdapter(this.dataSource); this.dataSource.load(); // act diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/sorting.integration.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/sorting.integration.tests.js index f6bf8cf34781..92e978da0a6d 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/sorting.integration.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/sorting.integration.tests.js @@ -83,8 +83,8 @@ QUnit.module('Initialization', baseModuleConfig, () => { } }).dxDataGrid('instance'); - assert.deepEqual(dataGrid.getController('data')._dataSource.group(), [{ selector: 'field1', desc: false, isExpanded: true }]); - assert.deepEqual(dataGrid.getController('data')._dataSource.sort(), [{ selector: 'field2', desc: false }]); + assert.deepEqual(dataGrid.getController('dataSource').getAdapter().group(), [{ selector: 'field1', desc: false, isExpanded: true }]); + assert.deepEqual(dataGrid.getController('dataSource').getAdapter().sort(), [{ selector: 'field2', desc: false }]); }); // T859208 diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.treeList/dataController.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.treeList/dataController.tests.js index a8cc8b81c41f..72761b7aff14 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.treeList/dataController.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.treeList/dataController.tests.js @@ -97,7 +97,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod ]; const dataSource = createDataSource(array); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); // act dataSource.load(); @@ -147,7 +147,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod ]; const dataSource = createDataSource(array); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); // act dataSource.load(); @@ -175,7 +175,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod onNodesInitialized: nodesInitialized }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); const rootNode = this.getRootNode(); @@ -198,7 +198,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod ]; const dataSource = createDataSource(array); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); @@ -240,7 +240,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod onNodesInitialized: nodesInitialized }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); const rootNode = this.getRootNode(); @@ -269,7 +269,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod itemsExpr: 'items', dataStructure: 'tree' }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); // act dataSource.load(); @@ -316,7 +316,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod dataStructure: 'tree', keyExpr: 'key' }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); // act dataSource.load(); @@ -357,7 +357,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod const dataSource = createDataSource(array, { key: 'id' }); this.applyOptions({ keyExpr: null }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); // act dataSource.load(); @@ -410,7 +410,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod // act, assert try { - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); assert.ok(false, 'exception should be rised'); } catch(e) { assert.ok(e.message.indexOf('E1044') >= 0, 'name of error'); @@ -430,7 +430,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod this.applyOptions({ keyExpr: 'key' }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); this.dataController.dataErrorOccurred.add(function(e) { dataErrors.push(e); }); @@ -452,7 +452,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod ]; const dataSource = createDataSource(array); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.expandRow(2); // TODO: remove when implemented expandAllEnabled @@ -707,7 +707,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod QUnit.test('There are no exceptions on getting node when hasn\'t datasource', function(assert) { // arrange - this.dataController._disposeDataSource(); + this.dataController.disposeDataSourceAdapter(); // act, assert assert.equal(this.getNodeByKey(1), undefined, 'no exceptions'); @@ -723,7 +723,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod const dataSource = createDataSource(array); const spy = sinon.spy(); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); const rootNode = this.dataController.getRootNode(); @@ -747,7 +747,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod const dataSource = createDataSource(array); const spy = sinon.spy(); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); const rootNode = this.dataController.getRootNode(); @@ -772,7 +772,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod const dataSource = createDataSource(array); const spy = sinon.spy(); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // act @@ -804,7 +804,7 @@ QUnit.module('Initialization', { beforeEach: setupModule, afterEach: teardownMod parentIdExpr: 'parentId', expandedRowKeys: ['key2'] }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); // act dataSource.load(); @@ -928,7 +928,7 @@ QUnit.module('Expand/Collapse nodes', { beforeEach: setupModule, afterEach: tear ]; const dataSource = createDataSource(array); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // assert @@ -953,7 +953,7 @@ QUnit.module('Expand/Collapse nodes', { beforeEach: setupModule, afterEach: tear ]; const dataSource = createDataSource(array); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); this.expandRow(1); @@ -977,7 +977,7 @@ QUnit.module('Expand/Collapse nodes', { beforeEach: setupModule, afterEach: tear ]; const dataSource = createDataSource(array); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // assert @@ -1011,7 +1011,7 @@ QUnit.module('Expand/Collapse nodes', { beforeEach: setupModule, afterEach: tear const dataSource = createDataSource(array); this.applyOptions({ expandedRowKeys: [1] }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // assert @@ -1031,7 +1031,7 @@ QUnit.module('Expand/Collapse nodes', { beforeEach: setupModule, afterEach: tear const dataSource = createDataSource(array); this.applyOptions({ expandedRowKeys: [2] }); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // assert @@ -1056,7 +1056,7 @@ QUnit.module('Expand/Collapse nodes', { beforeEach: setupModule, afterEach: tear ]; const dataSource = createDataSource(array); - this.dataController.setDataSource(dataSource); + this.dataController.initDataSourceAdapter(dataSource); dataSource.load(); let expandedRowKeys = this.dataController.option('expandedRowKeys'); @@ -1097,7 +1097,7 @@ QUnit.module('Expand/Collapse nodes', { beforeEach: setupModule, afterEach: tear }; }); that.applyOptions(options); - that.dataController.setDataSource(dataSource); + that.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // act @@ -1140,7 +1140,7 @@ QUnit.module('Expand/Collapse nodes', { beforeEach: setupModule, afterEach: tear e.cancel = true; } }); - that.dataController.setDataSource(dataSource); + that.dataController.initDataSourceAdapter(dataSource); dataSource.load(); // act @@ -1165,7 +1165,7 @@ QUnit.module('Expand/Collapse nodes', { beforeEach: setupModule, afterEach: tear e.cancel = true; } }); - that.dataController.setDataSource(dataSource); + that.dataController.initDataSourceAdapter(dataSource); dataSource.load(); that.expandRow(1);