diff --git a/package-lock.json b/package-lock.json index 4445915b..c6aee8af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.4.0", "license": "MIT", "dependencies": { - "@bpmn-io/element-templates-validator": "^1.0.0", + "@bpmn-io/element-templates-validator": "^1.1.0", "@bpmn-io/extract-process-variables": "^0.8.0", "bpmnlint": "^8.3.2", "classnames": "^2.3.1", @@ -581,20 +581,20 @@ } }, "node_modules/@bpmn-io/element-templates-validator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@bpmn-io/element-templates-validator/-/element-templates-validator-1.0.0.tgz", - "integrity": "sha512-v8AJk1WjTZpS3am8aAPnjm26/v5PdMJvyacEW1I1sFrIx99/FO4QLfAuk0qdkwvIUmzpR6xB4kuF3o+k4dBzYQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@bpmn-io/element-templates-validator/-/element-templates-validator-1.1.0.tgz", + "integrity": "sha512-f1yjNuyMW+cxzWSD0d3qeVMjcHcDqVQI0tUdDD3OC2xNgmOhf7fOFPdZuE/XIcJ27jeBr/ZuYz5ghsRX4+zKSw==", "dependencies": { - "@camunda/element-templates-json-schema": "^0.13.0", - "@camunda/zeebe-element-templates-json-schema": "^0.11.0", + "@camunda/element-templates-json-schema": "^0.14.0", + "@camunda/zeebe-element-templates-json-schema": "^0.12.0", "json-source-map": "^0.6.1", "min-dash": "^4.0.0" } }, "node_modules/@bpmn-io/element-templates-validator/node_modules/@camunda/element-templates-json-schema": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@camunda/element-templates-json-schema/-/element-templates-json-schema-0.13.0.tgz", - "integrity": "sha512-G6FMLRL/AAFlnBoMye2qpit3KucEDR7Ga1lmDiAEsMdAMcULEF0Vp6ehmhomU7GrCCsgj40jkkfuS2br+kn21g==" + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@camunda/element-templates-json-schema/-/element-templates-json-schema-0.14.0.tgz", + "integrity": "sha512-0yC5oQx6aRPBjtyjPoJkgY+MR0+R0QmQycFVaLE5Nh7QlVnm0gEgg/lLMkkA8+SJ5cHx/eZWZxl9IjVCUJWBfw==" }, "node_modules/@bpmn-io/extract-process-variables": { "version": "0.8.0", @@ -747,9 +747,9 @@ "dev": true }, "node_modules/@camunda/zeebe-element-templates-json-schema": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@camunda/zeebe-element-templates-json-schema/-/zeebe-element-templates-json-schema-0.11.0.tgz", - "integrity": "sha512-o7M7xWL9dCrZHAYwr3Q51kopjjd4HSq9G84mBukU6+JirHnSD6hw51mE6z5njJha1u0mOWPBM3m/iR9t9WrjnA==" + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@camunda/zeebe-element-templates-json-schema/-/zeebe-element-templates-json-schema-0.12.0.tgz", + "integrity": "sha512-seg7HpOZBnzp6WNROTcUf5rR8CluSvtJAlStphlQpPatMF4EXZ0dF8/WW82BA1e+UMmlJyusRbjir7YtY7nI+A==" }, "node_modules/@codemirror/autocomplete": { "version": "6.9.0", @@ -10205,20 +10205,20 @@ } }, "@bpmn-io/element-templates-validator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@bpmn-io/element-templates-validator/-/element-templates-validator-1.0.0.tgz", - "integrity": "sha512-v8AJk1WjTZpS3am8aAPnjm26/v5PdMJvyacEW1I1sFrIx99/FO4QLfAuk0qdkwvIUmzpR6xB4kuF3o+k4dBzYQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@bpmn-io/element-templates-validator/-/element-templates-validator-1.1.0.tgz", + "integrity": "sha512-f1yjNuyMW+cxzWSD0d3qeVMjcHcDqVQI0tUdDD3OC2xNgmOhf7fOFPdZuE/XIcJ27jeBr/ZuYz5ghsRX4+zKSw==", "requires": { - "@camunda/element-templates-json-schema": "^0.13.0", - "@camunda/zeebe-element-templates-json-schema": "^0.11.0", + "@camunda/element-templates-json-schema": "^0.14.0", + "@camunda/zeebe-element-templates-json-schema": "^0.12.0", "json-source-map": "^0.6.1", "min-dash": "^4.0.0" }, "dependencies": { "@camunda/element-templates-json-schema": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@camunda/element-templates-json-schema/-/element-templates-json-schema-0.13.0.tgz", - "integrity": "sha512-G6FMLRL/AAFlnBoMye2qpit3KucEDR7Ga1lmDiAEsMdAMcULEF0Vp6ehmhomU7GrCCsgj40jkkfuS2br+kn21g==" + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@camunda/element-templates-json-schema/-/element-templates-json-schema-0.14.0.tgz", + "integrity": "sha512-0yC5oQx6aRPBjtyjPoJkgY+MR0+R0QmQycFVaLE5Nh7QlVnm0gEgg/lLMkkA8+SJ5cHx/eZWZxl9IjVCUJWBfw==" } } }, @@ -10367,9 +10367,9 @@ } }, "@camunda/zeebe-element-templates-json-schema": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@camunda/zeebe-element-templates-json-schema/-/zeebe-element-templates-json-schema-0.11.0.tgz", - "integrity": "sha512-o7M7xWL9dCrZHAYwr3Q51kopjjd4HSq9G84mBukU6+JirHnSD6hw51mE6z5njJha1u0mOWPBM3m/iR9t9WrjnA==" + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@camunda/zeebe-element-templates-json-schema/-/zeebe-element-templates-json-schema-0.12.0.tgz", + "integrity": "sha512-seg7HpOZBnzp6WNROTcUf5rR8CluSvtJAlStphlQpPatMF4EXZ0dF8/WW82BA1e+UMmlJyusRbjir7YtY7nI+A==" }, "@codemirror/autocomplete": { "version": "6.9.0", diff --git a/package.json b/package.json index 9058db38..f71f1e49 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ ], "license": "MIT", "dependencies": { - "@bpmn-io/element-templates-validator": "^1.0.0", + "@bpmn-io/element-templates-validator": "^1.1.0", "@bpmn-io/extract-process-variables": "^0.8.0", "bpmnlint": "^8.3.2", "classnames": "^2.3.1", diff --git a/src/cloud-element-templates/Condition.js b/src/cloud-element-templates/Condition.js index 114165b4..04777db6 100644 --- a/src/cloud-element-templates/Condition.js +++ b/src/cloud-element-templates/Condition.js @@ -6,8 +6,14 @@ import { getPropertyValue } from './util/propertyUtil'; export function applyConditions(element, elementTemplate) { const { properties } = elementTemplate; - const filteredProperties = properties.filter(property => { - return isConditionMet(element, properties, property); + let filteredProperties = []; + + properties.forEach(property => { + if (isConditionMet(element, properties, property)) { + filteredProperties.push( + applyDropdownConditions(element, properties, property) + ); + } }); return { @@ -16,6 +22,20 @@ export function applyConditions(element, elementTemplate) { }; } +export function applyDropdownConditions(element, properties, property) { + const { type, choices } = property; + + if (type != 'Dropdown') + return property; + + else { + return { + ...property, + choices: choices.filter(choice => isConditionMet(element, properties, choice)) + }; + } +} + export function isConditionMet(element, properties, property) { const { condition } = property; diff --git a/src/cloud-element-templates/ElementTemplatesConditionChecker.js b/src/cloud-element-templates/ElementTemplatesConditionChecker.js index b48576f3..9e0b8c8c 100644 --- a/src/cloud-element-templates/ElementTemplatesConditionChecker.js +++ b/src/cloud-element-templates/ElementTemplatesConditionChecker.js @@ -5,7 +5,7 @@ import { import { isObject } from 'min-dash'; import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; -import { setPropertyValue, unsetProperty } from './util/propertyUtil'; +import { getPropertyValue, setPropertyValue, unsetProperty } from './util/propertyUtil'; import { MESSAGE_BINDING_TYPES } from './util/bindingTypes'; import { removeMessage } from './util/rootElementUtil'; @@ -58,7 +58,9 @@ export default class ElementTemplatesConditionChecker extends CommandInterceptor return; } - const newTemplate = applyConditions(element, template); + const newTemplate = applyConditions(element, template, this._injector); + + updateDropdownValues(newTemplate.properties, element, this._injector); const propertiesToAdd = getMissingProperties(oldTemplate, newTemplate); const propertiesToRemove = getPropertiesToRemove(newTemplate, oldTemplate); @@ -147,3 +149,22 @@ function equals(a, b) { function hasMessageProperties(template) { return template.properties.some(p => MESSAGE_BINDING_TYPES.includes(p.binding.type)); } + +function updateDropdownValues(properties, element, injector) { + + const commandStack = injector.get('commandStack'); + const bpmnFactory = injector.get('bpmnFactory'); + + properties.forEach(property => { + + if (property.type !== 'Dropdown' || !property.condition) { + return; + } + + const value = getPropertyValue(element, property); + + if (value && !property.choices.find(choice => choice.value === value)) { + setPropertyValue(bpmnFactory, commandStack, element, property, ''); + } + }); +} \ No newline at end of file diff --git a/test/spec/cloud-element-templates/ElementTemplateConditionChecker.spec.js b/test/spec/cloud-element-templates/ElementTemplateConditionChecker.spec.js index ca8098f3..52e3ed25 100644 --- a/test/spec/cloud-element-templates/ElementTemplateConditionChecker.spec.js +++ b/test/spec/cloud-element-templates/ElementTemplateConditionChecker.spec.js @@ -2,6 +2,7 @@ import TestContainer from 'mocha-test-container-support'; import { bootstrapModeler, + bootstrapPropertiesPanel, inject } from 'test/TestHelper'; @@ -17,12 +18,18 @@ import messageDiagramXML from './fixtures/condition-message.bpmn'; import template from './fixtures/condition.json'; import messageTemplates from './fixtures/condition-message.json'; +import dropdownConditions from './fixtures/condition-dropdown.json'; import { getBusinessObject } from 'bpmn-js/lib/util/ModelUtil'; import { findExtension, findMessage, findZeebeSubscription } from 'src/cloud-element-templates/Helper'; import ElementTemplatesConditionChecker from 'src/cloud-element-templates/ElementTemplatesConditionChecker'; import { getBpmnJS } from 'bpmn-js/test/helper'; import { isString } from 'min-dash'; +import { + query as domQuery +} from 'min-dom'; + +import { act } from '@testing-library/preact'; describe('provider/cloud-element-templates - ElementTemplatesConditionChecker', function() { @@ -1116,6 +1123,147 @@ describe('provider/cloud-element-templates - ElementTemplatesConditionChecker', }) ); }); + + + describe('conditional dropdown choices', function() { + + beforeEach(bootstrapPropertiesPanel(diagramXML, { + container: container, + modules: [ + coreModule, + elementTemplatesModule, + modelingModule, + ElementTemplatesConditionChecker, + BpmnPropertiesPanelModule + ], + moddleExtensions: { + zeebe: zeebeModdlePackage + } + })); + + beforeEach(inject(function(elementTemplates) { + elementTemplates.set([ dropdownConditions ]); + })); + + + it('should add conditional entries', inject( + async function(elementRegistry, modeling, selection) { + + // given + const element = elementRegistry.get('Task_1'); + + changeTemplate(element, dropdownConditions); + + // when + await act(() => { + selection.select(element); + modeling.updateProperties(element, { name: 'foo' }); + }); + + // then + expectDropdownOptions(container, 2, 'foo'); + }) + ); + + + it('should switch between conditional properties', inject( + async function(elementRegistry, modeling, selection) { + + // given + const element = elementRegistry.get('Task_1'); + + changeTemplate(element, dropdownConditions); + + // when + await act(() => { + selection.select(element); + modeling.updateProperties(element, { name: 'foo' }); + }); + + // then + expectDropdownOptions(container, 2, 'foo'); + + // when + await act(() => + modeling.updateProperties(element, { name: 'bar' }) + ); + + // then + expectDropdownOptions(container, 2, 'bar'); + }) + ); + + + it('undo', inject(async function(commandStack, elementRegistry, modeling, selection) { + + // given + const element = elementRegistry.get('Task_1'); + + changeTemplate(element, dropdownConditions); + + // when + await act(() => { + selection.select(element); + modeling.updateProperties(element, { name: 'foo' }); + }); + + // assume + expectDropdownOptions(container, 2, 'foo'); + + // when + await act(() => commandStack.undo()); + + // then + expectDropdownOptions(container, 1, 'foobar'); + })); + + + it('redo', inject(async function(commandStack, elementRegistry, modeling, selection) { + + // given + const element = elementRegistry.get('Task_1'); + + changeTemplate(element, dropdownConditions); + + // when + await act(() => { + selection.select(element); + modeling.updateProperties(element, { name: 'foo' }); + }); + + // when + await act(() => commandStack.undo()); + + // then + expectDropdownOptions(container, 1, 'foobar'); + + // when + await act(() => commandStack.redo()); + + // then + expectDropdownOptions(container, 2, 'foo'); + })); + + + it('should remove conditional entries', inject( + async function(elementRegistry, modeling, selection) { + + // given + const element = elementRegistry.get('Task_1'); + + changeTemplate(element, dropdownConditions); + + // when + await act(() => { + selection.select(element); + modeling.updateProperties(element, { name: '' }); + }); + + // then + expectDropdownOptions(container, 1, 'foobar'); + }) + ); + }); }); @@ -1178,4 +1326,11 @@ function expectZeebePropertyValue(businessObject, value) { expect(zeebeProperties).to.exist; expect(properties).to.have.lengthOf(1); expect(properties[0].value).to.eql(value); +} + +function expectDropdownOptions(container, length, value) { + const selectOptions = domQuery('select', container).options; + + expect(selectOptions).to.have.lengthOf(length); + expect(selectOptions[0].value).to.eql(value); } \ No newline at end of file diff --git a/test/spec/cloud-element-templates/fixtures/complex.json b/test/spec/cloud-element-templates/fixtures/complex.json index df37c46f..d58967b8 100644 --- a/test/spec/cloud-element-templates/fixtures/complex.json +++ b/test/spec/cloud-element-templates/fixtures/complex.json @@ -814,52 +814,67 @@ "choices": [ { "name": "Get an issue by ID", - "value": "getIssueById" + "value": "getIssueById", + "condition": { + "property": "operationGroup", + "equals": "issues" + } }, { "name": "Create an issue", - "value": "createAnIssue" + "value": "createAnIssue", + "condition": { + "property": "operationGroup", + "equals": "issues" + } }, { "name": "Delete an issue", - "value": "deleteAnIssue" + "value": "deleteAnIssue", + "condition": { + "property": "operationGroup", + "equals": "issues" + } }, { "name": "Comment to an issue", - "value": "commentToAnIssue" + "value": "commentToAnIssue", + "condition": { + "property": "operationGroup", + "equals": "issues" + } }, { "name": "Search issues", - "value": "searchIssues" - } - ], - "binding": { - "type": "zeebe:input", - "name": "operation" - }, - "condition": { - "property": "operationGroup", - "equals": "issues" - } - }, - { - "label": "Operation", - "id": "operation", - "group": "operation", - "description": "Choose operation to perform", - "type": "Dropdown", - "choices": [ + "value": "searchIssues", + "condition": { + "property": "operationGroup", + "equals": "issues" + } + }, { "name": "List all releases by project ID", - "value": "getReleasesByProjectId" + "value": "getReleasesByProjectId", + "condition": { + "property": "operationGroup", + "equals": "releases" + } }, { "name": "Get release by a tag name", - "value": "getReleaseByTagName" + "value": "getReleaseByTagName", + "condition": { + "property": "operationGroup", + "equals": "releases" + } }, { "name": "Create a release", - "value": "createRelease" + "value": "createRelease", + "condition": { + "property": "operationGroup", + "equals": "releases" + } } ], "binding": { @@ -868,7 +883,7 @@ }, "condition": { "property": "operationGroup", - "equals": "releases" + "oneOf": ["issues", "releases"] } }, { diff --git a/test/spec/cloud-element-templates/fixtures/condition-dropdown.json b/test/spec/cloud-element-templates/fixtures/condition-dropdown.json new file mode 100644 index 00000000..31bbcc68 --- /dev/null +++ b/test/spec/cloud-element-templates/fixtures/condition-dropdown.json @@ -0,0 +1,46 @@ +{ + "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "name": "Conditional dropdown", + "id": "example.com.dropdown-condition", + "description": "A conditional template.", + "appliesTo": ["bpmn:Task"], + "properties": [ + { + "id": "nameProp", + "label": "name", + "type": "String", + "binding": { + "type": "property", + "name": "name" + } + }, + { + "label": "dropdown", + "type": "Dropdown", + "value": "foo", + "choices": [ + { + "name": "FOO", + "value": "foo", + "condition": { + "property": "nameProp", + "equals": "foo" + } + }, + { + "name": "BAR", + "value": "bar", + "condition": { + "property": "nameProp", + "equals": "bar" + } + }, + { "name": "FOOBAR", "value": "foobar" } + ], + "binding": { + "type": "property", + "name": "customDropdownValue" + } + } + ] +}