diff --git a/e2e/testcafe-devextreme/tests/dataGrid/editing/editingEvents.ts b/e2e/testcafe-devextreme/tests/dataGrid/editing/editingEvents.ts index dcfe2120266d..6823bab89087 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/editing/editingEvents.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/editing/editingEvents.ts @@ -1,6 +1,8 @@ /* eslint-disable @typescript-eslint/no-misused-promises */ -import { ClientFunction } from 'testcafe'; +import { ClientFunction, Selector } from 'testcafe'; import DataGrid from 'devextreme-testcafe-models/dataGrid'; +import { Overlay } from 'devextreme-testcafe-models/dataGrid/overlay'; +import { createScreenshotsComparer } from 'devextreme-screenshot-comparer'; import { createWidget } from '../../../helpers/createWidget'; import url from '../../../helpers/getPageUrl'; @@ -172,3 +174,52 @@ testCases.forEach(({ caseName, expected, onRowRemoving }) => { }); }); }); + +// T1250405 +test('DataGrid - Canceled rows are hidden when multiple rows are added in batch mode', async (t) => { + const dataGrid = new DataGrid('#container'); + const { takeScreenshot, compareResults } = createScreenshotsComparer(t); + const addBtn = dataGrid.getToolbar().getItem(); + const saveBtn = dataGrid.getToolbar().getItem(1); + await t.expect(dataGrid.isReady()).ok(); + await t + .click(addBtn) + .pressKey('1') + .click(addBtn) + .pressKey('2') + .click(saveBtn); + + const overlay1 = new Overlay(Selector('.dx-overlay-wrapper'), 0); + const overlay2 = new Overlay(Selector('.dx-overlay-wrapper'), 1); + const cancelBtn = overlay2.getToolbar(1).getItem(1); + const saveBtnPopup = overlay1.getToolbar(1).getItem(0); + await t + .click(cancelBtn) + .click(saveBtnPopup); + await t + .expect(await takeScreenshot('T1250405-canceled-rows-hidden.png', dataGrid.element)) + .ok() + .expect(compareResults.isValid()) + .ok(compareResults.errorMessages()); +}).before(async () => { + await createWidget('dxDataGrid', { + dataSource: [ + { ID: 1, Text: 'Item 1' }, + ], + keyExpr: 'ID', + columns: ['Text'], + editing: { + mode: 'batch', + allowAdding: true, + }, + onRowInserting(e: any) { + e.cancel = new Promise((resolve) => { + const dialog = (window as any).DevExpress.ui.dialog.confirm( + 'Are you sure?', + 'Confirm changes', + ); + dialog.done((confirm) => resolve(!confirm)); + }); + }, + }); +}); diff --git a/e2e/testcafe-devextreme/tests/dataGrid/editing/etalons/T1250405-canceled-rows-hidden.png b/e2e/testcafe-devextreme/tests/dataGrid/editing/etalons/T1250405-canceled-rows-hidden.png new file mode 100644 index 000000000000..528a1bafdd4e Binary files /dev/null and b/e2e/testcafe-devextreme/tests/dataGrid/editing/etalons/T1250405-canceled-rows-hidden.png differ diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts index ce924bf71a0e..58ffb026ab32 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts @@ -80,6 +80,7 @@ interface Item { values?: unknown[]; visible?: boolean; isExpanded?: boolean; + isNewRow?: boolean; summaryCells?: unknown[]; rowIndex?: number; cells?: unknown[]; diff --git a/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing.ts b/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing.ts index f2b311cfa752..1e6fcbf1f082 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 @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/prefer-for-of */ /* eslint-disable @typescript-eslint/no-unused-vars, max-classes-per-file */ import { name as clickEventName } from '@js/common/core/events/click'; import eventsEngine from '@js/common/core/events/core/events_engine'; @@ -1336,10 +1337,12 @@ class EditingControllerImpl extends modules.ViewController { const removeChange = changes[index]; changes.forEach((change) => { - const insertAfterOrBeforeKey = this._getInsertAfterOrBeforeKey(change); + if (change.type === DATA_EDIT_DATA_INSERT_TYPE) { + const insertAfterOrBeforeKey = this._getInsertAfterOrBeforeKey(change); - if (equalByValue(insertAfterOrBeforeKey, removeChange.key)) { - change[isDefined(change.insertAfterKey) ? 'insertAfterKey' : 'insertBeforeKey'] = this._getInsertAfterOrBeforeKey(removeChange); + if (equalByValue(insertAfterOrBeforeKey, removeChange.key)) { + change[isDefined(change.insertAfterKey) ? 'insertAfterKey' : 'insertBeforeKey'] = this._getInsertAfterOrBeforeKey(removeChange); + } } }); } @@ -1679,7 +1682,8 @@ class EditingControllerImpl extends modules.ViewController { private _processSaveEditDataResult(results) { let hasSavedData = false; - const changes = [...this.getChanges()]; + const originalChanges = this.getChanges(); + const changes = [...originalChanges]; const changesLength = changes.length; for (let i = 0; i < results.length; i++) { @@ -1699,6 +1703,9 @@ class EditingControllerImpl extends modules.ViewController { } } else if (this._processRemove(changes, editIndex, cancel)) { hasSavedData = !cancel; + const removedChangeIndex = gridCoreUtils.getIndexByKey(results[i].key, originalChanges); + + this._updateInsertAfterOrBeforeKeys(originalChanges, removedChangeIndex); } } diff --git a/packages/testcafe-models/dataGrid/overlay.ts b/packages/testcafe-models/dataGrid/overlay.ts index 663a8745b07c..b593fe0b6203 100644 --- a/packages/testcafe-models/dataGrid/overlay.ts +++ b/packages/testcafe-models/dataGrid/overlay.ts @@ -1,10 +1,12 @@ import { Selector } from 'testcafe'; +import Toolbar from '../toolbar'; const CLASS = { overlayWrapper: 'dx-overlay-wrapper', overlayContent: 'dx-overlay-content', invalidMessage: 'dx-invalid-message', revertTooltip: 'dx-datagrid-revert-tooltip', + toolbar: 'dx-toolbar', checkbox: 'dx-checkbox', }; @@ -13,8 +15,10 @@ export class Overlay { content: Selector; - constructor(id?: Selector) { - this.element = id ?? Selector(`.${CLASS.overlayWrapper}`); + constructor(id?: Selector, index?: number) { + this.element = id + ? (index ? id.nth(index) : id) + : Selector(`.${CLASS.overlayWrapper}`).nth(index ? index : 0); this.content = this.element.find(`.${CLASS.overlayContent}`); } @@ -30,4 +34,8 @@ export class Overlay { getPopupCheckbox(): Selector { return this.element.find(`.${CLASS.overlayContent} .${CLASS.checkbox}`); } + + getToolbar(idx?: number): Toolbar { + return new Toolbar(this.element.find(`.${CLASS.toolbar}`).nth(idx ? idx : 0)); + } }