From c937a81b6378c8b68ff46f0d9f9ef70c5ab5dd5f Mon Sep 17 00:00:00 2001 From: Alyar <> Date: Thu, 5 Feb 2026 16:03:41 +0400 Subject: [PATCH 1/4] DataGrid: Fix the "Cannot read properties of undefined (reading 'values')" error that occurs when adding a row to the second page if initial row values are specified in onInitNewRow (T1274123) --- .../__tests__/__mock__/helpers/utils.ts | 14 +- .../__tests__/m_editing.integration.test.ts | 230 +++++++++++++++--- .../grids/grid_core/editing/m_editing.ts | 24 +- 3 files changed, 229 insertions(+), 39 deletions(-) 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 6337eb7339a1..b01ff30fe676 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 @@ -5,6 +5,11 @@ import $ from '@js/core/renderer'; import type { Properties as DataGridProperties } from '@js/ui/data_grid'; import DataGrid from '@js/ui/data_grid'; import { DataGridModel } from '@ts/grids/data_grid/__tests__/__mock__/model/data_grid'; +import type { Controllers } from '@ts/grids/grid_core/m_types'; + +export interface DataGridInstance extends DataGrid { + getController: (name: T) => Controllers[T]; +} export const SELECTORS = { gridContainer: '#gridContainer', @@ -12,12 +17,12 @@ export const SELECTORS = { export const GRID_CONTAINER_ID = 'gridContainer'; -export const createDataGrid = async ( +export const createDataGrid = ( options: DataGridProperties = {}, ): Promise<{ $container: dxElementWrapper; component: DataGridModel; - instance: DataGrid; + instance: DataGridInstance; }> => new Promise((resolve) => { const $container = $('
') .attr('id', GRID_CONTAINER_ID) @@ -28,7 +33,10 @@ export const createDataGrid = async ( ...options, }; - const instance = new DataGrid($container.get(0) as HTMLDivElement, dataGridOptions); + const instance = new DataGrid( + $container.get(0) as HTMLDivElement, + dataGridOptions, + ) as DataGridInstance; const component = new DataGridModel($container.get(0) as HTMLElement); jest.runAllTimers(); diff --git a/packages/devextreme/js/__internal/grids/grid_core/editing/__tests__/m_editing.integration.test.ts b/packages/devextreme/js/__internal/grids/grid_core/editing/__tests__/m_editing.integration.test.ts index dbc797a2f41f..c34a6ea6c6e5 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/editing/__tests__/m_editing.integration.test.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/editing/__tests__/m_editing.integration.test.ts @@ -1,6 +1,7 @@ import { afterEach, beforeEach, describe, expect, it, } from '@jest/globals'; +import CustomStore from '@ts/data/m_custom_store'; import { afterTest, @@ -9,40 +10,43 @@ import { flushAsync, } from '../../__tests__/__mock__/helpers/utils'; -const dataSource = [{ - ID: 1, - FirstName: 'John', - LastName: 'Heart', - Prefix: 'Mr.', - Position: 'CEO', - BirthDate: '1964/03/16', - HireDate: '1995/01/15', - Notes: 'John has been in the Audio/Video industry since 1990. He has led DevAv as its CEO since 2003.\r\n\r\nWhen not working hard as the CEO, John loves to golf and bowl. He once bowled a perfect game of 300.', - Address: '351 S Hill St.', -}, { - ID: 2, - FirstName: 'Olivia', - LastName: 'Peyton', - Prefix: 'Mrs.', - Position: 'Sales Assistant', - BirthDate: '1981/06/03', - HireDate: '2012/05/14', - Notes: 'Olivia loves to sell. She has been selling DevAV products since 2012. \r\n\r\nOlivia was homecoming queen in high school. She is expecting her first child in 6 months. Good Luck Olivia.', - Address: '807 W Paseo Del Mar', -}, { - ID: 3, - FirstName: 'Robert', - LastName: 'Reagan', - Prefix: 'Mr.', - Position: 'CMO', - BirthDate: '1974/09/07', - HireDate: '2002/11/08', - Notes: 'Robert was recently voted the CMO of the year by CMO Magazine. He is a proud member of the DevAV Management Team.\r\n\r\nRobert is a championship BBQ chef, so when you get the chance ask him for his secret recipe.', - Address: '4 Westmoreland Pl.', -}]; - describe('DataGrid editing', () => { - beforeEach(beforeTest); + let dataSource: Record[] = []; + + beforeEach(() => { + beforeTest(); + dataSource = [{ + ID: 1, + FirstName: 'John', + LastName: 'Heart', + Prefix: 'Mr.', + Position: 'CEO', + BirthDate: '1964/03/16', + HireDate: '1995/01/15', + Notes: 'John has been in the Audio/Video industry since 1990. He has led DevAv as its CEO since 2003.\r\n\r\nWhen not working hard as the CEO, John loves to golf and bowl. He once bowled a perfect game of 300.', + Address: '351 S Hill St.', + }, { + ID: 2, + FirstName: 'Olivia', + LastName: 'Peyton', + Prefix: 'Mrs.', + Position: 'Sales Assistant', + BirthDate: '1981/06/03', + HireDate: '2012/05/14', + Notes: 'Olivia loves to sell. She has been selling DevAV products since 2012. \r\n\r\nOlivia was homecoming queen in high school. She is expecting her first child in 6 months. Good Luck Olivia.', + Address: '807 W Paseo Del Mar', + }, { + ID: 3, + FirstName: 'Robert', + LastName: 'Reagan', + Prefix: 'Mr.', + Position: 'CMO', + BirthDate: '1974/09/07', + HireDate: '2002/11/08', + Notes: 'Robert was recently voted the CMO of the year by CMO Magazine. He is a proud member of the DevAV Management Team.\r\n\r\nRobert is a championship BBQ chef, so when you get the chance ask him for his secret recipe.', + Address: '4 Westmoreland Pl.', + }]; + }); afterEach(afterTest); // T1293181 @@ -97,4 +101,164 @@ describe('DataGrid editing', () => { expect(rows[recoveringRowIndex].data).toEqual(dataSource[recoveringRowIndex]); }); }); + + describe('Internal state cleanup after save', () => { + it('should clear internal state after updating and saving row', async () => { + const { instance } = await createDataGrid({ + keyExpr: 'ID', + dataSource, + editing: { + mode: 'batch', + allowUpdating: true, + }, + }); + + // Edit row using cellValue + instance.cellValue(0, 'FirstName', 'Updated'); + const editingController = instance.getController('editing'); + + // Verify internal state has entries before save + expect(editingController.getInternalStateSize()).toBe(1); + + // Save changes + instance.saveEditData(); + await flushAsync(); + + // Check internal state is cleared after save + expect(editingController.getInternalStateSize()).toBe(0); + }); + + it('should clear internal state after adding and saving row', async () => { + const { instance } = await createDataGrid({ + keyExpr: 'ID', + dataSource, + editing: { + mode: 'batch', + allowAdding: true, + }, + }); + + // Add new row + instance.addRow(); + await flushAsync(); + + const editingController = instance.getController('editing'); + expect(editingController.getInternalStateSize()).toBe(1); + + // Save changes + instance.saveEditData(); + await flushAsync(); + + // Check internal state is cleared + expect(editingController.getInternalStateSize()).toBe(0); + }); + + it('should clear internal state after deleting and saving row', async () => { + const { instance } = await createDataGrid({ + keyExpr: 'ID', + dataSource, + editing: { + mode: 'batch', + allowDeleting: true, + }, + }); + + // Delete row + instance.deleteRow(1); + + const editingController = instance.getController('editing'); + expect(editingController.getInternalStateSize()).toBe(1); + + // Save changes + instance.saveEditData(); + await flushAsync(); + + // Check internal state is cleared + expect(editingController.getInternalStateSize()).toBe(0); + }); + + it('should clear internal state for multiple operations after save', async () => { + const { instance } = await createDataGrid({ + keyExpr: 'ID', + dataSource, + editing: { + mode: 'batch', + allowUpdating: true, + allowAdding: true, + allowDeleting: true, + }, + }); + + // Multiple operations + instance.cellValue(0, 'FirstName', 'Updated'); + instance.deleteRow(1); + instance.addRow(); + await flushAsync(); + + const editingController = instance.getController('editing'); + expect(editingController.getInternalStateSize()).toBe(3); + + // Save all changes + instance.saveEditData(); + await flushAsync(); + + // Check internal state is completely cleared + expect(editingController.getInternalStateSize()).toBe(0); + }); + + it('should clear internal state when canceling changes', async () => { + const { instance } = await createDataGrid({ + keyExpr: 'ID', + dataSource, + editing: { + mode: 'batch', + allowUpdating: true, + allowAdding: true, + }, + }); + + // Make some changes + instance.cellValue(0, 'FirstName', 'Updated'); + instance.addRow(); + await flushAsync(); + + const editingController = instance.getController('editing'); + const stateBeforeCancel = editingController.getInternalStateSize(); + expect(stateBeforeCancel).toBe(2); + + // Cancel changes + instance.cancelEditData(); + + // Check internal state is cleared + expect(editingController.getInternalStateSize()).toBe(0); + }); + + it('should preserve internal state when save fails', async () => { + const failingDataSource = new CustomStore({ + key: 'ID', + load: () => Promise.resolve([...dataSource]), + update: () => Promise.reject(new Error('Save failed')), + }); + + const { instance } = await createDataGrid({ + keyExpr: 'ID', + dataSource: failingDataSource, + editing: { + mode: 'batch', + allowUpdating: true, + }, + }); + + instance.cellValue(0, 'FirstName', 'Updated'); + + const editingController = instance.getController('editing'); + + expect(editingController.getInternalStateSize()).toBe(1); + + instance.saveEditData(); + await flushAsync(); + + expect(editingController.getInternalStateSize()).toBe(1); + }); + }); }); diff --git a/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing.ts b/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing.ts index b19c278be7fa..58f852772e41 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing.ts @@ -1326,6 +1326,13 @@ class EditingControllerImpl extends modules.ViewController { this._internalState.delete(getKeyHash(key)); } + private updateInternalDataKey(oldKey, newKey) { + const internalData = this._getInternalData(oldKey) ?? {}; + + this._removeInternalData(oldKey); + this._addInternalData({ ...internalData, key: newKey }); + } + private _updateInsertAfterOrBeforeKeys(changes, index) { const removeChange = changes[index]; @@ -1612,7 +1619,10 @@ class EditingControllerImpl extends modules.ViewController { params = { data, cancel: false }; deferred = this._executeEditingAction('onRowInserting', params, () => store.insert(params.data).done((data, key) => { if (isDefined(key)) { + const initialKey = changeCopy.key; + changeCopy.key = key; + this.updateInternalDataKey(initialKey, key); } if (data && isObject(data) && data !== params.data) { changeCopy.data = data; @@ -1715,17 +1725,17 @@ class EditingControllerImpl extends modules.ViewController { private _fireSaveEditDataEvents(changes) { each(changes, (_, { data, key, type }) => { - const internalData = this._addInternalData({ key }); + const internalData = this._getInternalData(key); const params: any = { key, data }; - if (internalData.error) { + if (internalData?.error) { params.error = internalData.error; } // eslint-disable-next-line default-case switch (type) { case DATA_EDIT_DATA_REMOVE_TYPE: - this.executeAction('onRowRemoved', extend({}, params, { data: internalData.oldData })); + this.executeAction('onRowRemoved', extend({}, params, { data: internalData?.oldData })); break; case DATA_EDIT_DATA_INSERT_TYPE: this.executeAction('onRowInserted', params); @@ -1734,6 +1744,8 @@ class EditingControllerImpl extends modules.ViewController { this.executeAction('onRowUpdated', params); break; } + + this._removeInternalData(key); }); this.executeAction('onSaved', { changes }); @@ -2485,6 +2497,12 @@ class EditingControllerImpl extends modules.ViewController { || !parameters.row.isEditing ); } + + /// #DEBUG + public getInternalStateSize(): number { + return this._internalState.size; + } + /// #ENDDEBUG } export type EditingController = EditingControllerImpl From 05dac8e8ad86e3b787cb0979e50e134bc86ac2df Mon Sep 17 00:00:00 2001 From: Alyar <> Date: Fri, 6 Feb 2026 12:34:52 +0400 Subject: [PATCH 2/4] add testcafe test --- .../dataGrid/common/editing/functional.ts | 93 +++++++++++++++++++ .../testcafe-models/dataGrid/editPopup.ts | 4 + packages/testcafe-models/dataGrid/index.ts | 17 +++- 3 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 packages/testcafe-models/dataGrid/editPopup.ts diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/editing/functional.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/editing/functional.ts index b8df5f523f83..1a83cc1e8a3b 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/editing/functional.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/editing/functional.ts @@ -2149,6 +2149,99 @@ test('The onEditorPreparing event should be called once after clicking on a cell })(); }); +test('Adding rows to a second page should work correctly when initial row values ​​are specified in the onInitNewRow method (T1274123)', async (t) => { + // arrange + const dataGrid = new DataGrid('#container'); + const addAndSaveData = async () => { + // act + await dataGrid.apiAddRow(); + + const editPopup = dataGrid.getEditPopup(); + + // assert + await t + .expect(editPopup.isVisible()) + .ok(); + + // act + await dataGrid.apiSaveEditData(); + + // assert + await t + .expect(dataGrid.isReady()) + .ok() + .expect(editPopup.isVisible()) + .notOk(); + }; + + await t.expect(dataGrid.isReady()).ok(); + + // act + await dataGrid.apiPageIndex(1); + await t.expect(dataGrid.isReady()).ok(); + + // assert + let visibleRows = await dataGrid.apiGetVisibleRows(); + + await t.expect(visibleRows.length) + .eql(10) + .expect(dataGrid.getDataCell(20, 0).element.textContent) + .eql('21'); + + // act + await addAndSaveData(); + await addAndSaveData(); + + // assert + visibleRows = await dataGrid.apiGetVisibleRows(); + + await t + .expect(visibleRows.length) + .eql(12) + .expect(visibleRows[10].key) + .eql(31) + .expect(visibleRows[11].key) + .eql(32); +}).before(async () => { + await ClientFunction(() => { + (window as any).myData = new Array(30).fill(null).map((_, index) => ({ id: index + 1, text: `item ${index + 1}` })); + (window as any).myStore = new (window as any).DevExpress.data.ArrayStore({ + key: 'id', + data: (window as any).myData, + }); + })(); + + return createWidget('dxDataGrid', { + dataSource: { + key: 'id', + load(loadOptions) { + return (window as any).myStore.load(loadOptions); + }, + totalCount() { + return (window as any).myStore.totalCount(); + }, + insert(values) { + if (values.id === 0) { + values.id = (window as any).myData.length + 1; + } + + return (window as any).myStore.insert(values); + }, + } as any, + columns: ['id', 'text'], + showBorders: true, + editing: { + mode: 'popup', + allowAdding: true, + }, + onInitNewRow(e) { + e.data.id = 0; + e.data.text = 'test'; + }, + height: 300, + }); +}); + fixture`Editing - ShowEditorAlways` .page(url(__dirname, '../../../container.html')); diff --git a/packages/testcafe-models/dataGrid/editPopup.ts b/packages/testcafe-models/dataGrid/editPopup.ts new file mode 100644 index 000000000000..a86126cd03b7 --- /dev/null +++ b/packages/testcafe-models/dataGrid/editPopup.ts @@ -0,0 +1,4 @@ +import Popup from '../popup'; + +export default class EditPopup extends Popup { +} diff --git a/packages/testcafe-models/dataGrid/index.ts b/packages/testcafe-models/dataGrid/index.ts index 5bc809d69425..620f379a3a1b 100644 --- a/packages/testcafe-models/dataGrid/index.ts +++ b/packages/testcafe-models/dataGrid/index.ts @@ -24,6 +24,7 @@ import { GroupPanel } from './groupPanel'; import GridCore from '../gridCore'; import { CLASS as CLASS_BASE } from '../gridCore'; import { AIPromptEditor } from './aiPromptEditor'; +import EditPopup from './editPopup'; export const CLASS = { ...CLASS_BASE, @@ -444,6 +445,10 @@ export default class DataGrid extends GridCore { return new EditForm(element, buttons); } + getEditPopup(): EditPopup { + return new EditPopup(this.element.find(`.${this.addWidgetPrefix(CLASS.popupEdit)}`)); + } + getToolbar(): Toolbar { return new Toolbar(this.element.find(`.${CLASS.toolbar}`)); } @@ -748,12 +753,18 @@ export default class DataGrid extends GridCore { )(); } - apiPageIndex(): Promise { + apiPageIndex(pageIndex?: number): Promise { const { getInstance } = this; return ClientFunction( - () => (getInstance() as any).pageIndex(), - { dependencies: { getInstance } }, + () => { + if(pageIndex === undefined) { + return (getInstance() as any).pageIndex(); + } + + (getInstance() as any).pageIndex(pageIndex); + }, + { dependencies: { getInstance, pageIndex } }, )(); } From 66d0718e666a33edcb176e0d63848dee2ac11bd2 Mon Sep 17 00:00:00 2001 From: Alyar <> Date: Fri, 6 Feb 2026 18:24:45 +0400 Subject: [PATCH 3/4] Fix copilot comments --- packages/testcafe-models/dataGrid/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/testcafe-models/dataGrid/index.ts b/packages/testcafe-models/dataGrid/index.ts index 620f379a3a1b..d4033deb5748 100644 --- a/packages/testcafe-models/dataGrid/index.ts +++ b/packages/testcafe-models/dataGrid/index.ts @@ -753,12 +753,12 @@ export default class DataGrid extends GridCore { )(); } - apiPageIndex(pageIndex?: number): Promise { + apiPageIndex(pageIndex?: number): Promise { const { getInstance } = this; return ClientFunction( () => { - if(pageIndex === undefined) { + if (pageIndex === undefined) { return (getInstance() as any).pageIndex(); } From a2bca062fde5a04322a3e858fccfe42b3f6759ee Mon Sep 17 00:00:00 2001 From: Alyar <> Date: Fri, 6 Feb 2026 18:27:57 +0400 Subject: [PATCH 4/4] Add typing for _internalState --- .../grids/grid_core/editing/m_editing.ts | 50 +++++++++++-------- .../grids/grid_core/editing/types.ts | 15 +++++- 2 files changed, 43 insertions(+), 22 deletions(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing.ts b/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing.ts index 58f852772e41..032d16072c1b 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing.ts @@ -7,7 +7,7 @@ import { removeEvent } from '@js/common/core/events/remove'; import { addNamespace } from '@js/common/core/events/utils/index'; import messageLocalization from '@js/common/core/localization/message'; import { createObjectWithChanges } from '@js/common/data/array_utils'; -import type { GridsEditMode } from '@js/common/grids'; +import type { DataChange, GridsEditMode } from '@js/common/grids'; import devices from '@js/core/devices'; import domAdapter from '@js/core/dom_adapter'; import Guid from '@js/core/guid'; @@ -33,7 +33,9 @@ import type { HeaderPanel } from '@ts/grids/grid_core/header_panel/m_header_pane import type { RowsView } from '@ts/grids/grid_core/views/m_rows_view'; import modules from '../m_modules'; -import type { Controllers, ModuleType, Views } from '../m_types'; +import type { + Controllers, ModuleType, RowKey, Views, +} from '../m_types'; import gridCoreUtils from '../m_utils'; import { ACTION_OPTION_NAMES, @@ -92,7 +94,9 @@ import { isEditingCell, isEditingOrShowEditorAlwaysDataCell, } from './m_editing_utils'; -import type { NormalizedEditCellOptions } from './types'; +import type { + InsertInfo, InternalEditData, NormalizedEditCellOptions, +} from './types'; class EditingControllerImpl extends modules.ViewController { protected _columnsController!: Controllers['columns']; @@ -133,7 +137,7 @@ class EditingControllerImpl extends modules.ViewController { protected _saveEditorHandler: any; - private _internalState!: Map; + private _internalState!: Map; protected _refocusEditCell: any; @@ -274,18 +278,19 @@ class EditingControllerImpl extends modules.ViewController { } } - private _getInternalData(key) { + private _getInternalData(key: RowKey): InternalEditData | undefined { return this._internalState.get(getKeyHash(key)); } - public _addInternalData(params) { + public _addInternalData(params: InternalEditData): InternalEditData { const internalData = this._getInternalData(params.key); if (internalData) { - return extend(internalData, params); + return extend(internalData, params) as InternalEditData; } this._internalState.set(getKeyHash(params.key), params); + return params; } @@ -774,6 +779,7 @@ class EditingControllerImpl extends modules.ViewController { this.update(changeType); const changes = this.getChanges(); + changes.forEach((change) => { const isInsert = change.type === DATA_EDIT_DATA_INSERT_TYPE; @@ -783,10 +789,10 @@ class EditingControllerImpl extends modules.ViewController { let { key } = change; - let insertInfo = this._getInternalData(key)?.insertInfo; + const insertInfo = this._getInternalData(key)?.insertInfo; + if (!isDefined(key) || !isDefined(insertInfo)) { - insertInfo = this._addInsertInfo(change); - key = insertInfo.key; + key = this._addInsertInfo(change).key; } const loadedRowIndex = this._getLoadedRowIndex(items, change); @@ -857,20 +863,22 @@ class EditingControllerImpl extends modules.ViewController { } } - private _createInsertInfo() { - const insertInfo = {}; - - insertInfo[INSERT_INDEX] = this._getInsertIndex(); - - return insertInfo; + private _createInsertInfo(): InsertInfo { + return { [INSERT_INDEX]: this._getInsertIndex() }; } - private _addInsertInfo(change, parentKey?) { - let insertInfo; + private _addInsertInfo( + change: Partial, + parentKey?: RowKey, + ): { insertInfo: InsertInfo; key: RowKey } { + let insertInfo: InsertInfo | undefined; + change.key = this.getChangeKeyValue(change); + const { key } = change; insertInfo = this._getInternalData(key)?.insertInfo; + if (!isDefined(insertInfo)) { const insertAfterOrBeforeKey = this._getInsertAfterOrBeforeKey(change); @@ -1042,7 +1050,7 @@ class EditingControllerImpl extends modules.ViewController { * @exteded: TreeList's editing */ protected _addRowCore(data, parentKey, initialOldEditRowIndex) { - const change = { data, type: DATA_EDIT_DATA_INSERT_TYPE }; + const change: Partial = { data, type: DATA_EDIT_DATA_INSERT_TYPE }; const editRowIndex = this._getVisibleEditRowIndex(); const insertInfo = this._addInsertInfo(change, parentKey); const { key } = insertInfo; @@ -1322,11 +1330,11 @@ class EditingControllerImpl extends modules.ViewController { return buttonConfig; } - private _removeInternalData(key) { + private _removeInternalData(key: RowKey): void { this._internalState.delete(getKeyHash(key)); } - private updateInternalDataKey(oldKey, newKey) { + private updateInternalDataKey(oldKey: RowKey, newKey: RowKey): void { const internalData = this._getInternalData(oldKey) ?? {}; this._removeInternalData(oldKey); diff --git a/packages/devextreme/js/__internal/grids/grid_core/editing/types.ts b/packages/devextreme/js/__internal/grids/grid_core/editing/types.ts index a3602f14732b..e3360e331618 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/editing/types.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/editing/types.ts @@ -1,5 +1,7 @@ import type { Column } from '../columns_controller/m_columns_controller'; -import type { Item } from '../data_controller/m_data_controller'; +import type { Item, UserData } from '../data_controller/m_data_controller'; +import type { RowKey } from '../m_types'; +import type { INSERT_INDEX } from './const'; export interface NormalizedEditCellOptions { item: Item; @@ -9,3 +11,14 @@ export interface NormalizedEditCellOptions { oldRowIndex: number; rowIndex: number; } + +export interface InsertInfo { + [INSERT_INDEX]: number; +} + +export interface InternalEditData { + key: RowKey; + oldData?: UserData; + insertInfo?: InsertInfo; + error?: Error; +}