From 698e40e45b7c9490659d39feee56dfae174e6776 Mon Sep 17 00:00:00 2001 From: ivanblinov2k17 Date: Tue, 9 Apr 2024 11:17:52 +0400 Subject: [PATCH 01/14] fix Core - deepExtendArraySafe can set object field as undefined Add flag shouldCopyUndefined Pass new flag into recursive call DataGrid(T1226946): add new deepExtendArraySafe option support Make the deepExtendArraySafe copy undefined if prevValue is undefined update test DataGrid(T1226946): fix incorrect test case Modify array store to copy undefined Fix to not copy nested undefined if shouldCopyUndefined is false --- .../tests/dataGrid/editing/undefinedValues.ts | 40 +++++++++++++++++++ packages/devextreme/js/core/utils/object.js | 8 ++-- packages/devextreme/js/data/array_utils.js | 6 +-- .../DevExpress.core/utils.object.tests.js | 20 ++++++++-- .../tests/DevExpress.data/storeArray.tests.js | 21 ++++++++++ packages/testcafe-models/dataGrid/index.ts | 2 +- 6 files changed, 87 insertions(+), 10 deletions(-) create mode 100644 e2e/testcafe-devextreme/tests/dataGrid/editing/undefinedValues.ts diff --git a/e2e/testcafe-devextreme/tests/dataGrid/editing/undefinedValues.ts b/e2e/testcafe-devextreme/tests/dataGrid/editing/undefinedValues.ts new file mode 100644 index 000000000000..ec3267d421df --- /dev/null +++ b/e2e/testcafe-devextreme/tests/dataGrid/editing/undefinedValues.ts @@ -0,0 +1,40 @@ +import DataGrid from 'devextreme-testcafe-models/dataGrid'; +import { createWidget } from '../../../helpers/createWidget'; +import url from '../../../helpers/getPageUrl'; + +fixture`Editing - undefined values` + .disablePageReloads + .page(url(__dirname, '../../container.html')); + +test('Should properly set nested undefined values (T1226946)', async (t) => { + const dataGrid = new DataGrid('#container'); + const firstCell = dataGrid.getDataCell(0, 0); + const secondCell = dataGrid.getDataCell(1, 0); + + await t.expect(firstCell.element().textContent).eql('100'); + await t.expect(secondCell.element().textContent).eql('undefined'); + + await dataGrid.apiCellValue(0, 0, { data: undefined }); + await dataGrid.apiSaveEditData(); + + await t.expect(firstCell.element().textContent).eql('undefined'); + await t.expect(secondCell.element().textContent).eql('undefined'); +}).before(async () => createWidget('dxDataGrid', { + dataSource: [{ + id: 0, + value: { + data: 100, + }, + }, { + id: 1, + value: { + data: undefined, + }, + }], + keyExpr: 'id', + columns: [{ + dataField: 'value', + customizeText: (cellInfo) => String(cellInfo.value.data ?? 'undefined'), + }], + showBorders: true, +})); diff --git a/packages/devextreme/js/core/utils/object.js b/packages/devextreme/js/core/utils/object.js index a86f93e70dff..26f92601ea9c 100644 --- a/packages/devextreme/js/core/utils/object.js +++ b/packages/devextreme/js/core/utils/object.js @@ -48,7 +48,7 @@ const assignValueToProperty = function(target, property, value, assignByReferenc }; // B239679, http://bugs.jquery.com/ticket/9477 -const deepExtendArraySafe = function(target, changes, extendComplexObject, assignByReference) { +const deepExtendArraySafe = function(target, changes, extendComplexObject, assignByReference, shouldCopyUndefined) { let prevValue; let newValue; @@ -62,10 +62,12 @@ const deepExtendArraySafe = function(target, changes, extendComplexObject, assig if(isPlainObject(newValue)) { const goDeeper = extendComplexObject ? isObject(prevValue) : isPlainObject(prevValue); - newValue = deepExtendArraySafe(goDeeper ? prevValue : {}, newValue, extendComplexObject, assignByReference); + newValue = deepExtendArraySafe(goDeeper ? prevValue : {}, newValue, extendComplexObject, assignByReference, shouldCopyUndefined); } - if(newValue !== undefined && prevValue !== newValue) { + if(shouldCopyUndefined && (prevValue !== newValue || prevValue === undefined)) { + assignValueToProperty(target, name, newValue, assignByReference); + } else if((newValue !== undefined) && prevValue !== newValue) { assignValueToProperty(target, name, newValue, assignByReference); } } diff --git a/packages/devextreme/js/data/array_utils.js b/packages/devextreme/js/data/array_utils.js index 22de890b5638..abf576b59f95 100644 --- a/packages/devextreme/js/data/array_utils.js +++ b/packages/devextreme/js/data/array_utils.js @@ -89,7 +89,7 @@ function cloneInstanceWithChangedPaths(instance, changes, clonedInstances) { } const instanceWithoutPrototype = { ...instance }; - deepExtendArraySafe(result, instanceWithoutPrototype, true, true); + deepExtendArraySafe(result, instanceWithoutPrototype, true, true, true); for(const name in instanceWithoutPrototype) { const value = instanceWithoutPrototype[name]; @@ -115,7 +115,7 @@ function cloneInstanceWithChangedPaths(instance, changes, clonedInstances) { function createObjectWithChanges(target, changes) { const result = cloneInstanceWithChangedPaths(target, changes); - return deepExtendArraySafe(result, changes, true, true); + return deepExtendArraySafe(result, changes, true, true, true); } function applyBatch({ keyInfo, data, changes, groupCount, useInsertIndex, immutable, disableCache, logError, skipCopying }) { @@ -186,7 +186,7 @@ function update(keyInfo, array, key, data, isBatch, immutable, logError) { target = key; } - deepExtendArraySafe(target, data, extendComplexObject); + deepExtendArraySafe(target, data, extendComplexObject, false, true); if(!isBatch) { if(config().useLegacyStoreResult) { return trivialPromise(key, data); diff --git a/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js b/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js index 585a4a1fd7cf..963d6ce242da 100644 --- a/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js @@ -171,7 +171,21 @@ QUnit.test('deepExtendArraySafe utility does not throw an error with \'null\' de assert.equal(result.deepProp.toChange, 'changed value'); }); -QUnit.test('deepExtendArraySafe utility does not pollute object prototype', function(assert) { - objectUtils.deepExtendArraySafe({ }, JSON.parse('{ "__proto__": { "pollution": true }}'), true); - assert.ok(!('pollution' in { }), 'object prototype is not polluted'); +QUnit.test('deepExtendArraySafe sets undefined', function(assert) { + const objWithValue = { time: { duration: 50 } }; + const objNoValue = {}; + objectUtils.deepExtendArraySafe(objWithValue, { time: { duration: undefined } }, true); + objectUtils.deepExtendArraySafe(objNoValue, { time: { duration: undefined } }, true); + + assert.equal(objWithValue.time.duration, 50); + assert.notOk(Object.prototype.hasOwnProperty.call(objNoValue.time, 'duration')); + + objectUtils.deepExtendArraySafe(objWithValue, { time: { duration: undefined } }, true, false, true); + objectUtils.deepExtendArraySafe(objNoValue, { time: { duration: undefined } }, true, false, true); + + assert.equal(objWithValue.time.duration, undefined); + assert.ok(Object.prototype.hasOwnProperty.call(objNoValue.time, 'duration')); + }); + + diff --git a/packages/devextreme/testing/tests/DevExpress.data/storeArray.tests.js b/packages/devextreme/testing/tests/DevExpress.data/storeArray.tests.js index 31102b9710c0..8bcbaf089e7e 100644 --- a/packages/devextreme/testing/tests/DevExpress.data/storeArray.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.data/storeArray.tests.js @@ -787,6 +787,27 @@ QUnit.test('update with key', function(assert) { }); +QUnit.test('update with explicit undefined', function(assert) { + const done = assert.async(); + + const store = new ArrayStore({ + key: 'id', + data: [{ id: 0, nested: { a: 1 } }], + }); + + store.update(0, { nested: { a: undefined } }).done(function(data, key) { + assert.equal(key, 0); + + const expectedData = { + nested: { a: undefined }, + id: key + }; + + assert.deepEqual(data, expectedData); + done(); + }); +}); + QUnit.test('insert duplicate key (simple)', function(assert) { const done = assert.async(); diff --git a/packages/testcafe-models/dataGrid/index.ts b/packages/testcafe-models/dataGrid/index.ts index 8f8e04973a61..177a1b330299 100644 --- a/packages/testcafe-models/dataGrid/index.ts +++ b/packages/testcafe-models/dataGrid/index.ts @@ -481,7 +481,7 @@ export default class DataGrid extends Widget { )(); } - apiCellValue(rowIndex: number, columnIndex: number, value: string): Promise { + apiCellValue(rowIndex: number, columnIndex: number, value: T): Promise { const { getInstance } = this; return ClientFunction( () => (getInstance() as any).cellValue(rowIndex, columnIndex, value), From d3013b1d2b660b43af8638f2c55fcb3eb6da96f2 Mon Sep 17 00:00:00 2001 From: ivanblinov2k17 Date: Tue, 9 Apr 2024 11:17:52 +0400 Subject: [PATCH 02/14] fix Core - deepExtendArraySafe can set object field as undefined Add flag shouldCopyUndefined Pass new flag into recursive call DataGrid(T1226946): add new deepExtendArraySafe option support Make the deepExtendArraySafe copy undefined if prevValue is undefined update test Core - deepExtendArraySafe - shallow -> deep copying of arrays --- .../tests/dataGrid/editing/undefinedValues.ts | 39 +++++++++++++++++ packages/devextreme/js/core/utils/object.js | 42 ++++++++++++++++--- packages/devextreme/js/data/array_utils.js | 4 +- .../DevExpress.core/utils.object.tests.js | 32 ++++++++++++-- packages/testcafe-models/dataGrid/index.ts | 2 +- 5 files changed, 108 insertions(+), 11 deletions(-) create mode 100644 e2e/testcafe-devextreme/tests/dataGrid/editing/undefinedValues.ts diff --git a/e2e/testcafe-devextreme/tests/dataGrid/editing/undefinedValues.ts b/e2e/testcafe-devextreme/tests/dataGrid/editing/undefinedValues.ts new file mode 100644 index 000000000000..e71d9757b867 --- /dev/null +++ b/e2e/testcafe-devextreme/tests/dataGrid/editing/undefinedValues.ts @@ -0,0 +1,39 @@ +import DataGrid from 'devextreme-testcafe-models/dataGrid'; +import { createWidget } from '../../../helpers/createWidget'; +import url from '../../../helpers/getPageUrl'; + +fixture`Editing - undefined values` + .disablePageReloads + .page(url(__dirname, '../../container.html')); + +test('Should properly set nested undefined values (T1226946)', async (t) => { + const dataGrid = new DataGrid('#container'); + const firstCell = dataGrid.getDataCell(0, 0); + const secondCell = dataGrid.getDataCell(1, 0); + + await t.expect(firstCell.element().textContent).eql('100'); + await t.expect(secondCell.element().textContent).eql('undefined'); + + await dataGrid.apiCellValue(0, 0, { data: undefined }); + + await t.expect(firstCell.element().textContent).eql('undefined'); + await t.expect(secondCell.element().textContent).eql('undefined'); +}).before(async () => createWidget('dxDataGrid', { + dataSource: [{ + id: 0, + value: { + data: 100, + }, + }, { + id: 1, + value: { + data: undefined, + }, + }], + keyExpr: 'id', + columns: [{ + dataField: 'value', + customizeText: (cellInfo) => String(cellInfo.value.data ?? 'undefined'), + }], + showBorders: true, +})); diff --git a/packages/devextreme/js/core/utils/object.js b/packages/devextreme/js/core/utils/object.js index a86f93e70dff..e3124b603737 100644 --- a/packages/devextreme/js/core/utils/object.js +++ b/packages/devextreme/js/core/utils/object.js @@ -39,16 +39,29 @@ const orderEach = function(map, func) { } }; -const assignValueToProperty = function(target, property, value, assignByReference) { +const assignValueToProperty = function(target, property, value, extendComplexObject, assignByReference, shouldCopyUndefined) { if(!assignByReference && variableWrapper.isWrapped(target[property])) { variableWrapper.assign(target[property], value); + } else if(!assignByReference && Array.isArray(value)) { + target[property] = value.map(item => { + let itemTarget = item; + if(!assignByReference) { + if(Array.isArray(item)) { + itemTarget = []; + } + if(isObject(item)) { + itemTarget = {}; + } + } + return deepExtendArraySafe(itemTarget, item, extendComplexObject, assignByReference, shouldCopyUndefined); + }); } else { target[property] = value; } }; // B239679, http://bugs.jquery.com/ticket/9477 -const deepExtendArraySafe = function(target, changes, extendComplexObject, assignByReference) { +const deepExtendArraySafe = function(target, changes, extendComplexObject, assignByReference, shouldCopyUndefined) { let prevValue; let newValue; @@ -62,12 +75,31 @@ const deepExtendArraySafe = function(target, changes, extendComplexObject, assig if(isPlainObject(newValue)) { const goDeeper = extendComplexObject ? isObject(prevValue) : isPlainObject(prevValue); - newValue = deepExtendArraySafe(goDeeper ? prevValue : {}, newValue, extendComplexObject, assignByReference); + newValue = deepExtendArraySafe(goDeeper ? prevValue : {}, newValue, extendComplexObject, assignByReference, shouldCopyUndefined); } - if(newValue !== undefined && prevValue !== newValue) { - assignValueToProperty(target, name, newValue, assignByReference); + if(Array.isArray(newValue) && !assignByReference) { + assignValueToProperty(target, name, newValue, extendComplexObject, assignByReference, shouldCopyUndefined); } + + if(shouldCopyUndefined && (prevValue !== newValue || prevValue === undefined)) { + assignValueToProperty(target, name, newValue, extendComplexObject, assignByReference, shouldCopyUndefined); + } else if((newValue !== undefined) && prevValue !== newValue) { + assignValueToProperty(target, name, newValue, extendComplexObject, assignByReference, shouldCopyUndefined); + } + } + + if(Array.isArray(target) && !assignByReference) { + let changesTarget = changes; + if(!assignByReference) { + if(Array.isArray(changes)) { + changesTarget = []; + } + if(isObject(changes)) { + changesTarget = {}; + } + } + return deepExtendArraySafe(changesTarget, changes, extendComplexObject, assignByReference, shouldCopyUndefined); } return target; diff --git a/packages/devextreme/js/data/array_utils.js b/packages/devextreme/js/data/array_utils.js index 22de890b5638..3760eebb7cdd 100644 --- a/packages/devextreme/js/data/array_utils.js +++ b/packages/devextreme/js/data/array_utils.js @@ -89,7 +89,7 @@ function cloneInstanceWithChangedPaths(instance, changes, clonedInstances) { } const instanceWithoutPrototype = { ...instance }; - deepExtendArraySafe(result, instanceWithoutPrototype, true, true); + deepExtendArraySafe(result, instanceWithoutPrototype, true, true, true); for(const name in instanceWithoutPrototype) { const value = instanceWithoutPrototype[name]; @@ -115,7 +115,7 @@ function cloneInstanceWithChangedPaths(instance, changes, clonedInstances) { function createObjectWithChanges(target, changes) { const result = cloneInstanceWithChangedPaths(target, changes); - return deepExtendArraySafe(result, changes, true, true); + return deepExtendArraySafe(result, changes, true, true, true); } function applyBatch({ keyInfo, data, changes, groupCount, useInsertIndex, immutable, disableCache, logError, skipCopying }) { diff --git a/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js b/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js index 585a4a1fd7cf..ad0cdf419729 100644 --- a/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js @@ -171,7 +171,33 @@ QUnit.test('deepExtendArraySafe utility does not throw an error with \'null\' de assert.equal(result.deepProp.toChange, 'changed value'); }); -QUnit.test('deepExtendArraySafe utility does not pollute object prototype', function(assert) { - objectUtils.deepExtendArraySafe({ }, JSON.parse('{ "__proto__": { "pollution": true }}'), true); - assert.ok(!('pollution' in { }), 'object prototype is not polluted'); +QUnit.test('deepExtendArraySafe sets undefined', function(assert) { + const objWithValue = { time: { duration: 50 } }; + const objNoValue = {}; + objectUtils.deepExtendArraySafe(objWithValue, { time: { duration: undefined } }, true); + objectUtils.deepExtendArraySafe(objNoValue, { time: { duration: undefined } }, true); + + assert.equal(objWithValue.time.duration, 50); + assert.notOk(Object.prototype.hasOwnProperty.call(objNoValue.time, 'duration')); + + objectUtils.deepExtendArraySafe(objWithValue, { time: { duration: undefined } }, true, false, true); + objectUtils.deepExtendArraySafe(objNoValue, { time: { duration: undefined } }, true, false, true); + + assert.equal(objWithValue.time.duration, undefined); + assert.ok(Object.prototype.hasOwnProperty.call(objNoValue.time, 'duration')); +}); + + +QUnit.test('deepExtendArraySafe clones array inside object deeply', function(assert) { + const objWithValue = { time: undefined }; + const complexTime = { complexTime: 2 }; + const timeArray = [1, complexTime, 3]; + objectUtils.deepExtendArraySafe(objWithValue, { time: timeArray }, true); + + assert.deepEqual(objWithValue.time, timeArray); + assert.notStrictEqual(objWithValue.time, timeArray); + assert.notStrictEqual(objWithValue.time[1], complexTime); + timeArray[0] = 5; + assert.deepEqual(objWithValue.time, [1, complexTime, 3]); }); + diff --git a/packages/testcafe-models/dataGrid/index.ts b/packages/testcafe-models/dataGrid/index.ts index 8f8e04973a61..177a1b330299 100644 --- a/packages/testcafe-models/dataGrid/index.ts +++ b/packages/testcafe-models/dataGrid/index.ts @@ -481,7 +481,7 @@ export default class DataGrid extends Widget { )(); } - apiCellValue(rowIndex: number, columnIndex: number, value: string): Promise { + apiCellValue(rowIndex: number, columnIndex: number, value: T): Promise { const { getInstance } = this; return ClientFunction( () => (getInstance() as any).cellValue(rowIndex, columnIndex, value), From edaafb1e3d5afb537d75ce8d5ae20631c2d6b1ad Mon Sep 17 00:00:00 2001 From: ivanblinov2k17 Date: Fri, 19 Jul 2024 09:57:35 +0400 Subject: [PATCH 03/14] remove part that led to looping --- packages/devextreme/js/core/utils/object.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/packages/devextreme/js/core/utils/object.js b/packages/devextreme/js/core/utils/object.js index e3124b603737..cf11bed323c9 100644 --- a/packages/devextreme/js/core/utils/object.js +++ b/packages/devextreme/js/core/utils/object.js @@ -89,19 +89,6 @@ const deepExtendArraySafe = function(target, changes, extendComplexObject, assig } } - if(Array.isArray(target) && !assignByReference) { - let changesTarget = changes; - if(!assignByReference) { - if(Array.isArray(changes)) { - changesTarget = []; - } - if(isObject(changes)) { - changesTarget = {}; - } - } - return deepExtendArraySafe(changesTarget, changes, extendComplexObject, assignByReference, shouldCopyUndefined); - } - return target; }; From 1df5444fd164a8a91e56f1738aecc9acc1865379 Mon Sep 17 00:00:00 2001 From: ivanblinov2k17 Date: Fri, 19 Jul 2024 11:27:45 +0400 Subject: [PATCH 04/14] Fix lookup test + small refactor --- packages/devextreme/js/core/utils/object.js | 14 ++++++-------- .../tests/DevExpress.core/utils.object.tests.js | 2 +- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/devextreme/js/core/utils/object.js b/packages/devextreme/js/core/utils/object.js index cf11bed323c9..0494a0a49fe9 100644 --- a/packages/devextreme/js/core/utils/object.js +++ b/packages/devextreme/js/core/utils/object.js @@ -61,7 +61,7 @@ const assignValueToProperty = function(target, property, value, extendComplexObj }; // B239679, http://bugs.jquery.com/ticket/9477 -const deepExtendArraySafe = function(target, changes, extendComplexObject, assignByReference, shouldCopyUndefined) { +const deepExtendArraySafe = function(target, changes, extendComplexObject, assignByReference = true, shouldCopyUndefined) { let prevValue; let newValue; @@ -78,13 +78,11 @@ const deepExtendArraySafe = function(target, changes, extendComplexObject, assig newValue = deepExtendArraySafe(goDeeper ? prevValue : {}, newValue, extendComplexObject, assignByReference, shouldCopyUndefined); } - if(Array.isArray(newValue) && !assignByReference) { - assignValueToProperty(target, name, newValue, extendComplexObject, assignByReference, shouldCopyUndefined); - } - - if(shouldCopyUndefined && (prevValue !== newValue || prevValue === undefined)) { - assignValueToProperty(target, name, newValue, extendComplexObject, assignByReference, shouldCopyUndefined); - } else if((newValue !== undefined) && prevValue !== newValue) { + if( + Array.isArray(newValue) && !assignByReference || + shouldCopyUndefined && (prevValue !== newValue || prevValue === undefined) || + newValue !== undefined && prevValue !== newValue + ) { assignValueToProperty(target, name, newValue, extendComplexObject, assignByReference, shouldCopyUndefined); } } diff --git a/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js b/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js index ad0cdf419729..0c3a959d2c9c 100644 --- a/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js @@ -192,7 +192,7 @@ QUnit.test('deepExtendArraySafe clones array inside object deeply', function(ass const objWithValue = { time: undefined }; const complexTime = { complexTime: 2 }; const timeArray = [1, complexTime, 3]; - objectUtils.deepExtendArraySafe(objWithValue, { time: timeArray }, true); + objectUtils.deepExtendArraySafe(objWithValue, { time: timeArray }, true, false); assert.deepEqual(objWithValue.time, timeArray); assert.notStrictEqual(objWithValue.time, timeArray); From 39fdc05e210b28c62d7f0506e060017936916c20 Mon Sep 17 00:00:00 2001 From: ivanblinov2k17 Date: Fri, 19 Jul 2024 14:31:06 +0400 Subject: [PATCH 05/14] Fix KO + deepExtendArraySafe test --- .../testing/tests/DevExpress.knockout/objectUtils.tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devextreme/testing/tests/DevExpress.knockout/objectUtils.tests.js b/packages/devextreme/testing/tests/DevExpress.knockout/objectUtils.tests.js index 48dcb95770b2..8531858af2b2 100644 --- a/packages/devextreme/testing/tests/DevExpress.knockout/objectUtils.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.knockout/objectUtils.tests.js @@ -12,7 +12,7 @@ if(QUnit.urlParams['nocsp']) { QUnit.test('deepExtendArraySafe works correctly with array contain observables', function(assert) { const testObj = { id: 4, name: ko.observable('John') }; - const resultObj = objectUtils.deepExtendArraySafe(testObj, { name: 'Sue' }); + const resultObj = objectUtils.deepExtendArraySafe(testObj, { name: 'Sue' }, false, false); assert.equal(variableWrapper.isWrapped(resultObj.name), true, '\'name\' field is still observable'); assert.equal(resultObj.name(), 'Sue', 'New value accepted'); From 8960dc3d4fd3fdb0755ed45be7b9399aef1951d2 Mon Sep 17 00:00:00 2001 From: ivanblinov2k17 Date: Fri, 19 Jul 2024 16:29:04 +0400 Subject: [PATCH 06/14] Rm assignByReference true default value --- .../columns_controller/m_columns_controller_utils.ts | 10 +++++----- packages/devextreme/js/core/utils/object.js | 2 +- .../tests/DevExpress.core/utils.object.tests.js | 2 +- .../tests/DevExpress.knockout/objectUtils.tests.js | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller_utils.ts b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller_utils.ts index 1a719808363b..c5f9592d5b6d 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller_utils.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller_utils.ts @@ -56,11 +56,11 @@ export const createColumn = function (that: ColumnsController, columnOptions, us if (!columnOptions.type) { result = { headerId: `dx-col-${globalColumnId++}` }; } - result = deepExtendArraySafe(result, DEFAULT_COLUMN_OPTIONS); - deepExtendArraySafe(result, commonColumnOptions); - deepExtendArraySafe(result, calculatedColumnOptions); - deepExtendArraySafe(result, columnOptions); - deepExtendArraySafe(result, { selector: null }); + result = deepExtendArraySafe(result, DEFAULT_COLUMN_OPTIONS, false, true); + deepExtendArraySafe(result, commonColumnOptions, false, true); + deepExtendArraySafe(result, calculatedColumnOptions, false, true); + deepExtendArraySafe(result, columnOptions, false, true); + deepExtendArraySafe(result, { selector: null }, false, true); } if (columnOptions.filterOperations === columnOptions.defaultFilterOperations) { setFilterOperationsAsDefaultValues(result); diff --git a/packages/devextreme/js/core/utils/object.js b/packages/devextreme/js/core/utils/object.js index 0494a0a49fe9..b9bd07d04a27 100644 --- a/packages/devextreme/js/core/utils/object.js +++ b/packages/devextreme/js/core/utils/object.js @@ -61,7 +61,7 @@ const assignValueToProperty = function(target, property, value, extendComplexObj }; // B239679, http://bugs.jquery.com/ticket/9477 -const deepExtendArraySafe = function(target, changes, extendComplexObject, assignByReference = true, shouldCopyUndefined) { +const deepExtendArraySafe = function(target, changes, extendComplexObject, assignByReference, shouldCopyUndefined) { let prevValue; let newValue; diff --git a/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js b/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js index 0c3a959d2c9c..1cc6e5a5ded8 100644 --- a/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js @@ -188,7 +188,7 @@ QUnit.test('deepExtendArraySafe sets undefined', function(assert) { }); -QUnit.test('deepExtendArraySafe clones array inside object deeply', function(assert) { +QUnit.test('deepExtendArraySafe copies array into object property deeply', function(assert) { const objWithValue = { time: undefined }; const complexTime = { complexTime: 2 }; const timeArray = [1, complexTime, 3]; diff --git a/packages/devextreme/testing/tests/DevExpress.knockout/objectUtils.tests.js b/packages/devextreme/testing/tests/DevExpress.knockout/objectUtils.tests.js index 8531858af2b2..48dcb95770b2 100644 --- a/packages/devextreme/testing/tests/DevExpress.knockout/objectUtils.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.knockout/objectUtils.tests.js @@ -12,7 +12,7 @@ if(QUnit.urlParams['nocsp']) { QUnit.test('deepExtendArraySafe works correctly with array contain observables', function(assert) { const testObj = { id: 4, name: ko.observable('John') }; - const resultObj = objectUtils.deepExtendArraySafe(testObj, { name: 'Sue' }, false, false); + const resultObj = objectUtils.deepExtendArraySafe(testObj, { name: 'Sue' }); assert.equal(variableWrapper.isWrapped(resultObj.name), true, '\'name\' field is still observable'); assert.equal(resultObj.name(), 'Sue', 'New value accepted'); From daf331e62d551a34503821fa97d09093dc783834 Mon Sep 17 00:00:00 2001 From: ivanblinov2k17 Date: Mon, 22 Jul 2024 09:50:10 +0400 Subject: [PATCH 07/14] rm redundant if clause --- packages/devextreme/js/core/utils/object.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/devextreme/js/core/utils/object.js b/packages/devextreme/js/core/utils/object.js index b9bd07d04a27..ba58f9099c1c 100644 --- a/packages/devextreme/js/core/utils/object.js +++ b/packages/devextreme/js/core/utils/object.js @@ -45,14 +45,14 @@ const assignValueToProperty = function(target, property, value, extendComplexObj } else if(!assignByReference && Array.isArray(value)) { target[property] = value.map(item => { let itemTarget = item; - if(!assignByReference) { - if(Array.isArray(item)) { - itemTarget = []; - } - if(isObject(item)) { - itemTarget = {}; - } + + if(Array.isArray(item)) { + itemTarget = []; + } + if(isObject(item)) { + itemTarget = {}; } + return deepExtendArraySafe(itemTarget, item, extendComplexObject, assignByReference, shouldCopyUndefined); }); } else { From 564d33745ee6e1a9786afe17f97f7b75fdbdb130 Mon Sep 17 00:00:00 2001 From: ivanblinov2k17 Date: Tue, 6 Aug 2024 11:42:58 +0400 Subject: [PATCH 08/14] Fix william review sugggestions --- packages/devextreme/js/core/utils/object.js | 7 ++++--- .../tests/DevExpress.core/utils.object.tests.js | 17 ++++++++++++----- .../tests/DevExpress.data/storeArray.tests.js | 1 + 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/devextreme/js/core/utils/object.js b/packages/devextreme/js/core/utils/object.js index 26f92601ea9c..b82b07d75bc6 100644 --- a/packages/devextreme/js/core/utils/object.js +++ b/packages/devextreme/js/core/utils/object.js @@ -65,9 +65,10 @@ const deepExtendArraySafe = function(target, changes, extendComplexObject, assig newValue = deepExtendArraySafe(goDeeper ? prevValue : {}, newValue, extendComplexObject, assignByReference, shouldCopyUndefined); } - if(shouldCopyUndefined && (prevValue !== newValue || prevValue === undefined)) { - assignValueToProperty(target, name, newValue, assignByReference); - } else if((newValue !== undefined) && prevValue !== newValue) { + if( + shouldCopyUndefined && (prevValue !== newValue || prevValue === undefined) || + newValue !== undefined && prevValue !== newValue + ) { assignValueToProperty(target, name, newValue, assignByReference); } } diff --git a/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js b/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js index 963d6ce242da..505ca4a4e2b4 100644 --- a/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js @@ -174,18 +174,25 @@ QUnit.test('deepExtendArraySafe utility does not throw an error with \'null\' de QUnit.test('deepExtendArraySafe sets undefined', function(assert) { const objWithValue = { time: { duration: 50 } }; const objNoValue = {}; - objectUtils.deepExtendArraySafe(objWithValue, { time: { duration: undefined } }, true); - objectUtils.deepExtendArraySafe(objNoValue, { time: { duration: undefined } }, true); - - assert.equal(objWithValue.time.duration, 50); - assert.notOk(Object.prototype.hasOwnProperty.call(objNoValue.time, 'duration')); objectUtils.deepExtendArraySafe(objWithValue, { time: { duration: undefined } }, true, false, true); objectUtils.deepExtendArraySafe(objNoValue, { time: { duration: undefined } }, true, false, true); assert.equal(objWithValue.time.duration, undefined); + assert.ok(Object.prototype.hasOwnProperty.call(objWithValue.time, 'duration')); assert.ok(Object.prototype.hasOwnProperty.call(objNoValue.time, 'duration')); }); +QUnit.test('deepExtendArraySafe doesn\'t set undefined if shouldCopyUndefined == false', function(assert) { + const objWithValue = { time: { duration: 50 } }; + const objNoValue = {}; + objectUtils.deepExtendArraySafe(objWithValue, { time: { duration: undefined } }, true, false, false); + objectUtils.deepExtendArraySafe(objNoValue, { time: { duration: undefined } }, true, false, false); + + assert.equal(objWithValue.time.duration, 50); + assert.notOk(Object.prototype.hasOwnProperty.call(objNoValue.time, 'duration')); + +}); + diff --git a/packages/devextreme/testing/tests/DevExpress.data/storeArray.tests.js b/packages/devextreme/testing/tests/DevExpress.data/storeArray.tests.js index 8bcbaf089e7e..84cc244759c6 100644 --- a/packages/devextreme/testing/tests/DevExpress.data/storeArray.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.data/storeArray.tests.js @@ -804,6 +804,7 @@ QUnit.test('update with explicit undefined', function(assert) { }; assert.deepEqual(data, expectedData); + assert.ok(Object.prototype.hasOwnProperty.call(data.nested, 'a')); done(); }); }); From 98560ea1a1eb4e62a2ffbe12c932917017d13a24 Mon Sep 17 00:00:00 2001 From: ivanblinov2k17 Date: Tue, 6 Aug 2024 12:05:18 +0400 Subject: [PATCH 09/14] Fix suggestions from william's review --- packages/devextreme/js/core/utils/object.js | 46 ++++++++++----------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/packages/devextreme/js/core/utils/object.js b/packages/devextreme/js/core/utils/object.js index ba58f9099c1c..dccd6be5aef3 100644 --- a/packages/devextreme/js/core/utils/object.js +++ b/packages/devextreme/js/core/utils/object.js @@ -39,25 +39,11 @@ const orderEach = function(map, func) { } }; -const assignValueToProperty = function(target, property, value, extendComplexObject, assignByReference, shouldCopyUndefined) { - if(!assignByReference && variableWrapper.isWrapped(target[property])) { - variableWrapper.assign(target[property], value); - } else if(!assignByReference && Array.isArray(value)) { - target[property] = value.map(item => { - let itemTarget = item; - - if(Array.isArray(item)) { - itemTarget = []; - } - if(isObject(item)) { - itemTarget = {}; - } - - return deepExtendArraySafe(itemTarget, item, extendComplexObject, assignByReference, shouldCopyUndefined); - }); - } else { - target[property] = value; +const getDeepCopyTarget = (item) => { + if(isObject(item)) { + return Array.isArray(item) ? [] : {}; } + return item; }; // B239679, http://bugs.jquery.com/ticket/9477 @@ -78,12 +64,24 @@ const deepExtendArraySafe = function(target, changes, extendComplexObject, assig newValue = deepExtendArraySafe(goDeeper ? prevValue : {}, newValue, extendComplexObject, assignByReference, shouldCopyUndefined); } - if( - Array.isArray(newValue) && !assignByReference || - shouldCopyUndefined && (prevValue !== newValue || prevValue === undefined) || - newValue !== undefined && prevValue !== newValue - ) { - assignValueToProperty(target, name, newValue, extendComplexObject, assignByReference, shouldCopyUndefined); + const isDeepCopyArray = Array.isArray(newValue) && !assignByReference; + const hasDifferentNewValue = (shouldCopyUndefined || newValue !== undefined) && prevValue !== newValue || + shouldCopyUndefined && prevValue === undefined; + + if(isDeepCopyArray || hasDifferentNewValue) { + if(!assignByReference && variableWrapper.isWrapped(target[name])) { + variableWrapper.assign(target[name], newValue); + } else if(!assignByReference && Array.isArray(newValue)) { + target[name] = newValue.map(item => deepExtendArraySafe( + getDeepCopyTarget(item), + item, + extendComplexObject, + assignByReference, + shouldCopyUndefined + )); + } else { + target[name] = newValue; + } } } From 4e72dd7a1ca3a69c22332c3b3f8c3ca604d4d8c3 Mon Sep 17 00:00:00 2001 From: ivanblinov2k17 Date: Wed, 14 Aug 2024 11:59:25 +0400 Subject: [PATCH 10/14] Refactor to be able to set legacy/new behavior --- packages/devextreme/js/core/utils/object.js | 44 ++++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/packages/devextreme/js/core/utils/object.js b/packages/devextreme/js/core/utils/object.js index dccd6be5aef3..878f039503ad 100644 --- a/packages/devextreme/js/core/utils/object.js +++ b/packages/devextreme/js/core/utils/object.js @@ -46,8 +46,32 @@ const getDeepCopyTarget = (item) => { return item; }; +const legacyAssign = function(target, property, value, extendComplexObject, assignByReference, shouldCopyUndefined) { + if(!assignByReference && variableWrapper.isWrapped(target[property])) { + variableWrapper.assign(target[property], value); + } else { + target[property] = value; + } +}; + +const newAssign = function(target, property, value, extendComplexObject, assignByReference, shouldCopyUndefined) { + if(!assignByReference && variableWrapper.isWrapped(target[property])) { + variableWrapper.assign(target[property], value); + } else if(!assignByReference && Array.isArray(value)) { + target[property] = value.map(item => deepExtendArraySafe( + getDeepCopyTarget(item), + item, + extendComplexObject, + assignByReference, + shouldCopyUndefined + )); + } else { + target[property] = value; + } +}; + // B239679, http://bugs.jquery.com/ticket/9477 -const deepExtendArraySafe = function(target, changes, extendComplexObject, assignByReference, shouldCopyUndefined) { +const deepExtendArraySafe = function(target, changes, extendComplexObject, assignByReference, shouldCopyUndefined, assignFunc = legacyAssign) { let prevValue; let newValue; @@ -69,19 +93,7 @@ const deepExtendArraySafe = function(target, changes, extendComplexObject, assig shouldCopyUndefined && prevValue === undefined; if(isDeepCopyArray || hasDifferentNewValue) { - if(!assignByReference && variableWrapper.isWrapped(target[name])) { - variableWrapper.assign(target[name], newValue); - } else if(!assignByReference && Array.isArray(newValue)) { - target[name] = newValue.map(item => deepExtendArraySafe( - getDeepCopyTarget(item), - item, - extendComplexObject, - assignByReference, - shouldCopyUndefined - )); - } else { - target[name] = newValue; - } + assignFunc(target, name, newValue, extendComplexObject, assignByReference, shouldCopyUndefined); } } @@ -91,5 +103,7 @@ const deepExtendArraySafe = function(target, changes, extendComplexObject, assig export { clone, orderEach, - deepExtendArraySafe + deepExtendArraySafe, + legacyAssign, + newAssign }; From d7380f3c3a40e16fa4affb111d0d2f3311f64bda Mon Sep 17 00:00:00 2001 From: ivanblinov2k17 Date: Mon, 9 Sep 2024 16:53:50 +0400 Subject: [PATCH 11/14] Use new assign --- packages/devextreme/js/__internal/data/m_array_utils.ts | 4 ++-- .../testing/tests/DevExpress.core/utils.object.tests.js | 3 ++- .../testing/tests/DevExpress.knockout/objectUtils.tests.js | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/devextreme/js/__internal/data/m_array_utils.ts b/packages/devextreme/js/__internal/data/m_array_utils.ts index a05a750c4064..68d09b971ea5 100644 --- a/packages/devextreme/js/__internal/data/m_array_utils.ts +++ b/packages/devextreme/js/__internal/data/m_array_utils.ts @@ -2,7 +2,7 @@ import config from '@js/core/config'; import Guid from '@js/core/guid'; import { compileGetter } from '@js/core/utils/data'; import { extend } from '@js/core/utils/extend'; -import { deepExtendArraySafe } from '@js/core/utils/object'; +import { deepExtendArraySafe, newAssign } from '@js/core/utils/object'; import { isDefined, isEmptyObject, isObject, isPlainObject, } from '@js/core/utils/type'; @@ -195,7 +195,7 @@ function update(keyInfo, array, key, data, isBatch, immutable, logError) { target = key; } - deepExtendArraySafe(target, data, extendComplexObject, false, true); + deepExtendArraySafe(target, data, extendComplexObject, false, true, newAssign); if (!isBatch) { if (config().useLegacyStoreResult) { return trivialPromise(key, data); diff --git a/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js b/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js index 6d6f1d01f1ce..42e944b026f8 100644 --- a/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js @@ -1,4 +1,5 @@ const objectUtils = require('core/utils/object'); +const { newAssign } = require('../../../js/core/utils/object'); QUnit.test('orderEach', function(assert) { const checkOrderEach = function(mapKeys, keys) { @@ -204,7 +205,7 @@ QUnit.test('deepExtendArraySafe copies array into object property deeply', funct const objWithValue = { time: undefined }; const complexTime = { complexTime: 2 }; const timeArray = [1, complexTime, 3]; - objectUtils.deepExtendArraySafe(objWithValue, { time: timeArray }, true, false); + objectUtils.deepExtendArraySafe(objWithValue, { time: timeArray }, true, false, false, newAssign); assert.deepEqual(objWithValue.time, timeArray); assert.notStrictEqual(objWithValue.time, timeArray); diff --git a/packages/devextreme/testing/tests/DevExpress.knockout/objectUtils.tests.js b/packages/devextreme/testing/tests/DevExpress.knockout/objectUtils.tests.js index 48dcb95770b2..957c8333967d 100644 --- a/packages/devextreme/testing/tests/DevExpress.knockout/objectUtils.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.knockout/objectUtils.tests.js @@ -12,7 +12,7 @@ if(QUnit.urlParams['nocsp']) { QUnit.test('deepExtendArraySafe works correctly with array contain observables', function(assert) { const testObj = { id: 4, name: ko.observable('John') }; - const resultObj = objectUtils.deepExtendArraySafe(testObj, { name: 'Sue' }); + const resultObj = objectUtils.deepExtendArraySafe(testObj, { name: 'Sue' }, false, false, false, objectUtils.newAssign); assert.equal(variableWrapper.isWrapped(resultObj.name), true, '\'name\' field is still observable'); assert.equal(resultObj.name(), 'Sue', 'New value accepted'); From 5b59efb3c8c74b8d5a153761e3d2373b1e4a18d2 Mon Sep 17 00:00:00 2001 From: ivanblinov2k17 Date: Tue, 10 Sep 2024 05:26:52 +0400 Subject: [PATCH 12/14] Fix import --- .../testing/tests/DevExpress.core/utils.object.tests.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js b/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js index 42e944b026f8..8ea1458ca2da 100644 --- a/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js @@ -1,5 +1,4 @@ const objectUtils = require('core/utils/object'); -const { newAssign } = require('../../../js/core/utils/object'); QUnit.test('orderEach', function(assert) { const checkOrderEach = function(mapKeys, keys) { @@ -205,7 +204,7 @@ QUnit.test('deepExtendArraySafe copies array into object property deeply', funct const objWithValue = { time: undefined }; const complexTime = { complexTime: 2 }; const timeArray = [1, complexTime, 3]; - objectUtils.deepExtendArraySafe(objWithValue, { time: timeArray }, true, false, false, newAssign); + objectUtils.deepExtendArraySafe(objWithValue, { time: timeArray }, true, false, false, objectUtils.newAssign); assert.deepEqual(objWithValue.time, timeArray); assert.notStrictEqual(objWithValue.time, timeArray); From f46af5756e02fb97aa61aeaadca1bd3885e0661f Mon Sep 17 00:00:00 2001 From: ivanblinov2k17 Date: Tue, 10 Sep 2024 08:26:27 +0400 Subject: [PATCH 13/14] Add test for scheduler + fix new assign behavior --- .../scheduler/m_appointment_adapter.ts | 4 +- packages/devextreme/js/core/utils/object.js | 10 ++++ ...integration.recurringAppointments.tests.js | 46 +++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/m_appointment_adapter.ts b/packages/devextreme/js/__internal/scheduler/m_appointment_adapter.ts index 20677a67304c..acb3191ef5a3 100644 --- a/packages/devextreme/js/__internal/scheduler/m_appointment_adapter.ts +++ b/packages/devextreme/js/__internal/scheduler/m_appointment_adapter.ts @@ -1,5 +1,5 @@ import { extend } from '@js/core/utils/extend'; -import { deepExtendArraySafe } from '@js/core/utils/object'; +import { deepExtendArraySafe, newAssign } from '@js/core/utils/object'; import errors from '@js/ui/widget/ui.errors'; import { ExpressionUtils } from './m_expression_utils'; @@ -147,7 +147,7 @@ class AppointmentAdapter { clone(options: any = undefined) { const result = new AppointmentAdapter( - deepExtendArraySafe({}, this.rawAppointment), + deepExtendArraySafe({}, this.rawAppointment, false, false, false, newAssign), this.dataAccessors, this.timeZoneCalculator, options, diff --git a/packages/devextreme/js/core/utils/object.js b/packages/devextreme/js/core/utils/object.js index 878f039503ad..351adf4640e0 100644 --- a/packages/devextreme/js/core/utils/object.js +++ b/packages/devextreme/js/core/utils/object.js @@ -55,6 +55,7 @@ const legacyAssign = function(target, property, value, extendComplexObject, assi }; const newAssign = function(target, property, value, extendComplexObject, assignByReference, shouldCopyUndefined) { + const goDeeper = extendComplexObject ? isObject(target) : isPlainObject(target); if(!assignByReference && variableWrapper.isWrapped(target[property])) { variableWrapper.assign(target[property], value); } else if(!assignByReference && Array.isArray(value)) { @@ -65,6 +66,15 @@ const newAssign = function(target, property, value, extendComplexObject, assignB assignByReference, shouldCopyUndefined )); + } else if(!assignByReference && goDeeper) { + target[property] = deepExtendArraySafe( + getDeepCopyTarget(value), + value, + extendComplexObject, + assignByReference, + shouldCopyUndefined, + newAssign + ); } else { target[property] = value; } diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/integration.recurringAppointments.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/integration.recurringAppointments.tests.js index 60db59f8ee2a..deed380449a0 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/integration.recurringAppointments.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/integration.recurringAppointments.tests.js @@ -707,6 +707,52 @@ supportedScrollingModes.forEach(scrollingMode => { assert.equal(updatedRecurringItem.recurrenceException, dateSerialization.serializeDate(exceptionDate, 'yyyyMMddTHHmmssZ'), 'Exception for recurrence appointment is correct'); }); + test('Recurrent Task editing, single mode, should not reference copy recurrent data (T1228488)', function(assert) { + const updatedItem = { + text: 'Task 2', + customData: { texts: ['123', '456'] }, + startDate: new Date(2015, 1, 11, 3), + endDate: new Date(2015, 1, 11, 4), + }; + + const scheduler = this.createInstance({ + currentDate: new Date(2015, 1, 9), + dataSource: [{ + text: 'Task 1', + startDate: new Date(2015, 1, 9, 1, 0), + endDate: new Date(2015, 1, 9, 2, 0), + customData: { texts: ['123'] }, + recurrenceRule: 'FREQ=DAILY' + }], + currentView: 'week', + onAppointmentAdding: (e) => { + e.appointmentData.customData.texts.push('456'); + }, + firstDayOfWeek: 1 + }); + + scheduler.appointments.click(2); + this.clock.tick(300); + scheduler.tooltip.clickOnItem(); + $('.dx-dialog-buttons .dx-button').eq(1).trigger('dxclick'); + + const $title = $('.dx-textbox').eq(0); + const title = $title.dxTextBox('instance'); + const $startDate = $('.dx-datebox').eq(0); + const startDate = $startDate.dxDateBox('instance'); + + title.option('value', 'Task 2'); + startDate.option('value', new Date(2015, 1, 11, 3, 0)); + $('.dx-button.dx-popup-done').eq(0).trigger('dxclick'); + this.clock.tick(300); + + const updatedSingleItem = scheduler.instance.option('dataSource')[1]; + const updatedRecurringItem = scheduler.instance.option('dataSource')[0]; + + assert.deepEqual(updatedSingleItem, updatedItem, 'New data is correct'); + assert.deepEqual(updatedRecurringItem.customData.texts, ['123'], 'Recurrence data is correct'); + }); + test('Recurrent Task edition canceling, single mode', function(assert) { const data = new DataSource({ store: [ From 1ab6a37b9cc8c771239ab45add44d3f900d83aa3 Mon Sep 17 00:00:00 2001 From: ivanblinov2k17 Date: Thu, 12 Sep 2024 16:33:00 +0400 Subject: [PATCH 14/14] Use flag instead of importing new callback --- packages/devextreme/js/__internal/data/m_array_utils.ts | 4 ++-- .../js/__internal/scheduler/m_appointment_adapter.ts | 4 ++-- packages/devextreme/js/core/utils/object.js | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/devextreme/js/__internal/data/m_array_utils.ts b/packages/devextreme/js/__internal/data/m_array_utils.ts index 68d09b971ea5..818eb0ee1ff8 100644 --- a/packages/devextreme/js/__internal/data/m_array_utils.ts +++ b/packages/devextreme/js/__internal/data/m_array_utils.ts @@ -2,7 +2,7 @@ import config from '@js/core/config'; import Guid from '@js/core/guid'; import { compileGetter } from '@js/core/utils/data'; import { extend } from '@js/core/utils/extend'; -import { deepExtendArraySafe, newAssign } from '@js/core/utils/object'; +import { deepExtendArraySafe } from '@js/core/utils/object'; import { isDefined, isEmptyObject, isObject, isPlainObject, } from '@js/core/utils/type'; @@ -195,7 +195,7 @@ function update(keyInfo, array, key, data, isBatch, immutable, logError) { target = key; } - deepExtendArraySafe(target, data, extendComplexObject, false, true, newAssign); + deepExtendArraySafe(target, data, extendComplexObject, false, true, true); if (!isBatch) { if (config().useLegacyStoreResult) { return trivialPromise(key, data); diff --git a/packages/devextreme/js/__internal/scheduler/m_appointment_adapter.ts b/packages/devextreme/js/__internal/scheduler/m_appointment_adapter.ts index acb3191ef5a3..ae25588a776b 100644 --- a/packages/devextreme/js/__internal/scheduler/m_appointment_adapter.ts +++ b/packages/devextreme/js/__internal/scheduler/m_appointment_adapter.ts @@ -1,5 +1,5 @@ import { extend } from '@js/core/utils/extend'; -import { deepExtendArraySafe, newAssign } from '@js/core/utils/object'; +import { deepExtendArraySafe } from '@js/core/utils/object'; import errors from '@js/ui/widget/ui.errors'; import { ExpressionUtils } from './m_expression_utils'; @@ -147,7 +147,7 @@ class AppointmentAdapter { clone(options: any = undefined) { const result = new AppointmentAdapter( - deepExtendArraySafe({}, this.rawAppointment, false, false, false, newAssign), + deepExtendArraySafe({}, this.rawAppointment, false, false, false, true), this.dataAccessors, this.timeZoneCalculator, options, diff --git a/packages/devextreme/js/core/utils/object.js b/packages/devextreme/js/core/utils/object.js index 351adf4640e0..f851747d0bc9 100644 --- a/packages/devextreme/js/core/utils/object.js +++ b/packages/devextreme/js/core/utils/object.js @@ -81,9 +81,10 @@ const newAssign = function(target, property, value, extendComplexObject, assignB }; // B239679, http://bugs.jquery.com/ticket/9477 -const deepExtendArraySafe = function(target, changes, extendComplexObject, assignByReference, shouldCopyUndefined, assignFunc = legacyAssign) { +const deepExtendArraySafe = function(target, changes, extendComplexObject, assignByReference, shouldCopyUndefined, useNewAssign) { let prevValue; let newValue; + const assignFunc = useNewAssign ? newAssign : legacyAssign; for(const name in changes) { prevValue = target[name];