From 6f877d102ee50701dcbcbb465c99ec05fbe1cd04 Mon Sep 17 00:00:00 2001 From: tongsonbarbs Date: Fri, 13 Dec 2024 00:32:38 +0800 Subject: [PATCH] DataGrid - Canceled rows are hidden when multiple rows are added in batch mode (T1250405) (#28501) --- .../etalons/T1250405-canceled-rows-hidden.png | Bin 0 -> 4174 bytes .../data_controller/m_data_controller.ts | 1 + .../grids/grid_core/editing/m_editing.ts | 15 +++-- .../testcafe/model/dataGrid/overlay.ts | 15 ++++- .../tests/dataGrid/editing/editingEvents.ts | 53 +++++++++++++++++- 5 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 e2e/testcafe-devextreme/tests/dataGrid/editing/etalons/T1250405-canceled-rows-hidden.png 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 0000000000000000000000000000000000000000..528a1bafdd4e90cc8a53864b7114e56097a9d1bb GIT binary patch literal 4174 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 CM*a@~ literal 0 HcmV?d00001 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 b1fba2ee066f..fd3c348c1475 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 49391db27258..0b4613b1c31b 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 type { GridsEditMode } from '@js/common/grids'; import devices from '@js/core/devices'; @@ -1337,10 +1338,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); + } } }); } @@ -1680,7 +1683,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++) { @@ -1700,6 +1704,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/devextreme/testing/testcafe/model/dataGrid/overlay.ts b/packages/devextreme/testing/testcafe/model/dataGrid/overlay.ts index 663a8745b07c..1fcb1cfd92a0 100644 --- a/packages/devextreme/testing/testcafe/model/dataGrid/overlay.ts +++ b/packages/devextreme/testing/testcafe/model/dataGrid/overlay.ts @@ -1,10 +1,15 @@ +/* eslint-disable @typescript-eslint/no-extra-parens */ +/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ +/* eslint-disable no-nested-ternary */ 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 +18,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 || 0); this.content = this.element.find(`.${CLASS.overlayContent}`); } @@ -30,4 +37,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 || 0)); + } } diff --git a/packages/devextreme/testing/testcafe/tests/dataGrid/editing/editingEvents.ts b/packages/devextreme/testing/testcafe/tests/dataGrid/editing/editingEvents.ts index c15527b3e201..dfb526de61e6 100644 --- a/packages/devextreme/testing/testcafe/tests/dataGrid/editing/editingEvents.ts +++ b/packages/devextreme/testing/testcafe/tests/dataGrid/editing/editingEvents.ts @@ -1,5 +1,7 @@ /* eslint-disable @typescript-eslint/no-misused-promises */ -import { ClientFunction } from 'testcafe'; +import { ClientFunction, Selector } from 'testcafe'; +import { createScreenshotsComparer } from 'devextreme-screenshot-comparer'; +import { Overlay } from '../../../model/dataGrid/overlay'; import { createWidget } from '../../../helpers/createWidget'; import url from '../../../helpers/getPageUrl'; import DataGrid from '../../../model/dataGrid'; @@ -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)); + }); + }, + }); +});