From 89bc5b10c9eb0b3f8a73feabac61414fac22b4df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Goulart?= Date: Fri, 23 Aug 2024 03:15:32 +0200 Subject: [PATCH] fix: fix filepicker component merge error --- .../src/carbon-styles.js | 3 +- .../src/carbon-styles.scss | 3 +- .../test/spec/complex.json | 6 + .../src/components/PlaygroundRoot.js | 3 +- .../form-js-playground/test/spec/form.json | 6 + packages/form-js-viewer/src/Form.js | 5 +- .../src/core/FormFieldInstanceRegistry.js | 60 +++-- .../expressionLanguage/ConditionChecker.js | 6 + .../repeatRender/RepeatRenderManager.js | 71 +++--- .../form-js-viewer/src/render/FileRegistry.js | 91 +++++++ .../src/render/components/FormField.js | 36 ++- .../components/form-fields/FilePicker.js | 108 +++++---- .../form-js-viewer/src/render/hooks/index.js | 1 + .../hooks/useBooleanExpressionEvaluation.js | 21 ++ .../render/hooks/useExpressionEvaluation.js | 5 +- .../src/render/hooks/useService.js | 6 + packages/form-js-viewer/src/render/index.js | 2 + .../src/util/constants/FilePickerConstants.js | 3 + .../form-js-viewer/src/util/expressions.js | 5 +- .../extractFileReferencesFromRemovedData.js | 33 +++ .../core/FormFieldInstanceRegistry.spec.js | 222 ++++++------------ .../form-fields/Dynamiclist.spec.js | 15 +- .../components/form-fields/FilePicker.spec.js | 89 +++++-- .../render/components/helper/mocks/index.js | 25 ++ 24 files changed, 520 insertions(+), 305 deletions(-) create mode 100644 packages/form-js-viewer/src/render/FileRegistry.js create mode 100644 packages/form-js-viewer/src/render/hooks/useBooleanExpressionEvaluation.js create mode 100644 packages/form-js-viewer/src/util/constants/FilePickerConstants.js create mode 100644 packages/form-js-viewer/src/util/extractFileReferencesFromRemovedData.js diff --git a/packages/form-js-carbon-styles/src/carbon-styles.js b/packages/form-js-carbon-styles/src/carbon-styles.js index 8c5fbeeb1..80c25f412 100644 --- a/packages/form-js-carbon-styles/src/carbon-styles.js +++ b/packages/form-js-carbon-styles/src/carbon-styles.js @@ -676,7 +676,8 @@ const RADIO_STYLES = css` const BUTTON_STYLES = css` .fjs-container { - .fjs-form-field.fjs-form-field-button .fjs-button { + .fjs-form-field.fjs-form-field-button .fjs-button, + .fjs-form-field.fjs-form-field-filepicker .fjs-button { font-size: var(--cds-body-short-01-font-size); font-weight: var(--cds-body-short-01-font-weight); line-height: var(--cds-body-short-01-line-height); diff --git a/packages/form-js-carbon-styles/src/carbon-styles.scss b/packages/form-js-carbon-styles/src/carbon-styles.scss index 3e43f4f8b..5d5df95e1 100644 --- a/packages/form-js-carbon-styles/src/carbon-styles.scss +++ b/packages/form-js-carbon-styles/src/carbon-styles.scss @@ -795,7 +795,8 @@ // Button styles ///////////// .fjs-container { - .fjs-form-field.fjs-form-field-button .fjs-button { + .fjs-form-field.fjs-form-field-button .fjs-button, + .fjs-form-field.fjs-form-field-filepicker .fjs-button { font-size: var(--cds-body-short-01-font-size); font-weight: var(--cds-body-short-01-font-weight); line-height: var(--cds-body-short-01-line-height); diff --git a/packages/form-js-carbon-styles/test/spec/complex.json b/packages/form-js-carbon-styles/test/spec/complex.json index 9c12838c6..35fc9626e 100644 --- a/packages/form-js-carbon-styles/test/spec/complex.json +++ b/packages/form-js-carbon-styles/test/spec/complex.json @@ -643,6 +643,12 @@ "height": 30, "id": "Field_1a82jjsdasd" }, + { + "type": "filepicker", + "key": "filepicker", + "accept": ".png,.jpg", + "multiple": true + }, { "action": "reset", "label": "reset", diff --git a/packages/form-js-playground/src/components/PlaygroundRoot.js b/packages/form-js-playground/src/components/PlaygroundRoot.js index b89a3040c..1a4e3347f 100644 --- a/packages/form-js-playground/src/components/PlaygroundRoot.js +++ b/packages/form-js-playground/src/components/PlaygroundRoot.js @@ -140,7 +140,8 @@ export function PlaygroundRoot(config) { // pipe viewer changes to output data editor formViewer.on('changed', updateOutputData); - formViewer.on('formFieldInstanceRegistry.changed', updateOutputData); + formViewer.on('formFieldInstance.added', updateOutputData); + formViewer.on('formFieldInstance.removed', updateOutputData); inputDataEditor.on('changed', (event) => { try { diff --git a/packages/form-js-playground/test/spec/form.json b/packages/form-js-playground/test/spec/form.json index 1dcf0f7c9..aaf4b2b61 100644 --- a/packages/form-js-playground/test/spec/form.json +++ b/packages/form-js-playground/test/spec/form.json @@ -1,6 +1,12 @@ { "$schema": "../../../form-json-schema/resources/schema.json", "components": [ + { + "type": "filepicker", + "key": "filepicker", + "accept": ".png,.jpg", + "multiple": true + }, { "type": "expression", "key": "expressionResult", diff --git a/packages/form-js-viewer/src/Form.js b/packages/form-js-viewer/src/Form.js index 07d7572c4..9653358c5 100644 --- a/packages/form-js-viewer/src/Form.js +++ b/packages/form-js-viewer/src/Form.js @@ -153,7 +153,7 @@ export class Form { /** * Submit the form, triggering all field validations. * - * @returns { { data: Data, errors: Errors } } + * @returns { { data: Data, errors: Errors, files: Map } } */ submit() { const { properties } = this._getState(); @@ -168,9 +168,12 @@ export class Form { const errors = this.validate(); + const files = this.get('fileRegistry').getAllFiles(); + const result = { data, errors, + files, }; this._emit('submit', result); diff --git a/packages/form-js-viewer/src/core/FormFieldInstanceRegistry.js b/packages/form-js-viewer/src/core/FormFieldInstanceRegistry.js index 57e4e083f..0eff3673c 100644 --- a/packages/form-js-viewer/src/core/FormFieldInstanceRegistry.js +++ b/packages/form-js-viewer/src/core/FormFieldInstanceRegistry.js @@ -9,36 +9,50 @@ export class FormFieldInstanceRegistry { eventBus.on('form.clear', () => this.clear()); } - add(instance) { - const { id, expressionContextInfo, valuePath, indexes } = instance; - - const instanceId = [id, ...Object.values(indexes || {})].join('_'); - - if (this._formFieldInstances[instanceId]) { - throw new Error('this form field instance is already registered'); + syncInstance(instanceId, formFieldInfo) { + const { hidden, ...restInfo } = formFieldInfo; + + const isInstanceExpected = !hidden; + const doesInstanceExist = this._formFieldInstances[instanceId]; + + if (isInstanceExpected && !doesInstanceExist) { + this._formFieldInstances[instanceId] = { + instanceId, + ...restInfo, + }; + + this._eventBus.fire('formFieldInstance.added', { instanceId }); + } else if (!isInstanceExpected && doesInstanceExist) { + delete this._formFieldInstances[instanceId]; + + this._eventBus.fire('formFieldInstance.removed', { instanceId }); + } else if (isInstanceExpected && doesInstanceExist) { + const wasInstanceChaged = Object.keys(restInfo).some((key) => { + return this._formFieldInstances[instanceId][key] !== restInfo[key]; + }); + + if (wasInstanceChaged) { + this._formFieldInstances[instanceId] = { + instanceId, + ...restInfo, + }; + + this._eventBus.fire('formFieldInstance.changed', { instanceId }); + } } - this._formFieldInstances[instanceId] = { - id, - instanceId, - expressionContextInfo, - valuePath, - indexes, - }; - - this._eventBus.fire('formFieldInstanceRegistry.changed', { instanceId, action: 'added' }); - return instanceId; } - remove(instanceId) { - if (!this._formFieldInstances[instanceId]) { - return; + cleanupInstance(instanceId) { + if (this._formFieldInstances[instanceId]) { + delete this._formFieldInstances[instanceId]; + this._eventBus.fire('formFieldInstance.removed', { instanceId }); } + } - delete this._formFieldInstances[instanceId]; - - this._eventBus.fire('formFieldInstanceRegistry.changed', { instanceId, action: 'removed' }); + get(instanceId) { + return this._formFieldInstances[instanceId]; } getAll() { diff --git a/packages/form-js-viewer/src/features/expressionLanguage/ConditionChecker.js b/packages/form-js-viewer/src/features/expressionLanguage/ConditionChecker.js index 04a184f61..72c8bd64f 100644 --- a/packages/form-js-viewer/src/features/expressionLanguage/ConditionChecker.js +++ b/packages/form-js-viewer/src/features/expressionLanguage/ConditionChecker.js @@ -84,11 +84,17 @@ export class ConditionChecker { // if we have a hidden repeatable field, and the data structure allows, we clear it directly at the root and stop recursion if (context.isHidden && isRepeatable) { context.preventRecursion = true; + this._eventBus.fire('conditionChecker.remove', { + item: { [field.key]: get(workingData, getFilterPath(field, indexes)) }, + }); this._cleanlyClearDataAtPath(getFilterPath(field, indexes), workingData); } // for simple leaf fields, we always clear if (context.isHidden && isClosed) { + this._eventBus.fire('conditionChecker.remove', { + item: { [field.key]: get(workingData, getFilterPath(field, indexes)) }, + }); this._cleanlyClearDataAtPath(getFilterPath(field, indexes), workingData); } }); diff --git a/packages/form-js-viewer/src/features/repeatRender/RepeatRenderManager.js b/packages/form-js-viewer/src/features/repeatRender/RepeatRenderManager.js index c9a9eba25..329552bd8 100644 --- a/packages/form-js-viewer/src/features/repeatRender/RepeatRenderManager.js +++ b/packages/form-js-viewer/src/features/repeatRender/RepeatRenderManager.js @@ -15,11 +15,16 @@ import { useScrollIntoView } from '../../render/hooks'; import classNames from 'classnames'; export class RepeatRenderManager { - constructor(form, formFields, formFieldRegistry, pathRegistry) { + constructor(form, formFields, formFieldRegistry, pathRegistry, eventBus) { this._form = form; + /** @type {import('../../render/FormFields').FormFields} */ this._formFields = formFields; + /** @type {import('../../core/FormFieldRegistry').FormFieldRegistry} */ this._formFieldRegistry = formFieldRegistry; + /** @type {import('../../core/PathRegistry').PathRegistry} */ this._pathRegistry = pathRegistry; + /** @type {import('../../core/EventBus').EventBus} */ + this._eventBus = eventBus; this.Repeater = this.Repeater.bind(this); this.RepeatFooter = this.RepeatFooter.bind(this); } @@ -58,12 +63,14 @@ export class RepeatRenderManager { const hasChildren = repeaterField.components && repeaterField.components.length > 0; const showRemove = repeaterField.allowAddRemove && hasChildren; - const displayValues = isCollapsed ? values.slice(0, nonCollapsedItems) : values; - const hiddenValues = isCollapsed ? values.slice(nonCollapsedItems) : []; - + /** + * @param {number} index + */ const onDeleteItem = (index) => { const updatedValues = values.slice(); - updatedValues.splice(index, 1); + const removedItem = updatedValues.splice(index, 1)[0]; + + this._eventBus.fire('repeatRenderManager.remove', { dataPath, index, item: removedItem }); props.onChange({ field: repeaterField, @@ -76,38 +83,24 @@ export class RepeatRenderManager { return ( <> - {displayValues.map((itemValue, itemIndex) => ( - - ))} - {hiddenValues.length > 0 ? ( -
- {hiddenValues.map((itemValue, itemIndex) => ( - - ))} + {values.map((itemValue, itemIndex) => ( +
= nonCollapsedItems : false, + })}> +
- ) : null} + ))} ); } @@ -146,6 +139,8 @@ export class RepeatRenderManager { shouldScroll.current = true; + this._eventBus.fire('repeatRenderManager.add', { dataPath, index: updatedValues.length - 1, item: newItem }); + props.onChange({ value: updatedValues, }); @@ -186,7 +181,7 @@ export class RepeatRenderManager {