diff --git a/packages/form-js-editor/src/features/properties-panel/PropertiesProvider.js b/packages/form-js-editor/src/features/properties-panel/PropertiesProvider.js index fd996ee13..45fb43d1e 100644 --- a/packages/form-js-editor/src/features/properties-panel/PropertiesProvider.js +++ b/packages/form-js-editor/src/features/properties-panel/PropertiesProvider.js @@ -67,7 +67,7 @@ export class PropertiesProvider { SerializationGroup(field, editField), ConstraintsGroup(field, editField), ValidationGroup(field, editField), - CustomValidationsGroup(field, editField), + CustomValidationsGroup(field, editField, getService), CustomPropertiesGroup(field, editField), ].filter((group) => group != null); diff --git a/packages/form-js-editor/src/features/properties-panel/entries/CustomValidationEntry.js b/packages/form-js-editor/src/features/properties-panel/entries/CustomValidationEntry.js index a797b3aa5..53f80d3d7 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/CustomValidationEntry.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/CustomValidationEntry.js @@ -1,21 +1,12 @@ -import { - get, - set -} from 'min-dash'; +import { get, set } from 'min-dash'; import { useService } from '../hooks'; import { TextFieldEntry, FeelEntry } from '@bpmn-io/properties-panel'; import { useCallback } from 'preact/hooks'; - export function CustomValidationEntry(props) { - const { - editField, - field, - idPrefix, - index - } = props; + const { editField, field, idPrefix, index } = props; const entries = [ { @@ -24,7 +15,7 @@ export function CustomValidationEntry(props) { field, id: idPrefix + '-condition', idPrefix, - index + index, }, { component: Message, @@ -32,37 +23,31 @@ export function CustomValidationEntry(props) { field, id: idPrefix + '-message', idPrefix, - index - } + index, + }, ]; return entries; } function Condition(props) { - const { - editField, - field, - id, - index - } = props; + const { editField, field, id, index } = props; const debounce = useService('debounce'); - const setValue = (value, error) => { if (error) { return; } - const validate = get(field, [ 'validate' ]); - const newValidate = set(validate, [ 'custom', index, 'condition' ], value); + const validate = get(field, ['validate']); + const newValidate = set(validate, ['custom', index, 'condition'], value); return editField(field, 'validate', newValidate); }; const getValue = () => { - return get(field, [ 'validate', 'custom', index, 'condition' ]); + return get(field, ['validate', 'custom', index, 'condition']); }; const conditionEntryValidate = useCallback((value) => { @@ -79,17 +64,12 @@ function Condition(props) { id, label: 'Condition', setValue, - validate: conditionEntryValidate + validate: conditionEntryValidate, }); } function Message(props) { - const { - editField, - field, - id, - index - } = props; + const { editField, field, id, index } = props; const debounce = useService('debounce'); @@ -98,14 +78,14 @@ function Message(props) { return; } - const validate = get(field, [ 'validate' ]); - const newValidate = set(validate, [ 'custom', index, 'message' ], value); + const validate = get(field, ['validate']); + const newValidate = set(validate, ['custom', index, 'message'], value); return editField(field, 'validate', newValidate); }; const getValue = () => { - return get(field, [ 'validate', 'custom', index, 'message' ]); + return get(field, ['validate', 'custom', index, 'message']); }; const messageEntryValidate = useCallback((value) => { @@ -121,6 +101,6 @@ function Message(props) { id, label: 'Message', setValue, - validate: messageEntryValidate + validate: messageEntryValidate, }); } diff --git a/packages/form-js-editor/src/features/properties-panel/groups/CustomValidationsGroup.js b/packages/form-js-editor/src/features/properties-panel/groups/CustomValidationsGroup.js index 3a9343483..a44ffd941 100644 --- a/packages/form-js-editor/src/features/properties-panel/groups/CustomValidationsGroup.js +++ b/packages/form-js-editor/src/features/properties-panel/groups/CustomValidationsGroup.js @@ -1,22 +1,19 @@ -import { - get, - set, - without -} from 'min-dash'; +import { get, set, without } from 'min-dash'; import { arrayAdd } from '../Util'; -import { - ListGroup -} from '@bpmn-io/properties-panel'; +import { ListGroup } from '@bpmn-io/properties-panel'; import { VALIDATION_TYPE_OPTIONS } from './ValidationGroup'; import { CustomValidationEntry } from '../entries/CustomValidationEntry'; -export function CustomValidationsGroup(field, editField) { +export function CustomValidationsGroup(field, editField, getService) { + const validate = get(field, ['validate'], {}); + const { validatable, keyed } = getService('formFields').get(field.type).config; - const validate = get(field, [ 'validate' ], {}); - const shouldRender = [ undefined, VALIDATION_TYPE_OPTIONS.custom.value ].includes(validate.validationType); + const isValidatable = validatable !== undefined ? validatable : keyed; + const isCustomValidation = [undefined, VALIDATION_TYPE_OPTIONS.custom.value].includes(validate.validationType); + const shouldRender = isValidatable && isCustomValidation; if (!shouldRender) { return; @@ -26,44 +23,38 @@ export function CustomValidationsGroup(field, editField) { id: 'custom-validation', label: 'Custom Validations', component: ListGroup, - ...CustomValidationsEntry({ editField, field, id: 'custom-validation-list' }) + ...CustomValidationsEntry({ editField, field, id: 'custom-validation-list' }), }; } - export function CustomValidationsEntry(props) { - const { - editField, - field, - id: idPrefix - } = props; + const { editField, field, id: idPrefix } = props; const addEntry = (e) => { - e.stopPropagation(); - const customValidations = get(field, [ 'validate', 'custom' ], []); + const customValidations = get(field, ['validate', 'custom'], []); const newIndex = customValidations.length + 1; const newValue = { condition: 'false', - message: 'Error' + message: 'Error', }; const newArray = arrayAdd(customValidations, newIndex, newValue); - const newValidate = set(field.validate, [ 'custom' ], newArray); + const newValidate = set(field.validate || {}, ['custom'], newArray); - editField(field, [ 'validate' ], newValidate); + editField(field, ['validate'], newValidate); }; const removeEntry = (entry) => { - const customValidations = get(field, [ 'validate', 'custom' ], []); + const customValidations = get(field, ['validate', 'custom'], []); const newArray = without(customValidations, entry); - const newValidate = set(field.validate, [ 'custom' ], newArray); + const newValidate = set(field.validate, ['custom'], newArray); - editField(field, [ 'validate' ], newValidate); + editField(field, ['validate'], newValidate); }; - const items = get(field, [ 'validate', 'custom' ], []).map((entry, index) => { + const items = get(field, ['validate', 'custom'], []).map((entry, index) => { const id = idPrefix + '-' + index; return { @@ -72,15 +63,16 @@ export function CustomValidationsEntry(props) { editField, field, idPrefix, - index + index, }), - remove: () => removeEntry(entry) + label: 'Rule ' + (index + 1), + remove: () => removeEntry(entry), }; }); return { items, add: addEntry, - shouldSort: false + shouldSort: false, }; -} \ No newline at end of file +} diff --git a/packages/form-js-viewer/src/core/Validator.js b/packages/form-js-viewer/src/core/Validator.js index 5af98fe5d..2a51cd689 100644 --- a/packages/form-js-viewer/src/core/Validator.js +++ b/packages/form-js-viewer/src/core/Validator.js @@ -79,7 +79,10 @@ export class Validator { if ('custom' in evaluatedValidation && value && evaluatedValidation.custom.length) { const { custom } = evaluatedValidation; custom.forEach(({ condition, message }) => { - if (condition && !runExpressionEvaluation(this._expressionLanguage, value, expressionContextInfo)) { + if ( + condition && + !runExpressionEvaluation(this._expressionLanguage, condition, { ...expressionContextInfo, value }) + ) { errors = [...errors, message]; } }); diff --git a/packages/form-js-viewer/src/render/components/form-fields/ExpressionField.js b/packages/form-js-viewer/src/render/components/form-fields/ExpressionField.js index 5ac067847..79aaceddb 100644 --- a/packages/form-js-viewer/src/render/components/form-fields/ExpressionField.js +++ b/packages/form-js-viewer/src/render/components/form-fields/ExpressionField.js @@ -45,6 +45,7 @@ ExpressionField.config = { label: 'Expression', group: 'basic-input', keyed: true, + validatable: false, emptyValue: null, escapeGridRender: true, create: (options = {}) => ({ diff --git a/packages/form-js-viewer/src/util/expressions.js b/packages/form-js-viewer/src/util/expressions.js index 81640b281..858c72d40 100644 --- a/packages/form-js-viewer/src/util/expressions.js +++ b/packages/form-js-viewer/src/util/expressions.js @@ -22,13 +22,13 @@ export function buildExpressionContext(context) { * If the string is not an expression, it is returned as is. * * @param {any} expressionLanguage - The expression language to use. - * @param {string} value - The string to evaluate. + * @param {string} expression - The string expression to evaluate. * @param {Object} expressionContextInfo - The context information to use. * @returns {any} - Evaluated value or the original value if not an expression. */ -export function runExpressionEvaluation(expressionLanguage, value, expressionContextInfo) { - if (expressionLanguage && expressionLanguage.isExpression(value)) { - return expressionLanguage.evaluate(value, buildExpressionContext(expressionContextInfo)); +export function runExpressionEvaluation(expressionLanguage, expression, expressionContextInfo) { + if (expressionLanguage && expressionLanguage.isExpression(expression)) { + return expressionLanguage.evaluate(expression, buildExpressionContext(expressionContextInfo)); } - return value; + return expression; }