From 2db13962d755b72e81eab98020a2a2177defccb8 Mon Sep 17 00:00:00 2001 From: smbea Date: Thu, 25 Jan 2024 15:30:34 +0100 Subject: [PATCH] feat(templates): add validation to textarea and select Related to https://github.com/bpmn-io/properties-panel/issues/315 --- .../properties/CustomProperties.js | 12 +- .../properties/CustomProperties.bpmn | 2 +- .../properties/CustomProperties.json | 242 ++++++++++++++++-- .../properties/CustomProperties.spec.js | 157 ++++++------ 4 files changed, 317 insertions(+), 96 deletions(-) diff --git a/src/element-templates/properties/CustomProperties.js b/src/element-templates/properties/CustomProperties.js index 0e65767e..1f31c6db 100644 --- a/src/element-templates/properties/CustomProperties.js +++ b/src/element-templates/properties/CustomProperties.js @@ -295,7 +295,8 @@ function DropdownProperty(props) { } = property; const bpmnFactory = useService('bpmnFactory'), - commandStack = useService('commandStack'); + commandStack = useService('commandStack'), + translate = useService('translate'); const getOptions = () => { const { choices } = property; @@ -316,7 +317,8 @@ function DropdownProperty(props) { description: PropertyDescription({ description }), getValue: propertyGetter(element, property, scope), setValue: propertySetter(bpmnFactory, commandStack, element, property, scope), - disabled: editable === false + disabled: editable === false, + validate: propertyValidator(translate, property) }); } @@ -368,7 +370,8 @@ function TextAreaProperty(props) { const bpmnFactory = useService('bpmnFactory'), commandStack = useService('commandStack'), - debounce = useService('debounceInput'); + debounce = useService('debounceInput'), + translate = useService('translate'); return TextAreaEntry({ debounce, @@ -378,7 +381,8 @@ function TextAreaProperty(props) { description: PropertyDescription({ description }), getValue: propertyGetter(element, property, scope), setValue: propertySetter(bpmnFactory, commandStack, element, property, scope), - disabled: editable === false + disabled: editable === false, + validate: propertyValidator(translate, property) }); } diff --git a/test/spec/element-templates/properties/CustomProperties.bpmn b/test/spec/element-templates/properties/CustomProperties.bpmn index b1f0bf3a..4bc6b3a7 100644 --- a/test/spec/element-templates/properties/CustomProperties.bpmn +++ b/test/spec/element-templates/properties/CustomProperties.bpmn @@ -51,7 +51,7 @@ - + diff --git a/test/spec/element-templates/properties/CustomProperties.json b/test/spec/element-templates/properties/CustomProperties.json index 3028b229..bfc1e650 100644 --- a/test/spec/element-templates/properties/CustomProperties.json +++ b/test/spec/element-templates/properties/CustomProperties.json @@ -313,62 +313,81 @@ "appliesTo": [ "bpmn:Task" ], + "groups": [ + { + "id": "input", + "label": "Textfield" + }, + { + "id": "textarea", + "label": "Textarea" + }, + { + "id": "select", + "label": "Select" + } + ], "properties": [ { - "label": "NotEmpty", + "label": "String - NotEmpty", "description": "Must not be empty", "type": "String", + "group": "input", "binding": { - "type": "camunda:property", - "name": "prop" + "type": "property", + "name": "name" }, "constraints": { "notEmpty": true } }, { - "label": "MinLength", + "label": "String - MinLength", "description": "Must have min length 5", "type": "String", + "group": "input", "binding": { - "type": "camunda:property", - "name": "prop" + "type": "property", + "name": "name" }, "constraints": { "minLength": 5 } }, { - "label": "MaxLength", + "label": "String - MaxLength", "description": "Must have max length 5", "type": "String", + "group": "input", "binding": { - "type": "camunda:property", - "name": "prop" + "type": "property", + "name": "name" }, "constraints": { "maxLength": 5 } }, { - "label": "Pattern (String)", + "label": "String - Pattern (String)", "description": "Must match /A+B/", "type": "String", + "group": "input", "binding": { - "type": "camunda:property", - "name": "prop" + "type": "property", + "name": "name" }, "constraints": { "pattern": "A+B" } }, { - "label": "Pattern (String + Message)", + "label": "String - Pattern (String + Message)", "description": "Must be https url", "type": "String", + "group": "input", "binding": { - "type": "camunda:property", - "name": "prop" + "type": "property", + "name": "name" }, "constraints": { "pattern": { @@ -378,12 +397,13 @@ } }, { - "label": "Pattern (Integer)", + "label": "String - Pattern (Integer)", "description": "Must be integer", "type": "String", + "group": "input", "binding": { - "type": "camunda:property", - "name": "prop" + "type": "property", + "name": "name" }, "constraints": { "pattern": { @@ -391,6 +411,192 @@ "value": "\\d+" } } + }, + { + "label": "TextArea - NotEmpty", + "description": "Must not be empty", + "type": "Text", + "group": "textarea", + "binding": { + "type": "property", + "name": "name" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "TextArea - MinLength", + "description": "Must have min length 5", + "type": "Text", + "group": "textarea", + "binding": { + "type": "property", + "name": "name" + }, + "constraints": { + "minLength": 5 + } + }, + { + "label": "TextArea - MaxLength", + "description": "Must have max length 5", + "type": "Text", + "group": "textarea", + "binding": { + "type": "property", + "name": "name" + }, + "constraints": { + "maxLength": 5 + } + }, + { + "label": "TextArea - Pattern (String)", + "description": "Must match /A+B/", + "type": "Text", + "group": "textarea", + "binding": { + "type": "property", + "name": "name" + }, + "constraints": { + "pattern": "A+B" + } + }, + { + "label": "TextArea - Pattern (String + Message)", + "description": "Must be https url", + "type": "Text", + "group": "textarea", + "binding": { + "type": "property", + "name": "name" + }, + "constraints": { + "pattern": { + "message": "Must start with https://", + "value": "https://.*" + } + } + }, + { + "label": "TextArea - Pattern (Integer)", + "description": "Must be integer", + "type": "Text", + "group": "textarea", + "binding": { + "type": "property", + "name": "name" + }, + "constraints": { + "pattern": { + "message": "Must be positive integer", + "value": "\\d+" + } + } + }, + { + "label": "Select - NotEmpty", + "description": "Must not be empty", + "type": "Dropdown", + "choices": [ + { "name": "FOOO", "value": "FOO" } + ], + "group": "select", + "binding": { + "type": "property", + "name": "name" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Select - MinLength", + "description": "Must have min length 5", + "type": "Dropdown", + "group": "select", + "binding": { + "type": "property", + "name": "name" + }, + "choices": [ + { "name": "FOOOOOOO", "value": "FOOOOOOO" } + ], + "constraints": { + "minLength": 5 + } + }, + { + "label": "Select - MaxLength", + "description": "Must have max length 5", + "type": "Dropdown", + "group": "select", + "binding": { + "type": "property", + "name": "name" + }, + "choices": [ + { "name": "FOOOOOOO", "value": "FOOOOOOO" } + ], + "constraints": { + "maxLength": 5 + } + }, + { + "label": "Select - Pattern (String)", + "description": "Must match /A+B/", + "type": "Dropdown", + "group": "select", + "choices": [ + { "name": "AAAB", "value": "AAAB" } + ], + "binding": { + "type": "property", + "name": "name" + }, + "constraints": { + "pattern": "A+B" + } + }, + { + "label": "Select - Pattern (String + Message)", + "description": "Must be https url", + "type": "Dropdown", + "group": "select", + "choices": [ + { "name": "https://", "value": "https://" } + ], + "binding": { + "type": "property", + "name": "name" + }, + "constraints": { + "pattern": { + "message": "Must start with https://", + "value": "https://.*" + } + } + }, + { + "label": "Select - Pattern (Integer)", + "description": "Must be integer", + "type": "Dropdown", + "group": "select", + "binding": { + "type": "property", + "name": "name" + }, + "choices": [ + { "name": "20", "value": "20" } + ], + "constraints": { + "pattern": { + "message": "Must be positive integer", + "value": "\\d+" + } + } } ] }, diff --git a/test/spec/element-templates/properties/CustomProperties.spec.js b/test/spec/element-templates/properties/CustomProperties.spec.js index bb5a38e1..e5cf15b3 100644 --- a/test/spec/element-templates/properties/CustomProperties.spec.js +++ b/test/spec/element-templates/properties/CustomProperties.spec.js @@ -957,8 +957,7 @@ describe('provider/element-templates - CustomProperties', function() { // assume expect(select.value).to.equal(''); - // TODO(philippfromme): selects can't be validated at the moment - expectValid(entry); + expectError(entry, 'Must not be empty.'); // when changeInput(select, 'POST'); @@ -1514,117 +1513,129 @@ describe('provider/element-templates - CustomProperties', function() { describe('validation', function() { - it('should validate nonEmpty', async function() { + [ + [ 'String', 'input' ], + [ 'Select', 'select' ], + [ 'TextArea', 'textarea' ] + ].forEach(function([ name, selector ]) { - // given - await expectSelected('ValidateTask'); + describe(name, function() { - const entry = findEntry('custom-entry-com.validated-inputs.Task-0', container), - input = findInput('text', entry); + it('should validate nonEmpty', async function() { - // assume - expectError(entry, 'Must not be empty.'); + // given + await expectSelected('ValidateTask'); - // when - changeInput(input, 'FOO'); + const entry = findEntry(`custom-entry-com.validated-inputs.Task-${selector}-0`, container), + input = domQuery(selector, entry); - // then - expectValid(entry); - }); + // assume + expectError(entry, 'Must not be empty.'); + // when + changeInput(input, 'FOO'); - it('should validate minLength', async function() { + // then + expectValid(entry); + }); - // given - await expectSelected('ValidateTask'); - const entry = findEntry('custom-entry-com.validated-inputs.Task-1', container), - input = findInput('text', entry); + it('should validate minLength', async function() { - // assume - expectError(entry, 'Must have min length 5.'); + // given + await expectSelected('ValidateTask'); - // when - changeInput(input, 'FOOOOOOO'); + const entry = findEntry(`custom-entry-com.validated-inputs.Task-${selector}-1`, container), + input = domQuery(selector, entry); - // then - expectValid(entry); - }); + // assume + expectError(entry, 'Must have min length 5.'); + // when + changeInput(input, 'FOOOOOOO'); - it('should validate maxLength', async function() { + // then + expectValid(entry); + }); - // given - await expectSelected('ValidateTask'); - const entry = findEntry('custom-entry-com.validated-inputs.Task-2', container), - input = findInput('text', entry); + it('should validate maxLength', async function() { - // assume - expectValid(entry); + // given + await expectSelected('ValidateTask'); - // when - changeInput(input, 'FOOOOOOO'); + const entry = findEntry(`custom-entry-com.validated-inputs.Task-${selector}-2`, container), + input = domQuery(selector, entry); - // then - expectError(entry, 'Must have max length 5.'); - }); + // assume + expectValid(entry); + // when + changeInput(input, 'FOOOOOOO'); - it('should validate pattern (String)', async function() { + // then + expectError(entry, 'Must have max length 5.'); + }); - // given - await expectSelected('ValidateTask'); - const entry = findEntry('custom-entry-com.validated-inputs.Task-3', container), - input = findInput('text', entry); + it('should validate pattern (String)', async function() { - // assume - expectError(entry, 'Must match pattern A+B.'); + // given + await expectSelected('ValidateTask'); - // when - changeInput(input, 'AAAB'); + const entry = findEntry(`custom-entry-com.validated-inputs.Task-${selector}-3`, container), + input = domQuery(selector, entry); - // then - expectValid(entry); - }); + // assume + expectError(entry, 'Must match pattern A+B.'); + // when + changeInput(input, 'AAAB'); - it('should validate pattern (String + Message)', async function() { + // then + expectValid(entry); + }); - // given - await expectSelected('ValidateTask'); - const entry = findEntry('custom-entry-com.validated-inputs.Task-4', container), - input = findInput('text', entry); + it('should validate pattern (String + Message)', async function() { - // assume - expectError(entry, 'Must start with https://'); + // given + await expectSelected('ValidateTask'); - // when - changeInput(input, 'https://'); + const entry = findEntry(`custom-entry-com.validated-inputs.Task-${selector}-4`, container), + input = domQuery(selector, entry); - // then - expectValid(entry); - }); + // assume + expectError(entry, 'Must start with https://'); + // when + changeInput(input, 'https://'); - it('should validate pattern (Integer)', async function() { + // then + expectValid(entry); + }); - // given - await expectSelected('ValidateTask'); - const entry = findEntry('custom-entry-com.validated-inputs.Task-5', container), - input = findInput('text', entry); + it('should validate pattern (Integer)', async function() { - // assume - expectError(entry, 'Must be positive integer'); + // given + await expectSelected('ValidateTask'); - // when - changeInput(input, '20'); + const entry = findEntry(`custom-entry-com.validated-inputs.Task-${selector}-5`, container), + input = domQuery(selector, entry); + + // assume + expectError(entry, 'Must be positive integer'); + + // when + changeInput(input, '20'); + + // then + expectValid(entry); + }); + + }); - // then - expectValid(entry); }); });