diff --git a/package-lock.json b/package-lock.json index 6249bc56e..d05164c6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@babel/core": "^7.18.10", "@babel/plugin-transform-react-jsx": "^7.14.5", "@babel/plugin-transform-react-jsx-source": "^7.14.5", - "@bpmn-io/properties-panel": "^3.13.0", + "@bpmn-io/properties-panel": "^3.18.0", "@carbon/react": "^1.42.1", "@carbon/styles": "^1.42.1", "@playwright/test": "^1.40.1", @@ -2134,14 +2134,14 @@ "link": true }, "node_modules/@bpmn-io/properties-panel": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@bpmn-io/properties-panel/-/properties-panel-3.13.0.tgz", - "integrity": "sha512-KaeHwZpWDycSj8mDnDT/iO5TvyL9eYw+oHbj/69BDMblDp4xenUasWKrif5chZdt/B9gGnVtJp4vIdvlauC/XA==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@bpmn-io/properties-panel/-/properties-panel-3.18.0.tgz", + "integrity": "sha512-Tn6kbfZR9EVhusMsn1h4sD/mShT11vXB7lKQWZwShu4c7b15flNKoM9T1YXl0ATc9uzDP4W2Fj+XjXRKIB2g2g==", "dependencies": { - "@bpmn-io/feel-editor": "^1.0.0", + "@bpmn-io/feel-editor": "^1.2.0", "@codemirror/view": "^6.14.0", "classnames": "^2.3.1", - "feelers": "^1.1.0", + "feelers": "^1.3.0", "focus-trap": "^7.5.2", "min-dash": "^4.1.1", "min-dom": "^4.0.3" @@ -22040,7 +22040,7 @@ "dependencies": { "@bpmn-io/draggle": "^4.0.0", "@bpmn-io/form-js-viewer": "^1.6.4", - "@bpmn-io/properties-panel": "^3.13.0", + "@bpmn-io/properties-panel": "^3.18.0", "array-move": "^3.0.1", "big.js": "^6.2.1", "ids": "^1.0.0", @@ -23626,7 +23626,7 @@ "requires": { "@bpmn-io/draggle": "^4.0.0", "@bpmn-io/form-js-viewer": "^1.6.4", - "@bpmn-io/properties-panel": "^3.13.0", + "@bpmn-io/properties-panel": "^3.18.0", "array-move": "^3.0.1", "big.js": "^6.2.1", "ids": "^1.0.0", @@ -23748,14 +23748,14 @@ "version": "file:packages/form-json-schema" }, "@bpmn-io/properties-panel": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@bpmn-io/properties-panel/-/properties-panel-3.13.0.tgz", - "integrity": "sha512-KaeHwZpWDycSj8mDnDT/iO5TvyL9eYw+oHbj/69BDMblDp4xenUasWKrif5chZdt/B9gGnVtJp4vIdvlauC/XA==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@bpmn-io/properties-panel/-/properties-panel-3.18.0.tgz", + "integrity": "sha512-Tn6kbfZR9EVhusMsn1h4sD/mShT11vXB7lKQWZwShu4c7b15flNKoM9T1YXl0ATc9uzDP4W2Fj+XjXRKIB2g2g==", "requires": { - "@bpmn-io/feel-editor": "^1.0.0", + "@bpmn-io/feel-editor": "^1.2.0", "@codemirror/view": "^6.14.0", "classnames": "^2.3.1", - "feelers": "^1.1.0", + "feelers": "^1.3.0", "focus-trap": "^7.5.2", "min-dash": "^4.1.1", "min-dom": "^4.0.3" diff --git a/package.json b/package.json index 951c5daa9..10aeddf15 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "@babel/core": "^7.18.10", "@babel/plugin-transform-react-jsx": "^7.14.5", "@babel/plugin-transform-react-jsx-source": "^7.14.5", - "@bpmn-io/properties-panel": "^3.13.0", + "@bpmn-io/properties-panel": "^3.18.0", "@carbon/react": "^1.42.1", "@carbon/styles": "^1.42.1", "@playwright/test": "^1.40.1", diff --git a/packages/form-js-editor/package.json b/packages/form-js-editor/package.json index 779fdc034..d21338416 100644 --- a/packages/form-js-editor/package.json +++ b/packages/form-js-editor/package.json @@ -48,7 +48,7 @@ "dependencies": { "@bpmn-io/draggle": "^4.0.0", "@bpmn-io/form-js-viewer": "^1.6.4", - "@bpmn-io/properties-panel": "^3.13.0", + "@bpmn-io/properties-panel": "^3.18.0", "array-move": "^3.0.1", "big.js": "^6.2.1", "ids": "^1.0.0", diff --git a/packages/form-js-editor/src/features/properties-panel/Util.js b/packages/form-js-editor/src/features/properties-panel/Util.js index 36428c75f..9e05429a2 100644 --- a/packages/form-js-editor/src/features/properties-panel/Util.js +++ b/packages/form-js-editor/src/features/properties-panel/Util.js @@ -27,6 +27,11 @@ export function countDecimals(number) { return num.toFixed().split('.')[1].length || 0; } +/** + * + * @param {unknown} value + * @returns {boolean} + */ export function isValidNumber(value) { return (typeof value === 'number' || typeof value === 'string') && value !== '' && !isNaN(Number(value)); } diff --git a/packages/form-js-editor/src/features/properties-panel/entries/ColumnEntry.js b/packages/form-js-editor/src/features/properties-panel/entries/ColumnEntry.js index b91185cc2..3cfb0eebf 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/ColumnEntry.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/ColumnEntry.js @@ -17,8 +17,7 @@ export function ColumnEntry(props) { editField, field, idPrefix, - index, - validateFactory + index } = props; const entries = [ @@ -28,8 +27,7 @@ export function ColumnEntry(props) { field, id: idPrefix + '-label', idPrefix, - index, - validateFactory + index }, { component: Key, @@ -37,8 +35,7 @@ export function ColumnEntry(props) { field, id: idPrefix + '-key', idPrefix, - index, - validateFactory + index } ]; diff --git a/packages/form-js-editor/src/features/properties-panel/entries/ColumnsEntry.js b/packages/form-js-editor/src/features/properties-panel/entries/ColumnsEntry.js index 618dfb9ec..56b11c344 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/ColumnsEntry.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/ColumnsEntry.js @@ -8,6 +8,7 @@ import { } from '@bpmn-io/properties-panel'; import { MIN_COLUMNS } from '../../../core/FormLayoutValidator'; +import { useCallback } from 'preact/hooks'; export const AUTO_OPTION_VALUE = ''; @@ -41,9 +42,9 @@ function Columns(props) { const debounce = useService('debounce'); const formLayoutValidator = useService('formLayoutValidator'); - const validate = (value) => { + const validate = useCallback((value) => { return formLayoutValidator.validateField(field, value ? parseInt(value) : null); - }; + }, [ field, formLayoutValidator ]); const setValue = (value, error) => { diff --git a/packages/form-js-editor/src/features/properties-panel/entries/ColumnsExpressionEntry.js b/packages/form-js-editor/src/features/properties-panel/entries/ColumnsExpressionEntry.js index 21201a32b..f79951055 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/ColumnsExpressionEntry.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/ColumnsExpressionEntry.js @@ -54,19 +54,6 @@ function ColumnsExpression(props) { editField(field, PATH, value); }; - /** - * @param {string|void} value - * @returns {string|null} - */ - const validate = (value) => { - - if (!isString(value) || value.length === 0 || value === '=') { - return 'Must not be empty.'; - } - - return null; - }; - const schema = '[\n {\n "key": "column_1",\n "label": "Column 1"\n }\n]'; const tooltip =
@@ -90,3 +77,19 @@ function ColumnsExpression(props) { validate, }); } + + +// helpers ////////// + +/** + * @param {string|void} value + * @returns {string|null} + */ +const validate = (value) => { + + if (!isString(value) || value.length === 0 || value === '=') { + return 'Must not be empty.'; + } + + return null; +}; diff --git a/packages/form-js-editor/src/features/properties-panel/entries/CustomValueEntry.js b/packages/form-js-editor/src/features/properties-panel/entries/CustomValueEntry.js index 12c5b259e..80a41b11f 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/CustomValueEntry.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/CustomValueEntry.js @@ -3,6 +3,7 @@ import { get } from 'min-dash'; import { useService } from '../hooks'; import { TextFieldEntry } from '@bpmn-io/properties-panel'; +import { useCallback } from 'preact/hooks'; export function CustomValueEntry(props) { @@ -63,6 +64,12 @@ function Key(props) { return Object.keys(get(field, [ 'properties' ]))[ index ]; }; + const validate = useCallback( + () => validateFactory(Object.keys(get(field, [ 'properties' ]))[index]), + [ validateFactory, field, index ], + ); + + return TextFieldEntry({ debounce, element: field, @@ -70,7 +77,7 @@ function Key(props) { id, label: 'Key', setValue, - validate: validateFactory(getValue()) + validate }); } diff --git a/packages/form-js-editor/src/features/properties-panel/entries/DefaultValueEntry.js b/packages/form-js-editor/src/features/properties-panel/entries/DefaultValueEntry.js index 080b93500..6ab43462a 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/DefaultValueEntry.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/DefaultValueEntry.js @@ -14,6 +14,7 @@ import Big from 'big.js'; import { useService } from '../hooks'; import { countDecimals, INPUTS, isValidNumber, OPTIONS_INPUTS } from '../Util'; +import { useCallback } from 'preact/hooks'; export const EMPTY_OPTION = null; @@ -178,6 +179,22 @@ function DefaultValueNumber(props) { const decimalDigitsSet = decimalDigits || decimalDigits === 0; + const validate = useCallback( + (value) => { + if (value === undefined || value === null) { + return; + } + + if (!isValidNumber(value)) { + return 'Should be a valid number'; + } + if (decimalDigitsSet && countDecimals(value) > decimalDigits) { + return `Should not contain more than ${decimalDigits} decimal digits`; + } + }, + [ decimalDigitsSet, decimalDigits ], + ); + return TextFieldEntry({ debounce, label, @@ -185,11 +202,7 @@ function DefaultValueNumber(props) { getValue, id, setValue, - validate: (value) => { - if (value === undefined || value === null) return; - if (!isValidNumber(value)) return 'Should be a valid number'; - if (decimalDigitsSet && countDecimals(value) > decimalDigits) return `Should not contain more than ${decimalDigits} decimal digits`; - } + validate }); } diff --git a/packages/form-js-editor/src/features/properties-panel/entries/HeightEntry.js b/packages/form-js-editor/src/features/properties-panel/entries/HeightEntry.js index 586f32a5f..0d1a1179d 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/HeightEntry.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/HeightEntry.js @@ -65,10 +65,27 @@ function Height(props) { id, getValue, setValue, - validate: (value) => { - if (value === undefined || value === null) return; - if (value < 1) return 'Should be greater than zero.'; - if (!Number.isInteger(value)) return 'Should be an integer.'; - } + validate }); } + +// helpers ////////// + +/** + * @param {number|void} value + * @returns {string|null} + */ +const validate = (value) => { + if (typeof value !== 'number') { + return null; + } + + if (!Number.isInteger(value)) { + return 'Should be an integer.'; + } + + if (value < 1) { + return 'Should be greater than zero.'; + } +}; + diff --git a/packages/form-js-editor/src/features/properties-panel/entries/HtmlEntry.js b/packages/form-js-editor/src/features/properties-panel/entries/HtmlEntry.js index fd722f445..3972dbd4c 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/HtmlEntry.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/HtmlEntry.js @@ -45,15 +45,7 @@ function Content(props) { return editField(field, path, value || ''); }; - const validate = (value) => { - // allow empty state - if (value === undefined || value === null || value === '') { return null; } - - // allow expressions - if (value.startsWith('=')) { return null; } - - }; return FeelTemplatingEntry({ debounce, @@ -69,4 +61,22 @@ function Content(props) { }); } -const description = <>Supports HTML, styling, and templating. Styles are automatically scoped to the HTML component. Learn more; \ No newline at end of file +// helpers ////////// + +const description = <>Supports HTML, styling, and templating. Styles are automatically scoped to the HTML component. Learn more; + +/** + * @param {string|void} value + * @returns {string|null} + */ +const validate = (value) => { + + // allow empty state + if (typeof value !== 'string' || value === '') { + return null; + } + + // allow expressions + if (value.startsWith('=')) { return null; } + +}; \ No newline at end of file diff --git a/packages/form-js-editor/src/features/properties-panel/entries/IFrameUrlEntry.js b/packages/form-js-editor/src/features/properties-panel/entries/IFrameUrlEntry.js index 6d4ec119a..cf4efa3d8 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/IFrameUrlEntry.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/IFrameUrlEntry.js @@ -47,16 +47,6 @@ function Url(props) { return editField(field, path, value); }; - const validate = (value) => { - if (!value || value.startsWith('=')) { - return; - } - - if (!HTTPS_PATTERN.test(value)) { - return 'For security reasons the URL must start with "https".'; - } - }; - return FeelTemplatingEntry({ debounce, element: field, @@ -88,4 +78,18 @@ function getTooltip() {

); -} \ No newline at end of file +} + +/** + * @param {string|void} value + * @returns {string|null} + */ +const validate = (value) => { + if (!value || value.startsWith('=')) { + return; + } + + if (!HTTPS_PATTERN.test(value)) { + return 'For security reasons the URL must start with "https".'; + } +}; \ No newline at end of file diff --git a/packages/form-js-editor/src/features/properties-panel/entries/IdEntry.js b/packages/form-js-editor/src/features/properties-panel/entries/IdEntry.js index a982ddce6..d3e7fbbad 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/IdEntry.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/IdEntry.js @@ -3,6 +3,7 @@ import { get, isUndefined } from 'min-dash'; import { useService } from '../hooks'; import { TextFieldEntry, isTextFieldEntryEdited } from '@bpmn-io/properties-panel'; +import { useCallback } from 'preact/hooks'; export function IdEntry(props) { @@ -49,7 +50,7 @@ function Id(props) { return editField(field, path, value); }; - const validate = (value) => { + const validate = useCallback((value) => { if (isUndefined(value) || !value.length) { return 'Must not be empty.'; } @@ -61,7 +62,7 @@ function Id(props) { } return validateId(value) || null; - }; + }, [ formFieldRegistry, field ]); return TextFieldEntry({ debounce, diff --git a/packages/form-js-editor/src/features/properties-panel/entries/InputKeyOptionsSourceEntry.js b/packages/form-js-editor/src/features/properties-panel/entries/InputKeyOptionsSourceEntry.js index 33cc6a00a..269b0ba2b 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/InputKeyOptionsSourceEntry.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/InputKeyOptionsSourceEntry.js @@ -1,5 +1,5 @@ import { TextFieldEntry, isTextFieldEntryEdited } from '@bpmn-io/properties-panel'; -import { get, isUndefined } from 'min-dash'; +import { get } from 'min-dash'; import { useService } from '../hooks'; import { OPTIONS_SOURCES, OPTIONS_SOURCES_PATHS } from '@bpmn-io/form-js-viewer'; @@ -50,18 +50,6 @@ function InputValuesKey(props) { editField(field, path, value || ''); }; - const validate = (value) => { - if (isUndefined(value) || !value.length) { - return 'Must not be empty.'; - } - - if (/\s/.test(value)) { - return 'Must not contain spaces.'; - } - - return null; - }; - return TextFieldEntry({ debounce, description: 'Define which input property to populate the values from', @@ -74,3 +62,22 @@ function InputValuesKey(props) { validate }); } + + +// helpers ////////// + +/** + * @param {string|void} value + * @returns {string|null} + */ +const validate = (value) => { + if (typeof value !== 'string' || value.length === 0) { + return 'Must not be empty.'; + } + + if (/\s/.test(value)) { + return 'Must not contain spaces.'; + } + + return null; +}; diff --git a/packages/form-js-editor/src/features/properties-panel/entries/KeyEntry.js b/packages/form-js-editor/src/features/properties-panel/entries/KeyEntry.js index 9a9fd0128..27c5adcd4 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/KeyEntry.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/KeyEntry.js @@ -5,6 +5,7 @@ import { hasIntegerPathSegment, isValidDotPath } from '../Util'; import { useService } from '../hooks'; import { TextFieldEntry, isTextFieldEntryEdited } from '@bpmn-io/properties-panel'; +import { useCallback } from 'preact/hooks'; export function KeyEntry(props) { @@ -57,7 +58,7 @@ function Key(props) { return editField(field, path, value); }; - const validate = (value) => { + const validate = useCallback((value) => { if (value === field.key) { return null; @@ -88,7 +89,7 @@ function Key(props) { pathRegistry.claimPath(oldPath, { isClosed: true, claimerId: field.id }); return canClaim ? null : 'Must not conflict with other key/path assignments.'; - }; + }, [ field, pathRegistry ]); return TextFieldEntry({ debounce, diff --git a/packages/form-js-editor/src/features/properties-panel/entries/NumberEntries.js b/packages/form-js-editor/src/features/properties-panel/entries/NumberEntries.js index b313813b5..214fae2ca 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/NumberEntries.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/NumberEntries.js @@ -4,6 +4,7 @@ import { useService } from '../hooks'; import { countDecimals, isValidNumber } from '../Util'; import Big from 'big.js'; +import { useCallback } from 'preact/hooks'; export function NumberEntries(props) { const { @@ -63,11 +64,7 @@ function NumberDecimalDigits(props) { getValue, id, setValue, - validate: (value) => { - if (value === undefined || value === null) return; - if (value < 0) return 'Should be greater than or equal to zero.'; - if (!Number.isInteger(value)) return 'Should be an integer.'; - } + validate: validateNumberEntries }); } @@ -111,29 +108,62 @@ function NumberArrowStep(props) { const decimalDigitsSet = decimalDigits || decimalDigits === 0; - return TextFieldEntry({ - debounce, - label: 'Increment', - element: field, - getValue, - id, - setValue, - validate: (value) => { - - if (value === undefined || value === null) return; + const validate = useCallback( + (value) => { + if (value === undefined || value === null) { + return; + } - if (!isValidNumber(value)) return 'Should be a valid number.'; + if (!isValidNumber(value)) { + return 'Should be a valid number.'; + } - if (Big(value).cmp(0) <= 0) return 'Should be greater than zero.'; + if (Big(value).cmp(0) <= 0) { + return 'Should be greater than zero.'; + } if (decimalDigitsSet) { const minimumValue = Big(`1e-${decimalDigits}`); - if (Big(value).cmp(minimumValue) < 0) return `Should be at least ${minimumValue.toString()}.`; - if (countDecimals(value) > decimalDigits) return `Should not contain more than ${decimalDigits} decimal digits.`; + if (Big(value).cmp(minimumValue) < 0) { + return `Should be at least ${minimumValue.toString()}.`; + } + if (countDecimals(value) > decimalDigits) { + return `Should not contain more than ${decimalDigits} decimal digits.`; + } } - } + }, + [ decimalDigitsSet, decimalDigits ], + ); + + return TextFieldEntry({ + debounce, + label: 'Increment', + element: field, + getValue, + id, + setValue, + validate }); +} -} \ No newline at end of file +// helpers ////////// + +/** + * @param {number|void} value + * @returns {string|void} + */ +const validateNumberEntries = (value) => { + if (typeof value !== 'number') { + return; + } + + if (!Number.isInteger(value)) { + return 'Should be an integer.'; + } + + if (value < 0) { + return 'Should be greater than or equal to zero.'; + } +}; diff --git a/packages/form-js-editor/src/features/properties-panel/entries/PathEntry.js b/packages/form-js-editor/src/features/properties-panel/entries/PathEntry.js index bc8137ff9..7eb9d5544 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/PathEntry.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/PathEntry.js @@ -5,6 +5,7 @@ import { useService } from '../hooks'; import { TextFieldEntry, isTextFieldEntryEdited } from '@bpmn-io/properties-panel'; import { isValidDotPath } from '../Util'; +import { useCallback } from 'preact/hooks'; export function PathEntry(props) { @@ -61,7 +62,7 @@ function Path(props) { return editField(field, path, value); }; - const validate = (value) => { + const validate = useCallback((value) => { if (!value && isRepeating) { return 'Must not be empty'; @@ -102,7 +103,7 @@ function Path(props) { // If all checks pass return null; - }; + }, [ field, isRepeating, pathRegistry ]); const tooltip = isRepeating ? 'Routes the children of this component into a form variable, may be left empty to route at the root level.' diff --git a/packages/form-js-editor/src/features/properties-panel/entries/RowCountEntry.js b/packages/form-js-editor/src/features/properties-panel/entries/RowCountEntry.js index 275bcac1e..f39f9bd16 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/RowCountEntry.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/RowCountEntry.js @@ -51,31 +51,6 @@ function RowCount(props) { editField(field, path, value); }; - /** - * @param {string|void} value - * @returns {string|null} - */ - const validate = (value) => { - - if (isNil(value)) { - return null; - } - - if (!isNumber(value)) { - return 'Must be number'; - } - - if (!Number.isInteger(value)) { - return 'Should be an integer.'; - } - - if (value < 1) { - return 'Should be greater than zero.'; - } - - return null; - }; - return NumberFieldEntry({ debounce, label: 'Number of rows per page', @@ -85,4 +60,32 @@ function RowCount(props) { setValue, validate }); -} \ No newline at end of file +} + + +// helpers ////////// + +/** + * @param {string|void} value + * @returns {string|null} + */ +const validate = (value) => { + + if (isNil(value)) { + return null; + } + + if (!isNumber(value)) { + return 'Must be number'; + } + + if (!Number.isInteger(value)) { + return 'Should be an integer.'; + } + + if (value < 1) { + return 'Should be greater than zero.'; + } + + return null; +}; \ No newline at end of file diff --git a/packages/form-js-editor/src/features/properties-panel/entries/TableDataSourceEntry.js b/packages/form-js-editor/src/features/properties-panel/entries/TableDataSourceEntry.js index ccaa323a2..d9e7dce7d 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/TableDataSourceEntry.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/TableDataSourceEntry.js @@ -51,32 +51,6 @@ function Source(props) { editField(field, path, value); }; - /** - * @param {string|void} value - * @returns {string|null} - */ - const validate = (value) => { - - if (!isString(value) || value.length === 0) { - return 'Must not be empty.'; - } - - if (value.startsWith('=')) { - return null; - } - - if (!isValidDotPath(value)) { - return 'Must be a variable or a dot separated path.'; - } - - if (hasIntegerPathSegment(value)) { - return 'Must not contain numerical path segments.'; - } - - return null; - }; - - return FeelTemplatingEntry({ debounce, description: 'Specify the source from which to populate the table', @@ -92,3 +66,31 @@ function Source(props) { validate, }); } + + +// helper //////////////// + +/** + * @param {string|void} value + * @returns {string|null} + */ +const validate = (value) => { + + if (!isString(value) || value.length === 0) { + return 'Must not be empty.'; + } + + if (value.startsWith('=')) { + return null; + } + + if (!isValidDotPath(value)) { + return 'Must be a variable or a dot separated path.'; + } + + if (hasIntegerPathSegment(value)) { + return 'Must not contain numerical path segments.'; + } + + return null; +}; diff --git a/packages/form-js-editor/src/features/properties-panel/entries/ValueEntry.js b/packages/form-js-editor/src/features/properties-panel/entries/ValueEntry.js index 67dee370b..7b46a7e2c 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/ValueEntry.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/ValueEntry.js @@ -6,6 +6,7 @@ import { import { useService } from '../hooks'; import { TextFieldEntry } from '@bpmn-io/properties-panel'; +import { useCallback } from 'preact/hooks'; export function ValueEntry(props) { @@ -65,6 +66,15 @@ function Label(props) { return get(field, [ 'values', index, 'label' ]); }; + const validate = useCallback( + () => + validateFactory( + get(field, [ 'values', index, 'label' ]), + (entry) => entry.label, + ), + [ field, index, validateFactory ], + ); + return TextFieldEntry({ debounce, element: field, @@ -72,7 +82,7 @@ function Label(props) { id, label: 'Label', setValue, - validate: validateFactory(getValue(), (entry) => entry.label) + validate }); } @@ -100,6 +110,15 @@ function Value(props) { return get(field, [ 'values', index, 'value' ]); }; + const validate = useCallback( + () => + validateFactory( + get(field, [ 'values', index, 'value' ]), + (entry) => entry.value, + ), + [ field, index, validateFactory ], + ); + return TextFieldEntry({ debounce, element: field, @@ -107,6 +126,6 @@ function Value(props) { id, label: 'Value', setValue, - validate: validateFactory(getValue(), (entry) => entry.value) + validate }); } \ No newline at end of file diff --git a/packages/form-js-editor/src/features/properties-panel/entries/factories/simpleRangeIntegerEntryFactory.js b/packages/form-js-editor/src/features/properties-panel/entries/factories/simpleRangeIntegerEntryFactory.js index 4b0e52121..f45b58704 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/factories/simpleRangeIntegerEntryFactory.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/factories/simpleRangeIntegerEntryFactory.js @@ -4,6 +4,7 @@ import { TextFieldEntry, isTextFieldEntryEdited } from '@bpmn-io/properties-pane import { isValidNumber } from '../../Util'; import Big from 'big.js'; +import { useCallback } from 'preact/hooks'; export function simpleRangeIntegerEntryFactory(options) { const { @@ -60,6 +61,13 @@ const SimpleRangeIntegerEntry = (props) => { editField(field, path, Number(value)); }; + const validate = useCallback((value) => { + if (value === undefined || value === null || value === '') { return; } + if (!Number.isInteger(Number(value))) { return 'Should be an integer.'; } + if (Big(value).cmp(min) < 0) { return `Should be at least ${min}.`; } + if (Big(value).cmp(max) > 0) { return `Should be at most ${max}.`; } + }, [ min, max ]); + return TextFieldEntry({ debounce, label, @@ -67,11 +75,6 @@ const SimpleRangeIntegerEntry = (props) => { getValue, id, setValue, - validate: (value) => { - if (value === undefined || value === null || value === '') { return; } - if (!Number.isInteger(Number(value))) { return 'Should be an integer.'; } - if (Big(value).cmp(min) < 0) { return `Should be at least ${min}.`; } - if (Big(value).cmp(max) > 0) { return `Should be at most ${max}.`; } - } + validate }); };