diff --git a/src/cloud-element-templates/behavior/ConditionalBehavior.js b/src/cloud-element-templates/behavior/ConditionalBehavior.js index d927d41..288e127 100644 --- a/src/cloud-element-templates/behavior/ConditionalBehavior.js +++ b/src/cloud-element-templates/behavior/ConditionalBehavior.js @@ -22,17 +22,17 @@ export default class ConditionalBehavior extends CommandInterceptor { this._bpmnFactory = bpmnFactory; this._injector = injector; - // (1) save pre-conditional state + // (1) save pre-conditional state before updating a property this.preExecute([ 'element.updateProperties', 'element.updateModdleProperties', 'element.move' ], this._saveConditionalState, true, this); - // (2) so we can check if we need to apply post-conditional updates + // (2) check if we need to apply post-conditional updates // // if [additional bindings activate] then - // re-trigger setting the template + // (re-)trigger setting the template // else // else we're done // @@ -42,6 +42,11 @@ export default class ConditionalBehavior extends CommandInterceptor { 'propertiesPanel.zeebe.changeTemplate', 'element.move' ], this._applyConditions, true, this); + + // (3) set conditions before changing the template + this.preExecute([ + 'propertiesPanel.zeebe.changeTemplate' + ], this._ensureConditional, true, this); } _saveConditionalState(context) { @@ -60,35 +65,55 @@ export default class ConditionalBehavior extends CommandInterceptor { _applyConditions(context) { const { - element + element, + newTemplate, + oldTemplateWithConditions } = context; const template = this._elementTemplates.get(element); - // New Template is persisted before applying default values, + // new Template is persisted before applying default values, // new conditions might apply after the defaults are present. - const oldTemplate = context.oldTemplateWithConditions || context.newTemplate; + const oldTemplate = oldTemplateWithConditions || newTemplate; - if (!template || !oldTemplate || template.id !== oldTemplate.id) { + if (!template || !oldTemplate) { return; } - const newTemplate = applyConditions(element, template); + const newTemplateWithConditions = applyConditions(element, template); - // (3) this is the important check that verifies if we need to apply - // additional template properties - if (!hasDifferentPropertyBindings(newTemplate, oldTemplate)) { + // verify that new bindings were activated + if (!hasDifferentPropertyBindings(newTemplateWithConditions, oldTemplate)) { return; } + // do another pass to apply further conditional bindings + // newTemplate will always be the original template; it is filtered + // at a later step (3) const changeContext = { element, - newTemplate, + newTemplate: template, oldTemplate }; this._commandStack.execute('propertiesPanel.zeebe.changeTemplate', changeContext); } + + _ensureConditional(context) { + const { + element, + newTemplate + } = context; + + if (!newTemplate) { + return; + } + + // ensure conditions are applied before changing the template. + // `newTemplate` will always be the original template. + context.newTemplate = applyConditions(element, newTemplate); + } + } diff --git a/test/spec/cloud-element-templates/ElementTemplates.spec.js b/test/spec/cloud-element-templates/ElementTemplates.spec.js index d38cf6c..8c7ff23 100644 --- a/test/spec/cloud-element-templates/ElementTemplates.spec.js +++ b/test/spec/cloud-element-templates/ElementTemplates.spec.js @@ -582,13 +582,13 @@ describe('provider/cloud-element-templates - ElementTemplates', function() { // assume expect( - task.businessObject.extensionElements.values[1].inputParameters[0].source + task.businessObject.extensionElements.values[0].inputParameters[0].source ).to.eql( 'action1' ); expect( - task.businessObject.extensionElements.values[0].type + task.businessObject.extensionElements.values[1].type ).to.eql( 'action1-value' ); @@ -602,13 +602,13 @@ describe('provider/cloud-element-templates - ElementTemplates', function() { // assume expect( - updatedTask.businessObject.extensionElements.values[1].inputParameters[0].source + updatedTask.businessObject.extensionElements.values[0].inputParameters[0].source ).to.eql( 'action1' ); expect( - updatedTask.businessObject.extensionElements.values[0].type + updatedTask.businessObject.extensionElements.values[1].type ).to.eql( 'action1-value-2' ); diff --git a/test/spec/cloud-element-templates/behavior/ConditionalBehavior.dependent-dropdowns.json b/test/spec/cloud-element-templates/behavior/ConditionalBehavior.dependent-dropdowns.json new file mode 100644 index 0000000..57653a2 --- /dev/null +++ b/test/spec/cloud-element-templates/behavior/ConditionalBehavior.dependent-dropdowns.json @@ -0,0 +1,87 @@ +{ + "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "name": "Dependent Dropdowns", + "id": "com.camunda.example.dependent.dropdowns:1", + "description": "Test dependent dropdowns", + "appliesTo": [ + "bpmn:Task" + ], + "properties": [ + { + "id": "root", + "label": "Root", + "type": "Dropdown", + "choices": [ + { + "name": "Root A", + "value": "Root A" + }, + { + "name": "Root B", + "value": "Root B" + } + ], + "binding": { + "name": "root", + "type": "property" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Sub", + "type": "Dropdown", + "choices": [ + { + "name": "/A/1", + "value": "/A/1" + }, + { + "name": "/A/2", + "value": "/A/2" + } + ], + "binding": { + "name": "sub", + "type": "property" + }, + "condition": { + "property": "root", + "equals": "Root A" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Sub", + "type": "Dropdown", + "choices": [ + { + "name": "/B/1", + "value": "/B/1" + }, + { + "name": "/B/2", + "value": "/B/2" + }, + { + "name": "/B/3", + "value": "/B/3" + } + ], + "binding": { + "name": "sub", + "type": "property" + }, + "condition": { + "property": "root", + "equals": "Root B" + }, + "constraints": { + "notEmpty": true + } + } + ] +} \ No newline at end of file diff --git a/test/spec/cloud-element-templates/behavior/ConditionalBehavior.spec.js b/test/spec/cloud-element-templates/behavior/ConditionalBehavior.spec.js index f21ab1d..426594b 100644 --- a/test/spec/cloud-element-templates/behavior/ConditionalBehavior.spec.js +++ b/test/spec/cloud-element-templates/behavior/ConditionalBehavior.spec.js @@ -15,7 +15,6 @@ import { BpmnPropertiesPanelModule } from 'bpmn-js-properties-panel'; import ZeebeBehaviorsModule from 'camunda-bpmn-js-behaviors/lib/camunda-cloud'; - import diagramXML from '../fixtures/condition.bpmn'; import messageDiagramXML from '../fixtures/condition-message.bpmn'; import messageCorrelationDiagramXML from '../fixtures/message-correlation-key.bpmn'; @@ -25,6 +24,7 @@ import updateTemplates from '../fixtures/condition-update.json'; import chainedConditionsSimpleTemplate from '../fixtures/condition-chained.json'; import chainedConditionsComplexTemplate from './ConditionalBehavior.condition-chained.json'; import chainedConditionsSharedBindingTemplate from './ConditionalBehavior.condition-chained-shared-binding.json'; +import dependentDropdownsTemplate from './ConditionalBehavior.dependent-dropdowns.json'; import messageTemplates from '../fixtures/condition-message.json'; import messageCorrelationTemplate from '../fixtures/message-correlation-key.json'; @@ -1628,6 +1628,105 @@ describe('provider/cloud-element-templates - ConditionalBehavior', function() { }); + + describe('user input', function() { + + describe('should preserve valid values', function() { + + it('when applying template', inject( + function(elementRegistry, modeling) { + + // given + const element = elementRegistry.get('ServiceTask_1'); + const businessObject = getBusinessObject(element); + + modeling.updateModdleProperties(element, businessObject, { + root: 'Root B', + sub: '/B/2' + }); + + // assume + expect(businessObject.get('root')).to.eql('Root B'); + expect(businessObject.get('sub')).to.eql('/B/2'); + + // when + const updatedElement = changeTemplate(element, dependentDropdownsTemplate); + const updatedBusinessObject = getBusinessObject(updatedElement); + + // then + expect(updatedBusinessObject.get('root')).to.eql('Root B'); + expect(updatedBusinessObject.get('sub')).to.eql('/B/2'); + } + )); + + + it('when upgrading template', inject( + function(modeling) { + + // given + const newTemplate = { + ...dependentDropdownsTemplate, + version: 2 + }; + + const element = changeTemplate('ServiceTask_1', dependentDropdownsTemplate); + const businessObject = getBusinessObject(element); + + modeling.updateModdleProperties(element, businessObject, { + root: 'Root B', + sub: '/B/2' + }); + + // assume + expect(businessObject.get('root')).to.eql('Root B'); + expect(businessObject.get('sub')).to.eql('/B/2'); + + // when + const updatedElement = changeTemplate(element, newTemplate, dependentDropdownsTemplate); + const updatedBusinessObject = getBusinessObject(updatedElement); + + // then + expect(updatedBusinessObject.get('root')).to.eql('Root B'); + expect(updatedBusinessObject.get('sub')).to.eql('/B/2'); + } + )); + + + it('when changing template', inject( + function(modeling) { + + // given + const newTemplate = { + ...dependentDropdownsTemplate, + id: dependentDropdownsTemplate.id + '::v2' + }; + + const element = changeTemplate('ServiceTask_1', dependentDropdownsTemplate); + const businessObject = getBusinessObject(element); + + modeling.updateModdleProperties(element, businessObject, { + root: 'Root B', + sub: '/B/2' + }); + + // assume + expect(businessObject.get('root')).to.eql('Root B'); + expect(businessObject.get('sub')).to.eql('/B/2'); + + // when + const updatedElement = changeTemplate(element, newTemplate, dependentDropdownsTemplate); + const updatedBusinessObject = getBusinessObject(updatedElement); + + // then + expect(updatedBusinessObject.get('root')).to.eql('Root B'); + expect(updatedBusinessObject.get('sub')).to.eql('/B/2'); + } + )); + + }); + + }); + }); diff --git a/test/spec/cloud-element-templates/fixtures/condition.bpmn b/test/spec/cloud-element-templates/fixtures/condition.bpmn index a51a910..a14c402 100644 --- a/test/spec/cloud-element-templates/fixtures/condition.bpmn +++ b/test/spec/cloud-element-templates/fixtures/condition.bpmn @@ -4,6 +4,7 @@ + @@ -11,14 +12,18 @@ - + - + + + + +