From 290bb23fec2dd42047e458ad4880289be5e01f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 3 Jul 2020 13:41:23 +0200 Subject: [PATCH] update data structure to fit with data structure RFC v3 --- .../blockeditormodelobject.service.js | 89 ++++++++--- .../services/block-editor-service.spec.js | 144 ++++++++++++++++-- 2 files changed, 202 insertions(+), 31 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js index ec232b4914..5cf0bbf944 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js @@ -136,7 +136,7 @@ // We also like to watch our data model to be able to capture changes coming from other places. if (forSettings === true) { - blockObject.__watchers.push(isolatedScope.$watch("blockObjects._" + blockObject.key + "." + "layout.settings" + "." + prop.alias, createLayoutSettingsModelWatcher(blockObject, prop))); + blockObject.__watchers.push(isolatedScope.$watch("blockObjects._" + blockObject.key + "." + "settingsData" + "." + prop.alias, createLayoutSettingsModelWatcher(blockObject, prop))); } else { blockObject.__watchers.push(isolatedScope.$watch("blockObjects._" + blockObject.key + "." + "data" + "." + prop.alias, createDataModelWatcher(blockObject, prop))); } @@ -167,9 +167,9 @@ */ function createLayoutSettingsModelWatcher(blockObject, prop) { return function() { - if (prop.value !== blockObject.layout.settings[prop.alias]) { + if (prop.value !== blockObject.settingsData[prop.alias]) { // sync data: - prop.value = blockObject.layout.settings[prop.alias]; + prop.value = blockObject.settingsData[prop.alias]; } } } @@ -193,9 +193,9 @@ */ function createSettingsModelPropWatcher(blockObject, prop) { return function() { - if (blockObject.layout.settings[prop.alias] !== prop.value) { + if (blockObject.settingsData[prop.alias] !== prop.value) { // sync data: - blockObject.layout.settings[prop.alias] = prop.value; + blockObject.settingsData[prop.alias] = prop.value; } } } @@ -245,7 +245,8 @@ // ensure basic part of data-structure is in place: this.value = propertyModelValue; this.value.layout = this.value.layout || {}; - this.value.data = this.value.data || []; + this.value.contentData = this.value.contentData || []; + this.value.settingsData = this.value.settingsData || []; this.propertyEditorAlias = propertyEditorAlias; this.blockConfigurations = blockConfigurations; @@ -459,16 +460,25 @@ var settingsScaffold = this.getScaffoldFromKey(blockConfiguration.settingsElementTypeKey); if (settingsScaffold !== null) { - layoutEntry.settings = layoutEntry.settings || {}; - - blockObject.settingsData = layoutEntry.settings; + if (!layoutEntry.settingsUdi) { + // if this block does not have settings data, then create it. This could happen because settings model has been added later than this content was created. + layoutEntry.settingsUdi = this._createSettingsEntry(blockConfiguration.settingsElementTypeKey); + } + + var settingsUdi = layoutEntry.settingsUdi; + + var settingsData = this._getSettingsByUdi(settingsUdi); + if (settingsData === null) { + console.error("Couldnt find content settings data of " + settingsUdi) + return null; + } + + blockObject.settingsData = settingsData; // make basics from scaffold blockObject.settings = Utilities.copy(settingsScaffold); - layoutEntry.settings = layoutEntry.settings || {}; - if (!layoutEntry.settings.key) { layoutEntry.settings.key = String.CreateGuid(); } - if (!layoutEntry.settings.contentTypeKey) { layoutEntry.settings.contentTypeKey = blockConfiguration.settingsElementTypeKey; } - mapToElementModel(blockObject.settings, layoutEntry.settings); + blockObject.settings.udi = settingsUdi; + mapToElementModel(blockObject.settings, settingsData); } } @@ -487,7 +497,7 @@ mapToPropertyModel(this.content, this.data); } if (this.config.settingsElementTypeKey !== null) { - mapToPropertyModel(this.settings, this.layout.settings); + mapToPropertyModel(this.settings, this.settingsData); } } @@ -508,6 +518,7 @@ delete this.config; delete this.layout; delete this.data; + delete this.settingsData; delete this.content; delete this.settings; @@ -536,8 +547,15 @@ */ removeDataAndDestroyModel: function (blockObject) { var udi = blockObject.content.udi; + var settingsUdi = null; + if (blockObject.settings) { + settingsUdi = blockObject.settings.udi; + } this.destroyBlockObject(blockObject); this.removeDataByUdi(udi); + if(settingsUdi) { + this.removeSettingsByUdi(settingsUdi); + } }, /** @@ -586,7 +604,7 @@ } if (blockConfiguration.settingsElementTypeKey != null) { - entry.settings = { key: String.CreateGuid(), contentTypeKey: blockConfiguration.settingsElementTypeKey }; + entry.settingsUdi = this._createSettingsEntry(blockConfiguration.settingsElementTypeKey) } return entry; @@ -641,26 +659,55 @@ contentTypeKey: elementTypeKey, udi: udiService.create("element") }; - this.value.data.push(content); + this.value.contentData.push(content); return content.udi; }, // private _getDataByUdi: function(udi) { - return this.value.data.find(entry => entry.udi === udi) || null; + return this.value.contentData.find(entry => entry.udi === udi) || null; }, /** * @ngdoc method * @name removeDataByUdi * @methodOf umbraco.services.blockEditorModelObject - * @description Removes the data of a given UDI. + * @description Removes the content data of a given UDI. * Notice this method does not remove the block from your layout, this will need to be handlede by the Property Editor since this services donst know about your layout structure. - * @param {string} udi The UDI of the data to be removed. + * @param {string} udi The UDI of the content data to be removed. */ removeDataByUdi: function(udi) { - const index = this.value.data.findIndex(o => o.udi === udi); + const index = this.value.contentData.findIndex(o => o.udi === udi); if (index !== -1) { - this.value.data.splice(index, 1); + this.value.contentData.splice(index, 1); + } + }, + + // private + _createSettingsEntry: function(elementTypeKey) { + var settings = { + contentTypeKey: elementTypeKey, + udi: udiService.create("element") + }; + this.value.settingsData.push(settings); + return settings.udi; + }, + // private + _getSettingsByUdi: function(udi) { + return this.value.settingsData.find(entry => entry.udi === udi) || null; + }, + + /** + * @ngdoc method + * @name removeSettingsByUdi + * @methodOf umbraco.services.blockEditorModelObject + * @description Removes the settings data of a given UDI. + * Notice this method does not remove the settingsUdi from your layout, this will need to be handlede by the Property Editor since this services donst know about your layout structure. + * @param {string} udi The UDI of the settings data to be removed. + */ + removeSettingsByUdi: function(udi) { + const index = this.value.settingsData.findIndex(o => o.udi === udi); + if (index !== -1) { + this.value.settingsData.splice(index, 1); } }, diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js index e0cd2d0c93..8936f731bb 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/block-editor-service.spec.js @@ -36,7 +36,7 @@ } ] }, - data: [ + contentData: [ { udi: 1234, contentTypeKey: "7C5B74D1-E2F9-45A3-AE4B-FC7A829BF8AB", @@ -45,6 +45,32 @@ ] }; + var blockWithSettingsConfigurationMock = { contentTypeKey: "7C5B74D1-E2F9-45A3-AE4B-FC7A829BF8AB", label:"Test label", settingsElementTypeKey: "7C5B74D1-E2F9-45A3-AE4B-FC7A829BF8AB", view: "testview.html"}; + var propertyModelWithSettingsMock = { + layout: { + "Umbraco.TestBlockEditor": [ + { + udi: 1234, + settingsUdi: 4567 + } + ] + }, + contentData: [ + { + udi: 1234, + contentTypeKey: "7C5B74D1-E2F9-45A3-AE4B-FC7A829BF8AB", + testproperty: "myTestValue" + } + ], + settingsData: [ + { + udi: 4567, + contentTypeKey: "7C5B74D1-E2F9-45A3-AE4B-FC7A829BF8AB", + testproperty: "myTestValueForSettings" + } + ] + }; + describe('init blockEditorModelObject', function () { it('fail if no model value', function () { @@ -110,8 +136,8 @@ var blockObject = modelObject.getBlockObject(layout[0]); expect(blockObject).not.toBeUndefined(); - expect(blockObject.data.udi).toBe(propertyModelMock.data[0].udi); - expect(blockObject.content.variants[0].tabs[0].properties[0].value).toBe(propertyModelMock.data[0].testproperty); + expect(blockObject.data.udi).toBe(propertyModelMock.contentData[0].udi); + expect(blockObject.content.variants[0].tabs[0].properties[0].value).toBe(propertyModelMock.contentData[0].testproperty); done(); }); @@ -135,9 +161,9 @@ $rootScope.$digest();// invoke angularJS Store. - expect(blockObject.data).toBe(propertyModel.data[0]); + expect(blockObject.data).toEqual(propertyModel.contentData[0]); expect(blockObject.data.testproperty).toBe("anotherTestValue"); - expect(propertyModel.data[0].testproperty).toBe("anotherTestValue"); + expect(propertyModel.contentData[0].testproperty).toBe("anotherTestValue"); // @@ -152,7 +178,7 @@ var propertyModel = angular.copy(propertyModelMock); var complexValue = {"list": ["A", "B", "C"]}; - propertyModel.data[0].testproperty = complexValue; + propertyModel.contentData[0].testproperty = complexValue; var modelObject = blockEditorService.createModelObject(propertyModel, "Umbraco.TestBlockEditor", [blockConfigurationMock], $scope, $scope); @@ -168,8 +194,8 @@ $rootScope.$digest();// invoke angularJS Store. - expect(propertyModel.data[0].testproperty.list[0]).toBe("AA"); - expect(propertyModel.data[0].testproperty.list.length).toBe(4); + expect(propertyModel.contentData[0].testproperty.list[0]).toBe("AA"); + expect(propertyModel.contentData[0].testproperty.list.length).toBe(4); done(); }); @@ -218,8 +244,8 @@ // remove from data; modelObject.removeDataAndDestroyModel(blockObject); - expect(propertyModel.data.length).toBe(0); - expect(propertyModel.data[0]).toBeUndefined(); + expect(propertyModel.contentData.length).toBe(0); + expect(propertyModel.contentData[0]).toBeUndefined(); expect(propertyModel.layout["Umbraco.TestBlockEditor"].length).toBe(0); expect(propertyModel.layout["Umbraco.TestBlockEditor"][0]).toBeUndefined(); @@ -229,6 +255,104 @@ }); + + + + + + it('getBlockObject of block with settings has values', function (done) { + + var propertyModel = angular.copy(propertyModelWithSettingsMock); + + var modelObject = blockEditorService.createModelObject(propertyModel, "Umbraco.TestBlockEditor", [blockWithSettingsConfigurationMock], $scope, $scope); + + modelObject.load().then(() => { + + var layout = modelObject.getLayout(); + + var blockObject = modelObject.getBlockObject(layout[0]); + + expect(blockObject).not.toBeUndefined(); + expect(blockObject.data.udi).toBe(propertyModel.contentData[0].udi); + expect(blockObject.content.variants[0].tabs[0].properties[0].value).toBe(propertyModel.contentData[0].testproperty); + + done(); + }); + + }); + + + it('getBlockObject of block with settings syncs primative values', function (done) { + + var propertyModel = angular.copy(propertyModelWithSettingsMock); + + var modelObject = blockEditorService.createModelObject(propertyModel, "Umbraco.TestBlockEditor", [blockWithSettingsConfigurationMock], $scope, $scope); + + modelObject.load().then(() => { + + var layout = modelObject.getLayout(); + + var blockObject = modelObject.getBlockObject(layout[0]); + + blockObject.content.variants[0].tabs[0].properties[0].value = "anotherTestValue"; + blockObject.settings.variants[0].tabs[0].properties[0].value = "anotherTestValueForSettings"; + + $rootScope.$digest();// invoke angularJS Store. + + expect(blockObject.data).toEqual(propertyModel.contentData[0]); + expect(blockObject.data.testproperty).toBe("anotherTestValue"); + expect(propertyModel.contentData[0].testproperty).toBe("anotherTestValue"); + + expect(blockObject.settingsData).toEqual(propertyModel.settingsData[0]); + expect(blockObject.settingsData.testproperty).toBe("anotherTestValueForSettings"); + expect(propertyModel.settingsData[0].testproperty).toBe("anotherTestValueForSettings"); + + // + + done(); + }); + + }); + + + it('getBlockObject of block with settings syncs values of object', function (done) { + + var propertyModel = angular.copy(propertyModelWithSettingsMock); + + var complexValue = {"list": ["A", "B", "C"]}; + propertyModel.contentData[0].testproperty = complexValue; + + var complexSettingsValue = {"list": ["A", "B", "C"]}; + propertyModel.settingsData[0].testproperty = complexSettingsValue; + + var modelObject = blockEditorService.createModelObject(propertyModel, "Umbraco.TestBlockEditor", [blockWithSettingsConfigurationMock], $scope, $scope); + + modelObject.load().then(() => { + + var layout = modelObject.getLayout(); + + var blockObject = modelObject.getBlockObject(layout[0]); + + blockObject.content.variants[0].tabs[0].properties[0].value.list[0] = "AA"; + blockObject.content.variants[0].tabs[0].properties[0].value.list.push("D"); + + blockObject.settings.variants[0].tabs[0].properties[0].value.list[0] = "settingsValue"; + blockObject.settings.variants[0].tabs[0].properties[0].value.list.push("settingsNewValue"); + + $rootScope.$digest();// invoke angularJS Store. + + expect(propertyModel.contentData[0].testproperty.list[0]).toBe("AA"); + expect(propertyModel.contentData[0].testproperty.list.length).toBe(4); + + expect(propertyModel.settingsData[0].testproperty.list[0]).toBe("settingsValue"); + expect(propertyModel.settingsData[0].testproperty.list.length).toBe(4); + + done(); + }); + + }); + + }); });