Skip to content

Commit

Permalink
feat: use field instance validation in formfields
Browse files Browse the repository at this point in the history
Closes #1147
  • Loading branch information
Skaiir committed Apr 16, 2024
1 parent 5021a60 commit 5c0b830
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 48 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { UpdateFieldValidationHandler } from './cmd/UpdateFieldValidationHandler';
import { UpdateFieldInstanceValidationHandler } from './cmd/UpdateFieldInstanceValidationHandler';

export class ViewerCommands {
constructor(commandStack, eventBus) {
Expand All @@ -18,9 +19,13 @@ export class ViewerCommands {
getHandlers() {
return {
'formField.validation.update': UpdateFieldValidationHandler,
'formFieldInstance.validation.update': UpdateFieldInstanceValidationHandler,
};
}

/**
* @deprecated
*/
updateFieldValidation(field, value, indexes) {
const context = {
field,
Expand All @@ -30,6 +35,15 @@ export class ViewerCommands {

this._commandStack.execute('formField.validation.update', context);
}

updateFieldInstanceValidation(fieldInstance, value) {
const context = {
fieldInstance,
value,
};

this._commandStack.execute('formFieldInstance.validation.update', context);
}
}

ViewerCommands.$inject = ['commandStack', 'eventBus'];
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { set } from 'min-dash';
import { clone } from '../../../util';

export class UpdateFieldInstanceValidationHandler {
constructor(form, validator) {
this._form = form;
this._validator = validator;
}

execute(context) {
const { fieldInstance, value } = context;
const { id, indexes } = fieldInstance;
const { errors } = this._form._getState();

context.oldErrors = clone(errors);

const fieldErrors = this._validator.validateFieldInstance(fieldInstance, value);
const updatedErrors = set(
errors,
[id, ...Object.values(indexes || {})],
fieldErrors.length ? fieldErrors : undefined,
);
this._form._setState({ errors: updatedErrors });
}

revert(context) {
this._form._setState({ errors: context.oldErrors });
}
}

UpdateFieldInstanceValidationHandler.$inject = ['form', 'validator'];
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { set } from 'min-dash';
import { clone } from '../../../util';

/**
* @deprecated
*/
export class UpdateFieldValidationHandler {
constructor(form, validator) {
this._form = form;
Expand Down
17 changes: 7 additions & 10 deletions packages/form-js-viewer/src/render/components/FormField.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,35 +98,31 @@ export function FormField(props) {

if (initialValidationTrigger && hasInitialValue) {
setInitialValidationTrigger(false);
viewerCommands.updateFieldValidation(field, initialValue, indexes);
viewerCommands.updateFieldInstanceValidation(fieldInstance, initialValue);
}
}, [viewerCommands, field, initialValue, initialValidationTrigger, indexes]);
}, [fieldInstance, initialValidationTrigger, initialValue, viewerCommands]);

const onBlur = useCallback(() => {
const value = get(data, valuePath);

if (initialValidationTrigger) {
setInitialValidationTrigger(false);
viewerCommands.updateFieldValidation(field, value, indexes);
viewerCommands.updateFieldInstanceValidation(fieldInstance, value);
}

eventBus.fire('formField.blur', { formField: field });
}, [eventBus, field, indexes, viewerCommands, initialValidationTrigger, data, valuePath]);
}, [data, eventBus, field, fieldInstance, initialValidationTrigger, valuePath, viewerCommands]);

const onFocus = useCallback(() => {
eventBus.fire('formField.focus', { formField: field });
}, [eventBus, field]);

const onChange = useCallback(
(update) => {
if (!fieldConfig.keyed) {
return;
}

setInitialValidationTrigger(false);
_onChange({ ...update, field, indexes, fieldInstance });
_onChange({ field, indexes, fieldInstance, ...update });
},
[_onChange, field, fieldConfig.keyed, fieldInstance, indexes],
[_onChange, field, fieldInstance, indexes],
);

if (hidden) {
Expand All @@ -147,6 +143,7 @@ export function FormField(props) {
onFocus={disabled || readonly ? noop : onFocus}
readonly={readonly}
value={value}
fieldInstance={fieldInstance}
/>
);

Expand Down
72 changes: 34 additions & 38 deletions packages/form-js-viewer/src/util/expressions.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,34 @@
import { wrapObjectKeysWithUnderscores } from './simple';


/**
* Transform a LocalExpressionContext object into a usable FEEL context.
*
* @param {Object} context - The LocalExpressionContext object.
* @returns {Object} The usable FEEL context.
*/

export function buildExpressionContext(context) {
const {
data,
...specialContextKeys
} = context;

return {
...specialContextKeys,
...data,
...wrapObjectKeysWithUnderscores(specialContextKeys)
};
}

/**
* Evaluate a string based on the expressionLanguage and context information.
* 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 {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));
}
return value;
}
import { wrapObjectKeysWithUnderscores } from './simple';

/**
* Transform a LocalExpressionContext object into a usable FEEL context.
*
* @param {Object} context - The LocalExpressionContext object.
* @returns {Object} The usable FEEL context.
*/

export function buildExpressionContext(context) {
const { data, ...specialContextKeys } = context;

return {
...specialContextKeys,
...data,
...wrapObjectKeysWithUnderscores(specialContextKeys),
};
}

/**
* Evaluate a string based on the expressionLanguage and context information.
* 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 {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));
}
return value;
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,5 @@ export class ExpressionLanguageMock {

export class ViewerCommandsMock {
updateFieldValidation() {}
updateFieldInstanceValidation() {}
}

0 comments on commit 5c0b830

Please sign in to comment.