From 70bdc52dc217e4427b920128bee124900e7f09a2 Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Thu, 31 Oct 2024 21:21:37 +0100 Subject: [PATCH 01/10] :recycle: Refactor demo plugins configuration to use modal Instead of inline configuration options, expose them via a config modal instead, to be consistent with other, real, registration backends. The goal is to be able to drop RJSF eventually. --- .../form_design/RegistrationFields.stories.js | 68 ++++++++++++++---- .../registrations/demo/DemoOptionsForm.js | 72 +++++++++++++++++++ .../form_design/registrations/demo/index.js | 3 + .../admin/form_design/registrations/index.js | 5 ++ .../shared/OptionsConfiguration.js | 3 +- 5 files changed, 136 insertions(+), 15 deletions(-) create mode 100644 src/openforms/js/components/admin/form_design/registrations/demo/DemoOptionsForm.js create mode 100644 src/openforms/js/components/admin/form_design/registrations/demo/index.js diff --git a/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js b/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js index a320c6c0e2..064b777da8 100644 --- a/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js +++ b/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js @@ -24,20 +24,6 @@ export default { component: RegistrationFields, args: { availableBackends: [ - { - id: 'demo', - label: 'Demo - print to console', - schema: { - properties: { - extraLine: { - minLength: 1, - title: 'Extra print statement', - type: 'string', - }, - }, - type: 'object', - }, - }, { id: 'zgw-create-zaak', label: "ZGW API's", @@ -281,6 +267,48 @@ export default { properties: {}, }, }, + { + id: 'demo', + label: 'Demo - print to console', + schema: { + properties: { + extraLine: { + minLength: 1, + title: 'Extra print statement', + type: 'string', + }, + }, + type: 'object', + }, + }, + { + id: 'failing-demo', + label: 'Demo - fail registration', + schema: { + properties: { + extraLine: { + minLength: 1, + title: 'Extra print statement', + type: 'string', + }, + }, + type: 'object', + }, + }, + { + id: 'exception-demo', + label: 'Demo - exception during registration', + schema: { + properties: { + extraLine: { + minLength: 1, + title: 'Extra print statement', + type: 'string', + }, + }, + type: 'object', + }, + }, ], configuredBackends: [], onChange: fn(), @@ -495,6 +523,18 @@ export const ConfiguredBackends = { zdsZaaktypeStatusOmschrijving: '', }, }, + { + key: 'backend7', + name: 'Failing demo', + backend: 'failing-demo', + options: {extraLine: 'Filled out option'}, + }, + { + key: 'backend8', + name: 'Crashing demo', + backend: 'exception-demo', + options: {extraLine: 'Filled out option'}, + }, ], validationErrors: [ ['form.registrationBackends.1.options.zgwApiGroup', 'You sure about this?'], diff --git a/src/openforms/js/components/admin/form_design/registrations/demo/DemoOptionsForm.js b/src/openforms/js/components/admin/form_design/registrations/demo/DemoOptionsForm.js new file mode 100644 index 0000000000..da983638c2 --- /dev/null +++ b/src/openforms/js/components/admin/form_design/registrations/demo/DemoOptionsForm.js @@ -0,0 +1,72 @@ +import {useField} from 'formik'; +import PropTypes from 'prop-types'; +import React, {useContext} from 'react'; +import {FormattedMessage} from 'react-intl'; + +import OptionsConfiguration from 'components/admin/form_design/registrations/shared/OptionsConfiguration'; +import {filterErrors} from 'components/admin/form_design/registrations/shared/utils'; +import Field from 'components/admin/forms/Field'; +import Fieldset from 'components/admin/forms/Fieldset'; +import FormRow from 'components/admin/forms/FormRow'; +import {TextInput} from 'components/admin/forms/Inputs'; +import { + ValidationErrorContext, + ValidationErrorsProvider, +} from 'components/admin/forms/ValidationErrors'; + +const ExtraLine = () => { + const [fieldProps] = useField('extraLine'); + return ( + + + } + > + + + + ); +}; + +const DemoOptionsForm = ({name, label, formData, onChange}) => { + const validationErrors = useContext(ValidationErrorContext); + const relevantErrors = filterErrors(name, validationErrors); + return ( + + } + initialFormData={{extraLine: '', ...formData}} + onSubmit={values => onChange({formData: values})} + modalSize="small" + > + +
+ +
+
+
+ ); +}; + +DemoOptionsForm.propTypes = { + name: PropTypes.string.isRequired, + label: PropTypes.node.isRequired, + formData: PropTypes.shape({ + extraLine: PropTypes.string, + }), + onChange: PropTypes.func.isRequired, +}; + +export default DemoOptionsForm; diff --git a/src/openforms/js/components/admin/form_design/registrations/demo/index.js b/src/openforms/js/components/admin/form_design/registrations/demo/index.js new file mode 100644 index 0000000000..711ad84ab7 --- /dev/null +++ b/src/openforms/js/components/admin/form_design/registrations/demo/index.js @@ -0,0 +1,3 @@ +import DemoOptionsForm from './DemoOptionsForm'; + +export default DemoOptionsForm; diff --git a/src/openforms/js/components/admin/form_design/registrations/index.js b/src/openforms/js/components/admin/form_design/registrations/index.js index b5c8146bc6..1cd0e014d3 100644 --- a/src/openforms/js/components/admin/form_design/registrations/index.js +++ b/src/openforms/js/components/admin/form_design/registrations/index.js @@ -1,4 +1,5 @@ import CamundaOptionsForm from './camunda'; +import DemoOptionsForm from './demo'; import EmailOptionsForm from './email'; import ObjectsApiOptionsForm from './objectsapi/ObjectsApiOptionsForm'; import ObjectsApiSummaryHandler from './objectsapi/ObjectsApiSummaryHandler'; @@ -44,4 +45,8 @@ export const BACKEND_OPTIONS_FORMS = { 'stuf-zds-create-zaak': { form: StufZDSOptionsForm, }, + // demo plugins + demo: {form: DemoOptionsForm}, + 'failing-demo': {form: DemoOptionsForm}, + 'exception-demo': {form: DemoOptionsForm}, }; diff --git a/src/openforms/js/components/admin/form_design/registrations/shared/OptionsConfiguration.js b/src/openforms/js/components/admin/form_design/registrations/shared/OptionsConfiguration.js index 66211ac2ec..d2b53be39a 100644 --- a/src/openforms/js/components/admin/form_design/registrations/shared/OptionsConfiguration.js +++ b/src/openforms/js/components/admin/form_design/registrations/shared/OptionsConfiguration.js @@ -16,6 +16,7 @@ const OptionsConfiguration = ({ modalTitle, initialFormData, onSubmit, + modalSize = 'large', children, }) => { const intl = useIntl(); @@ -64,7 +65,7 @@ const OptionsConfiguration = ({ isOpen={modalOpen} title={modalTitle} closeModal={() => setModalOpen(false)} - extraModifiers={['large']} + extraModifiers={modalSize ? [modalSize] : undefined} > Date: Thu, 31 Oct 2024 21:35:50 +0100 Subject: [PATCH 02/10] :recycle: Use explicit React config form for MS Graph registration options Another step towards dropping RJSF. --- .../admin/form_design/registrations/index.js | 2 + .../ms_graph/MSGraphOptionsForm.js | 52 +++++++++++++++ .../registrations/ms_graph/fields.js | 63 +++++++++++++++++++ .../registrations/ms_graph/index.js | 3 + 4 files changed, 120 insertions(+) create mode 100644 src/openforms/js/components/admin/form_design/registrations/ms_graph/MSGraphOptionsForm.js create mode 100644 src/openforms/js/components/admin/form_design/registrations/ms_graph/fields.js create mode 100644 src/openforms/js/components/admin/form_design/registrations/ms_graph/index.js diff --git a/src/openforms/js/components/admin/form_design/registrations/index.js b/src/openforms/js/components/admin/form_design/registrations/index.js index 1cd0e014d3..0a4748155a 100644 --- a/src/openforms/js/components/admin/form_design/registrations/index.js +++ b/src/openforms/js/components/admin/form_design/registrations/index.js @@ -1,6 +1,7 @@ import CamundaOptionsForm from './camunda'; import DemoOptionsForm from './demo'; import EmailOptionsForm from './email'; +import MSGraphOptionsForm from './ms_graph'; import ObjectsApiOptionsForm from './objectsapi/ObjectsApiOptionsForm'; import ObjectsApiSummaryHandler from './objectsapi/ObjectsApiSummaryHandler'; import ObjectsApiVariableConfigurationEditor from './objectsapi/ObjectsApiVariableConfigurationEditor'; @@ -45,6 +46,7 @@ export const BACKEND_OPTIONS_FORMS = { 'stuf-zds-create-zaak': { form: StufZDSOptionsForm, }, + 'microsoft-graph': {form: MSGraphOptionsForm}, // demo plugins demo: {form: DemoOptionsForm}, 'failing-demo': {form: DemoOptionsForm}, diff --git a/src/openforms/js/components/admin/form_design/registrations/ms_graph/MSGraphOptionsForm.js b/src/openforms/js/components/admin/form_design/registrations/ms_graph/MSGraphOptionsForm.js new file mode 100644 index 0000000000..3501139856 --- /dev/null +++ b/src/openforms/js/components/admin/form_design/registrations/ms_graph/MSGraphOptionsForm.js @@ -0,0 +1,52 @@ +import PropTypes from 'prop-types'; +import React, {useContext} from 'react'; +import {FormattedMessage} from 'react-intl'; + +import OptionsConfiguration from 'components/admin/form_design/registrations/shared/OptionsConfiguration'; +import {filterErrors} from 'components/admin/form_design/registrations/shared/utils'; +import Fieldset from 'components/admin/forms/Fieldset'; +import { + ValidationErrorContext, + ValidationErrorsProvider, +} from 'components/admin/forms/ValidationErrors'; + +import {DriveID, FolderPath} from './fields'; + +const MSGraphOptionsForm = ({name, label, formData, onChange}) => { + const validationErrors = useContext(ValidationErrorContext); + const relevantErrors = filterErrors(name, validationErrors); + return ( + + } + initialFormData={{extraLine: '', ...formData}} + onSubmit={values => onChange({formData: values})} + modalSize="" + > + +
+ + +
+
+
+ ); +}; + +MSGraphOptionsForm.propTypes = { + name: PropTypes.string.isRequired, + label: PropTypes.node.isRequired, + formData: PropTypes.shape({ + extraLine: PropTypes.string, + }), + onChange: PropTypes.func.isRequired, +}; + +export default MSGraphOptionsForm; diff --git a/src/openforms/js/components/admin/form_design/registrations/ms_graph/fields.js b/src/openforms/js/components/admin/form_design/registrations/ms_graph/fields.js new file mode 100644 index 0000000000..0d1cc88b73 --- /dev/null +++ b/src/openforms/js/components/admin/form_design/registrations/ms_graph/fields.js @@ -0,0 +1,63 @@ +import {useField} from 'formik'; +import {FormattedMessage} from 'react-intl'; + +import Field from 'components/admin/forms/Field'; +import FormRow from 'components/admin/forms/FormRow'; +import {TextInput} from 'components/admin/forms/Inputs'; + +export const FolderPath = () => { + const [fieldProps] = useField('folderPath'); + return ( + + + } + helpText={ + '{{' year '}}', '{{' month '}}' and + '{{' day '}}'. The path must start with /. + `} + values={{ + code: chunks => {chunks}, + }} + /> + } + > + + + + ); +}; + +export const DriveID = () => { + const [fieldProps] = useField('driveId'); + return ( + + + } + helpText={ + + } + > + + + + ); +}; diff --git a/src/openforms/js/components/admin/form_design/registrations/ms_graph/index.js b/src/openforms/js/components/admin/form_design/registrations/ms_graph/index.js new file mode 100644 index 0000000000..e60a77d8ad --- /dev/null +++ b/src/openforms/js/components/admin/form_design/registrations/ms_graph/index.js @@ -0,0 +1,3 @@ +import MSGraphOptionsForm from './MSGraphOptionsForm'; + +export default MSGraphOptionsForm; From 8558f57555613ee82d893a5da86a27d66537e7dd Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Thu, 31 Oct 2024 21:36:12 +0100 Subject: [PATCH 03/10] :globe_with_meridians: Extract client site translation messages --- src/openforms/js/compiled-lang/en.json | 92 ++++++++++++++++++++++++++ src/openforms/js/compiled-lang/nl.json | 92 ++++++++++++++++++++++++++ src/openforms/js/lang/en.json | 30 +++++++++ src/openforms/js/lang/nl.json | 30 +++++++++ 4 files changed, 244 insertions(+) diff --git a/src/openforms/js/compiled-lang/en.json b/src/openforms/js/compiled-lang/en.json index f0fd88ca93..007a53ec0d 100644 --- a/src/openforms/js/compiled-lang/en.json +++ b/src/openforms/js/compiled-lang/en.json @@ -503,6 +503,12 @@ "value": "Toggle JSON Schema" } ], + "3zaZWN": [ + { + "type": 0, + "value": "Plugin configuration: demo" + } + ], "4/cCvG": [ { "type": 0, @@ -2413,6 +2419,12 @@ "value": "Something went wrong while rendering the registration options" } ], + "LBEeSy": [ + { + "type": 0, + "value": "Drive ID" + } + ], "LD3weJ": [ { "type": 0, @@ -4965,6 +4977,12 @@ "value": "Session will expire" } ], + "jpL29S": [ + { + "type": 0, + "value": "Folder path" + } + ], "jtWzSW": [ { "type": 0, @@ -5135,6 +5153,68 @@ "value": "Including today" } ], + "lNBZdl": [ + { + "type": 0, + "value": "The path of the folder where folders containing Open-Forms related documents will be created. You can use the expressions " + }, + { + "children": [ + { + "type": 0, + "value": "{{ year }}" + } + ], + "type": 8, + "value": "code" + }, + { + "type": 0, + "value": ", " + }, + { + "children": [ + { + "type": 0, + "value": "{{ month }}" + } + ], + "type": 8, + "value": "code" + }, + { + "type": 0, + "value": " and " + }, + { + "children": [ + { + "type": 0, + "value": "{{ day }}" + } + ], + "type": 8, + "value": "code" + }, + { + "type": 0, + "value": ". The path must start with " + }, + { + "children": [ + { + "type": 0, + "value": "/" + } + ], + "type": 8, + "value": "code" + }, + { + "type": 0, + "value": "." + } + ], "lOSmt+": [ { "type": 0, @@ -6019,6 +6099,12 @@ "value": "After how many seconds should the cached response expire." } ], + "wAQaa5": [ + { + "type": 0, + "value": "Extra print statement" + } + ], "wIaGgb": [ { "type": 0, @@ -6101,6 +6187,12 @@ "value": "Component where times for an appointment will be shown" } ], + "wnTTZr": [ + { + "type": 0, + "value": "ID of the drive to use. If left empty, the default drive will be used." + } + ], "wnxKT/": [ { "type": 0, diff --git a/src/openforms/js/compiled-lang/nl.json b/src/openforms/js/compiled-lang/nl.json index b440c28c3a..fa2a66b5e4 100644 --- a/src/openforms/js/compiled-lang/nl.json +++ b/src/openforms/js/compiled-lang/nl.json @@ -503,6 +503,12 @@ "value": "Toon/verberg JSON Schema" } ], + "3zaZWN": [ + { + "type": 0, + "value": "Plugin configuration: demo" + } + ], "4/cCvG": [ { "type": 0, @@ -2434,6 +2440,12 @@ "value": "Er ging iets fout bij het weergeven van de registratie-opties." } ], + "LBEeSy": [ + { + "type": 0, + "value": "Drive ID" + } + ], "LD3weJ": [ { "type": 0, @@ -4987,6 +4999,12 @@ "value": "Sessie gaat vervallen" } ], + "jpL29S": [ + { + "type": 0, + "value": "Folder path" + } + ], "jtWzSW": [ { "type": 0, @@ -5157,6 +5175,68 @@ "value": "Inclusief de 'huidige datum'" } ], + "lNBZdl": [ + { + "type": 0, + "value": "The path of the folder where folders containing Open-Forms related documents will be created. You can use the expressions " + }, + { + "children": [ + { + "type": 0, + "value": "{{ year }}" + } + ], + "type": 8, + "value": "code" + }, + { + "type": 0, + "value": ", " + }, + { + "children": [ + { + "type": 0, + "value": "{{ month }}" + } + ], + "type": 8, + "value": "code" + }, + { + "type": 0, + "value": " and " + }, + { + "children": [ + { + "type": 0, + "value": "{{ day }}" + } + ], + "type": 8, + "value": "code" + }, + { + "type": 0, + "value": ". The path must start with " + }, + { + "children": [ + { + "type": 0, + "value": "/" + } + ], + "type": 8, + "value": "code" + }, + { + "type": 0, + "value": "." + } + ], "lOSmt+": [ { "type": 0, @@ -6041,6 +6121,12 @@ "value": "Na hoeveel seconden moeten gecachete resultaten vervallen." } ], + "wAQaa5": [ + { + "type": 0, + "value": "Extra print statement" + } + ], "wIaGgb": [ { "type": 0, @@ -6123,6 +6209,12 @@ "value": "Veld waar beschikbare tijden voor een afspraak komen te staan" } ], + "wnTTZr": [ + { + "type": 0, + "value": "ID of the drive to use. If left empty, the default drive will be used." + } + ], "wnxKT/": [ { "type": 0, diff --git a/src/openforms/js/lang/en.json b/src/openforms/js/lang/en.json index d88e1efb15..3d79646422 100644 --- a/src/openforms/js/lang/en.json +++ b/src/openforms/js/lang/en.json @@ -219,6 +219,11 @@ "description": "Objects API variable configuration editor JSON Schema visibility toggle", "originalDefault": "Toggle JSON Schema" }, + "3zaZWN": { + "defaultMessage": "Plugin configuration: demo", + "description": "Demo registration options modal title", + "originalDefault": "Plugin configuration: demo" + }, "4FQxD/": { "defaultMessage": "Configure", "description": "JSON editor: 'configure' header label", @@ -1114,6 +1119,11 @@ "description": "Registration summary error message", "originalDefault": "Something went wrong while rendering the registration options" }, + "LBEeSy": { + "defaultMessage": "Drive ID", + "description": "MS Graph registration options 'driveId' label", + "originalDefault": "Drive ID" + }, "LD3weJ": { "defaultMessage": "Advanced configuration", "description": "Advanced configuration tab title", @@ -2294,6 +2304,11 @@ "description": "Model title session expiry warning", "originalDefault": "Session will expire" }, + "jpL29S": { + "defaultMessage": "Folder path", + "description": "MS Graph registration options 'folderPath' label", + "originalDefault": "Folder path" + }, "jy1jTd": { "defaultMessage": "{numErrors, plural, one {There is a validation error.} other {There are {numErrors} validation errors.} }", "description": "Registration validation errors icon next to button to configure options", @@ -2359,6 +2374,11 @@ "description": "Label for 'value' in MappingArrayInput table column", "originalDefault": "Value" }, + "lNBZdl": { + "defaultMessage": "The path of the folder where folders containing Open-Forms related documents will be created. You can use the expressions '{{' year '}}', '{{' month '}}' and '{{' day '}}'. The path must start with /.", + "description": "MS Graph registration options 'folderPath' help text", + "originalDefault": "The path of the folder where folders containing Open-Forms related documents will be created. You can use the expressions '{{' year '}}', '{{' month '}}' and '{{' day '}}'. The path must start with /." + }, "lcR5L6": { "defaultMessage": "Add item", "description": "Add item to multi-input field", @@ -2779,6 +2799,11 @@ "description": "Help text cache timeout", "originalDefault": "After how many seconds should the cached response expire." }, + "wAQaa5": { + "defaultMessage": "Extra print statement", + "description": "Demo registration options 'extraLine' label", + "originalDefault": "Extra print statement" + }, "wIaGgb": { "defaultMessage": "Errored Submissions Removal Method", "description": "Errored Submissions Removal Method field label", @@ -2804,6 +2829,11 @@ "description": "Times Component field help text", "originalDefault": "Component where times for an appointment will be shown" }, + "wnTTZr": { + "defaultMessage": "ID of the drive to use. If left empty, the default drive will be used.", + "description": "MS Graph registration options 'driveId' help text", + "originalDefault": "ID of the drive to use. If left empty, the default drive will be used." + }, "wnxKT/": { "defaultMessage": "Component where products for an appointment will be shown", "description": "Products Component field help text", diff --git a/src/openforms/js/lang/nl.json b/src/openforms/js/lang/nl.json index 0875de7a11..c2b89ec8f3 100644 --- a/src/openforms/js/lang/nl.json +++ b/src/openforms/js/lang/nl.json @@ -220,6 +220,11 @@ "description": "Objects API variable configuration editor JSON Schema visibility toggle", "originalDefault": "Toggle JSON Schema" }, + "3zaZWN": { + "defaultMessage": "Plugin configuration: demo", + "description": "Demo registration options modal title", + "originalDefault": "Plugin configuration: demo" + }, "4FQxD/": { "defaultMessage": "Configureren", "description": "JSON editor: 'configure' header label", @@ -1121,6 +1126,11 @@ "description": "Registration summary error message", "originalDefault": "Something went wrong while rendering the registration options" }, + "LBEeSy": { + "defaultMessage": "Drive ID", + "description": "MS Graph registration options 'driveId' label", + "originalDefault": "Drive ID" + }, "LD3weJ": { "defaultMessage": "Geavanceerde instellingen", "description": "Advanced configuration tab title", @@ -2312,6 +2322,11 @@ "description": "Model title session expiry warning", "originalDefault": "Session will expire" }, + "jpL29S": { + "defaultMessage": "Folder path", + "description": "MS Graph registration options 'folderPath' label", + "originalDefault": "Folder path" + }, "jy1jTd": { "defaultMessage": "{numErrors, plural, one {There is a validation error.} other {There are {numErrors} validation errors.} }", "description": "Registration validation errors icon next to button to configure options", @@ -2377,6 +2392,11 @@ "description": "Label for 'value' in MappingArrayInput table column", "originalDefault": "Value" }, + "lNBZdl": { + "defaultMessage": "The path of the folder where folders containing Open-Forms related documents will be created. You can use the expressions '{{' year '}}', '{{' month '}}' and '{{' day '}}'. The path must start with /.", + "description": "MS Graph registration options 'folderPath' help text", + "originalDefault": "The path of the folder where folders containing Open-Forms related documents will be created. You can use the expressions '{{' year '}}', '{{' month '}}' and '{{' day '}}'. The path must start with /." + }, "lcR5L6": { "defaultMessage": "Item toevoegen", "description": "Add item to multi-input field", @@ -2797,6 +2817,11 @@ "description": "Help text cache timeout", "originalDefault": "After how many seconds should the cached response expire." }, + "wAQaa5": { + "defaultMessage": "Extra print statement", + "description": "Demo registration options 'extraLine' label", + "originalDefault": "Extra print statement" + }, "wIaGgb": { "defaultMessage": "Opschoonmethode voor niet voltooide inzendingen", "description": "Errored Submissions Removal Method field label", @@ -2822,6 +2847,11 @@ "description": "Times Component field help text", "originalDefault": "Component where times for an appointment will be shown" }, + "wnTTZr": { + "defaultMessage": "ID of the drive to use. If left empty, the default drive will be used.", + "description": "MS Graph registration options 'driveId' help text", + "originalDefault": "ID of the drive to use. If left empty, the default drive will be used." + }, "wnxKT/": { "defaultMessage": "Veld waar beschikbare producten voor een afspraak komen te staan", "description": "Products Component field help text", From 04a83adca250431ae1b5f5e4602285e712353b26 Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Thu, 31 Oct 2024 21:44:46 +0100 Subject: [PATCH 04/10] :boom: Dropped support for RJSF in registration backend configuration We used React JsonSchema Form to render a minimal form layout given the serializer definition from the backend. This is now no longer supported - you must explicitly define a configuration form (we recommend using Formik for this) to use/render for a better UX and more consistent UI appearance. --- .../admin/form_design/RegistrationFields.js | 14 +++++++++----- .../admin/form_design/registrations/index.js | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/openforms/js/components/admin/form_design/RegistrationFields.js b/src/openforms/js/components/admin/form_design/RegistrationFields.js index 6e929dbaf9..d2aa9bcde5 100644 --- a/src/openforms/js/components/admin/form_design/RegistrationFields.js +++ b/src/openforms/js/components/admin/form_design/RegistrationFields.js @@ -2,7 +2,6 @@ import PropTypes from 'prop-types'; import React from 'react'; import {FormattedMessage, useIntl} from 'react-intl'; -import FormRjsfWrapper from 'components/admin/RJSFWrapper'; import ButtonContainer from 'components/admin/forms/ButtonContainer'; import Field from 'components/admin/forms/Field'; import Fieldset from 'components/admin/forms/Fieldset'; @@ -30,13 +29,18 @@ const backendKeyGenerator = (function* () { const BackendOptionsFormRow = ({backendType = null, currentOptions = {}, onChange, index}) => { if (!backendType) return null; + // if there's no configuration form, there's nothing to do + if (!Object.keys(backendType.schema.properties).length) return null; - const hasOptionsForm = Boolean(backendType && Object.keys(backendType.schema.properties).length); - // either use the custom backend-specific defined form, or fall back to the generic react-json-schema-form - const OptionsFormComponent = BACKEND_OPTIONS_FORMS[backendType.id]?.form ?? FormRjsfWrapper; - if (!hasOptionsForm && !BACKEND_OPTIONS_FORMS[backendType.id]) { + // Look up the configuration form from the registry + const OptionsFormComponent = BACKEND_OPTIONS_FORMS[backendType.id]?.form; + if (!OptionsFormComponent) { + console.debug( + `No configuration form known in the registry for plugin with ID '${backendType.id}'.` + ); return null; } + return ( Object | null, * onUserDefinedVariableEdit?: (...args: any) => Object | null, From 3d7043971c36c55864955c6dcdddaf3a7749d31c Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Thu, 31 Oct 2024 22:27:29 +0100 Subject: [PATCH 05/10] :boom: Drop RJSF support for payment provider configuration Similar to the registration backends, payment provider configuration forms must now be defined explicitly. --- .../admin/form_design/PaymentFields.js | 22 ++-- .../form_design/PaymentFields.stories.js | 57 +++++++++ .../payments/OptionsConfiguration.js | 113 ++++++++++++++++++ .../admin/form_design/payments/index.js | 6 + .../ogone_legacy/OgoneLegacyOptionsForm.js | 70 +++++++++++ .../payments/ogone_legacy/fields.js | 37 ++++++ .../payments/ogone_legacy/index.js | 3 + 7 files changed, 295 insertions(+), 13 deletions(-) create mode 100644 src/openforms/js/components/admin/form_design/PaymentFields.stories.js create mode 100644 src/openforms/js/components/admin/form_design/payments/OptionsConfiguration.js create mode 100644 src/openforms/js/components/admin/form_design/payments/index.js create mode 100644 src/openforms/js/components/admin/form_design/payments/ogone_legacy/OgoneLegacyOptionsForm.js create mode 100644 src/openforms/js/components/admin/form_design/payments/ogone_legacy/fields.js create mode 100644 src/openforms/js/components/admin/form_design/payments/ogone_legacy/index.js diff --git a/src/openforms/js/components/admin/form_design/PaymentFields.js b/src/openforms/js/components/admin/form_design/PaymentFields.js index 8e9edf9774..feafc9aae2 100644 --- a/src/openforms/js/components/admin/form_design/PaymentFields.js +++ b/src/openforms/js/components/admin/form_design/PaymentFields.js @@ -2,17 +2,20 @@ import PropTypes from 'prop-types'; import React from 'react'; import {FormattedMessage} from 'react-intl'; -import FormRjsfWrapper from 'components/admin/RJSFWrapper'; import Field from 'components/admin/forms/Field'; import Fieldset from 'components/admin/forms/Fieldset'; import FormRow from 'components/admin/forms/FormRow'; import Select from 'components/admin/forms/Select'; +import {PAYMENT_OPTIONS_FORMS} from './payments'; + const PaymentFields = ({backends = [], selectedBackend = '', backendOptions = {}, onChange}) => { const backendChoices = backends.map(backend => [backend.id, backend.label]); const backend = backends.find(backend => backend.id === selectedBackend); const hasOptionsForm = Boolean(backend && Object.keys(backend.schema.properties).length); + const OptionsFormComponent = backend ? PAYMENT_OPTIONS_FORMS[backend.id] : null; + return (
- {hasOptionsForm ? ( + {hasOptionsForm && ( - - } + - onChange({target: {name: 'form.paymentBackendOptions', value: formData}}) + onSubmit={values => + onChange({target: {name: 'form.paymentBackendOptions', value: values}}) } /> - ) : null} + )}
); }; diff --git a/src/openforms/js/components/admin/form_design/PaymentFields.stories.js b/src/openforms/js/components/admin/form_design/PaymentFields.stories.js new file mode 100644 index 0000000000..1a3965b6ce --- /dev/null +++ b/src/openforms/js/components/admin/form_design/PaymentFields.stories.js @@ -0,0 +1,57 @@ +import {fn} from '@storybook/test'; + +import { + FormDecorator, + ValidationErrorsDecorator, +} from 'components/admin/form_design/story-decorators'; + +import PaymentFields from './PaymentFields'; + +export default { + title: 'Form design / Payments / PaymentFields', + decorators: [ValidationErrorsDecorator, FormDecorator], + component: PaymentFields, + args: { + backends: [ + {id: 'demo', label: 'Demo', schema: {type: 'object', properties: {}}}, + { + id: 'ogone-legacy', + label: 'Ogone legacy', + schema: { + type: 'object', + properties: { + merchantId: { + type: 'integer', + enum: [1, 2], + enumNames: ['Merchant 1', 'Merchant 2'], + title: 'Merchant id', + description: 'Merchant to use', + }, + }, + required: ['merchantId'], + }, + }, + ], + selectedBackend: '', + backendOptions: {}, + onChange: fn(), + }, +}; + +export const NothingSelected = {}; + +export const Demo = { + args: { + selectedBackend: 'demo', + }, +}; + +export const OgoneLegacy = { + name: 'Ogone (legacy)', + args: { + selectedBackend: 'ogone-legacy', + backendOptions: { + merchantId: 2, + }, + }, +}; diff --git a/src/openforms/js/components/admin/form_design/payments/OptionsConfiguration.js b/src/openforms/js/components/admin/form_design/payments/OptionsConfiguration.js new file mode 100644 index 0000000000..1d38f5a98f --- /dev/null +++ b/src/openforms/js/components/admin/form_design/payments/OptionsConfiguration.js @@ -0,0 +1,113 @@ +import {Formik} from 'formik'; +import PropTypes from 'prop-types'; +import {useState} from 'react'; +import {FormattedMessage, useIntl} from 'react-intl'; + +import {SubmitAction} from 'components/admin/forms/ActionButton'; +import Field from 'components/admin/forms/Field'; +import SubmitRow from 'components/admin/forms/SubmitRow'; +import {ErrorIcon} from 'components/admin/icons'; +import {FormModal} from 'components/admin/modals'; + +// TODO: see if we can merge this with the registration options configuration component? +const OptionsConfiguration = ({ + numErrors, + modalTitle, + initialFormData, + onSubmit, + modalSize = '', + children, +}) => { + const intl = useIntl(); + const [modalOpen, setModalOpen] = useState(false); + return ( + + } + > + <> + + + {numErrors > 0 && ( + + )} + + + setModalOpen(false)} + extraModifiers={modalSize ? [modalSize] : undefined} + > + { + onSubmit(values); + actions.setSubmitting(false); + setModalOpen(false); + }} + > + {({handleSubmit}) => ( + <> + {children} + + + { + event.preventDefault(); + handleSubmit(event); + }} + /> + + + )} + + + + + ); +}; + +OptionsConfiguration.propTypes = { + modalTitle: PropTypes.node.isRequired, + numErrors: PropTypes.number.isRequired, + initialFormData: PropTypes.object.isRequired, + onSubmit: PropTypes.func.isRequired, +}; + +export default OptionsConfiguration; diff --git a/src/openforms/js/components/admin/form_design/payments/index.js b/src/openforms/js/components/admin/form_design/payments/index.js new file mode 100644 index 0000000000..e972084244 --- /dev/null +++ b/src/openforms/js/components/admin/form_design/payments/index.js @@ -0,0 +1,6 @@ +import OgoneLegacyOptionsForm from './ogone_legacy'; + +export const PAYMENT_OPTIONS_FORMS = { + demo: null, + 'ogone-legacy': OgoneLegacyOptionsForm, +}; diff --git a/src/openforms/js/components/admin/form_design/payments/ogone_legacy/OgoneLegacyOptionsForm.js b/src/openforms/js/components/admin/form_design/payments/ogone_legacy/OgoneLegacyOptionsForm.js new file mode 100644 index 0000000000..a0fab535f8 --- /dev/null +++ b/src/openforms/js/components/admin/form_design/payments/ogone_legacy/OgoneLegacyOptionsForm.js @@ -0,0 +1,70 @@ +import PropTypes from 'prop-types'; +import {useContext} from 'react'; +import {FormattedMessage} from 'react-intl'; + +// TODO: move into more generic location +import { + filterErrors, + getChoicesFromSchema, +} from 'components/admin/form_design/registrations/shared/utils'; +import Fieldset from 'components/admin/forms/Fieldset'; +import { + ValidationErrorContext, + ValidationErrorsProvider, +} from 'components/admin/forms/ValidationErrors'; + +import OptionsConfiguration from '../OptionsConfiguration'; +import {MerchantID} from './fields'; + +const OgoneLegacyOptionsForm = ({schema, formData, onSubmit}) => { + const validationErrors = useContext(ValidationErrorContext); + + const merchantChoices = getChoicesFromSchema( + schema.properties.merchantId.enum, + schema.properties.merchantId.enumNames + ).map(([value, label]) => ({value, label})); + + const relevantErrors = filterErrors('form.paymentBackendOptions', validationErrors); + + return ( + + } + initialFormData={{ + merchantId: null, + ...formData, + }} + onSubmit={onSubmit} + > + +
+ +
+
+
+ ); +}; + +OgoneLegacyOptionsForm.propTypes = { + onSubmit: PropTypes.func.isRequired, + schema: PropTypes.shape({ + type: PropTypes.oneOf(['object']), // it's the JSON schema root, it has to be + properties: PropTypes.shape({ + merchantId: { + enum: PropTypes.arrayOf(PropTypes.number), + enumNames: PropTypes.arrayOf(PropTypes.string), + }, + }), + required: PropTypes.arrayOf(PropTypes.string), + }).isRequired, + formData: PropTypes.shape({ + merchantId: PropTypes.number, + }), +}; + +export default OgoneLegacyOptionsForm; diff --git a/src/openforms/js/components/admin/form_design/payments/ogone_legacy/fields.js b/src/openforms/js/components/admin/form_design/payments/ogone_legacy/fields.js new file mode 100644 index 0000000000..863821ee05 --- /dev/null +++ b/src/openforms/js/components/admin/form_design/payments/ogone_legacy/fields.js @@ -0,0 +1,37 @@ +import PropTypes from 'prop-types'; +import {FormattedMessage} from 'react-intl'; + +import Field from 'components/admin/forms/Field'; +import FormRow from 'components/admin/forms/FormRow'; +import ReactSelect from 'components/admin/forms/ReactSelect'; + +export const MerchantID = ({options}) => ( + + + } + helpText={ + + } + > + + + +); + +MerchantID.propTypes = { + options: PropTypes.arrayOf( + PropTypes.shape({ + value: PropTypes.bool, + label: PropTypes.node.isRequired, + }) + ).isRequired, +}; diff --git a/src/openforms/js/components/admin/form_design/payments/ogone_legacy/index.js b/src/openforms/js/components/admin/form_design/payments/ogone_legacy/index.js new file mode 100644 index 0000000000..99d97e30a0 --- /dev/null +++ b/src/openforms/js/components/admin/form_design/payments/ogone_legacy/index.js @@ -0,0 +1,3 @@ +import OgoneLegacyOptionsForm from './OgoneLegacyOptionsForm'; + +export default OgoneLegacyOptionsForm; From da124e8110afffa1114ce67e8ffe08a206e0abf4 Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Thu, 31 Oct 2024 22:27:57 +0100 Subject: [PATCH 06/10] :globe_with_meridians: Extract payment provider config form translations --- src/openforms/js/compiled-lang/en.json | 58 ++++++++++++++++++++++++++ src/openforms/js/compiled-lang/nl.json | 58 ++++++++++++++++++++++++++ src/openforms/js/lang/en.json | 25 +++++++++++ src/openforms/js/lang/nl.json | 25 +++++++++++ 4 files changed, 166 insertions(+) diff --git a/src/openforms/js/compiled-lang/en.json b/src/openforms/js/compiled-lang/en.json index 007a53ec0d..0ae70acf73 100644 --- a/src/openforms/js/compiled-lang/en.json +++ b/src/openforms/js/compiled-lang/en.json @@ -1265,6 +1265,12 @@ "value": "'Remove row' text" } ], + "B/A1Bk": [ + { + "type": 0, + "value": "Which merchant should be used for payments related to this form." + } + ], "B1ONuu": [ { "type": 0, @@ -1841,6 +1847,12 @@ "value": "Base" } ], + "F2VuCs": [ + { + "type": 0, + "value": "Merchant ID" + } + ], "F9ew4C": [ { "type": 0, @@ -4089,6 +4101,12 @@ "value": "Incomplete Submissions Removal Limit" } ], + "cEALcN": [ + { + "type": 0, + "value": "Configure options" + } + ], "cID9dz": [ { "type": 0, @@ -5815,6 +5833,40 @@ "value": "Enable to attach file uploads to the registration email. If set, this overrides the global default. Form designers should take special care to ensure that the total file upload sizes do not exceed the email size limit." } ], + "sQpH2P": [ + { + "offset": 0, + "options": { + "one": { + "value": [ + { + "type": 0, + "value": "There is a validation error." + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "There are " + }, + { + "type": 1, + "value": "numErrors" + }, + { + "type": 0, + "value": " validation errors." + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "numErrors" + } + ], "sR9GVQ": [ { "type": 0, @@ -6467,6 +6519,12 @@ "value": "hasDescription" } ], + "zYu3XI": [ + { + "type": 0, + "value": "Plugin configuration: Ogone legacy" + } + ], "zeXZzX": [ { "type": 0, diff --git a/src/openforms/js/compiled-lang/nl.json b/src/openforms/js/compiled-lang/nl.json index fa2a66b5e4..b0baaa38ea 100644 --- a/src/openforms/js/compiled-lang/nl.json +++ b/src/openforms/js/compiled-lang/nl.json @@ -1269,6 +1269,12 @@ "value": "'Groep verwijderen'-tekst" } ], + "B/A1Bk": [ + { + "type": 0, + "value": "Which merchant should be used for payments related to this form." + } + ], "B1ONuu": [ { "type": 0, @@ -1862,6 +1868,12 @@ "value": "Base" } ], + "F2VuCs": [ + { + "type": 0, + "value": "Merchant ID" + } + ], "F9ew4C": [ { "type": 0, @@ -4111,6 +4123,12 @@ "value": "bewaartermijn voor sessies" } ], + "cEALcN": [ + { + "type": 0, + "value": "Configure options" + } + ], "cID9dz": [ { "type": 0, @@ -5837,6 +5855,40 @@ "value": "Enable to attach file uploads to the registration email. If set, this overrides the global default. Form designers should take special care to ensure that the total file upload sizes do not exceed the email size limit." } ], + "sQpH2P": [ + { + "offset": 0, + "options": { + "one": { + "value": [ + { + "type": 0, + "value": "There is a validation error." + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "There are " + }, + { + "type": 1, + "value": "numErrors" + }, + { + "type": 0, + "value": " validation errors." + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "numErrors" + } + ], "sR9GVQ": [ { "type": 0, @@ -6489,6 +6541,12 @@ "value": "hasDescription" } ], + "zYu3XI": [ + { + "type": 0, + "value": "Plugin configuration: Ogone legacy" + } + ], "zeXZzX": [ { "type": 0, diff --git a/src/openforms/js/lang/en.json b/src/openforms/js/lang/en.json index 3d79646422..ca43343f8f 100644 --- a/src/openforms/js/lang/en.json +++ b/src/openforms/js/lang/en.json @@ -544,6 +544,11 @@ "description": "Form definition select confirm button", "originalDefault": "Confirm" }, + "B/A1Bk": { + "defaultMessage": "Which merchant should be used for payments related to this form.", + "description": "Ogone legacy payment options 'merchantId' help text", + "originalDefault": "Which merchant should be used for payments related to this form." + }, "B5RSyi": { "defaultMessage": "Email payment subject", "description": "Email registration options 'emailPaymentSubject' label", @@ -769,6 +774,11 @@ "description": "StUF-ZDS registration backend options, 'base' tab label", "originalDefault": "Base" }, + "F2VuCs": { + "defaultMessage": "Merchant ID", + "description": "Ogone legacy payment options 'merchantId' label", + "originalDefault": "Merchant ID" + }, "F9ew4C": { "defaultMessage": "Version", "description": "Objects API registration options 'objecttypeVersion' label", @@ -1889,6 +1899,11 @@ "description": "Incomplete Submissions Removal Limit field label", "originalDefault": "Incomplete Submissions Removal Limit" }, + "cEALcN": { + "defaultMessage": "Configure options", + "description": "Link label to open payment provider options modal", + "originalDefault": "Configure options" + }, "cID9dz": { "defaultMessage": "(complex value)", "description": "JSON editor: complex value representation", @@ -2669,6 +2684,11 @@ "description": "Email registration options 'attachFilesToEmail' helpText", "originalDefault": "Enable to attach file uploads to the registration email. If set, this overrides the global default. Form designers should take special care to ensure that the total file upload sizes do not exceed the email size limit." }, + "sQpH2P": { + "defaultMessage": "{numErrors, plural, one {There is a validation error.} other {There are {numErrors} validation errors.} }", + "description": "Payment provider validation errors icon next to button to configure options", + "originalDefault": "{numErrors, plural, one {There is a validation error.} other {There are {numErrors} validation errors.} }" + }, "sptpzv": { "defaultMessage": "Switching to the new registration options will remove the existing JSON templates. You will also not be able to save the form until the variables are correctly mapped. Are you sure you want to continue?", "description": "Objects API registration backend: v2 switch warning message", @@ -2964,6 +2984,11 @@ "description": "JSON variable type \"object\" representation", "originalDefault": "Object" }, + "zYu3XI": { + "defaultMessage": "Plugin configuration: Ogone legacy", + "description": "Ogone legacy options modal title", + "originalDefault": "Plugin configuration: Ogone legacy" + }, "zeXZzX": { "defaultMessage": "Use logic rules to determine the price", "description": "dynamic pricing mode label", diff --git a/src/openforms/js/lang/nl.json b/src/openforms/js/lang/nl.json index c2b89ec8f3..8da64ec41f 100644 --- a/src/openforms/js/lang/nl.json +++ b/src/openforms/js/lang/nl.json @@ -548,6 +548,11 @@ "description": "Form definition select confirm button", "originalDefault": "Confirm" }, + "B/A1Bk": { + "defaultMessage": "Which merchant should be used for payments related to this form.", + "description": "Ogone legacy payment options 'merchantId' help text", + "originalDefault": "Which merchant should be used for payments related to this form." + }, "B5RSyi": { "defaultMessage": "Email payment subject", "description": "Email registration options 'emailPaymentSubject' label", @@ -776,6 +781,11 @@ "description": "StUF-ZDS registration backend options, 'base' tab label", "originalDefault": "Base" }, + "F2VuCs": { + "defaultMessage": "Merchant ID", + "description": "Ogone legacy payment options 'merchantId' label", + "originalDefault": "Merchant ID" + }, "F9ew4C": { "defaultMessage": "Versie", "description": "Objects API registration options 'objecttypeVersion' label", @@ -1905,6 +1915,11 @@ "description": "Incomplete Submissions Removal Limit field label", "originalDefault": "Incomplete Submissions Removal Limit" }, + "cEALcN": { + "defaultMessage": "Configure options", + "description": "Link label to open payment provider options modal", + "originalDefault": "Configure options" + }, "cID9dz": { "defaultMessage": "(complexe waarde)", "description": "JSON editor: complex value representation", @@ -2687,6 +2702,11 @@ "description": "Email registration options 'attachFilesToEmail' helpText", "originalDefault": "Enable to attach file uploads to the registration email. If set, this overrides the global default. Form designers should take special care to ensure that the total file upload sizes do not exceed the email size limit." }, + "sQpH2P": { + "defaultMessage": "{numErrors, plural, one {There is a validation error.} other {There are {numErrors} validation errors.} }", + "description": "Payment provider validation errors icon next to button to configure options", + "originalDefault": "{numErrors, plural, one {There is a validation error.} other {There are {numErrors} validation errors.} }" + }, "sptpzv": { "defaultMessage": "Let op! Migreren naar het nieuwe configuratieformaat maakt de bestaande JSON-sjablonen leeg. Daarnaast kan je het formulier pas opslaan als alle verplichte variabelen goed gekoppeld zijn. Ben je zeker dat je wil migreren?", "description": "Objects API registration backend: v2 switch warning message", @@ -2983,6 +3003,11 @@ "description": "JSON variable type \"object\" representation", "originalDefault": "Object" }, + "zYu3XI": { + "defaultMessage": "Plugin configuration: Ogone legacy", + "description": "Ogone legacy options modal title", + "originalDefault": "Plugin configuration: Ogone legacy" + }, "zeXZzX": { "defaultMessage": "Gebruik prijsregels om de prijs te bepalen", "description": "dynamic pricing mode label", From 14a5bb24c7be3bee42f6082ac9ee769b0eafc3a9 Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Thu, 31 Oct 2024 22:30:34 +0100 Subject: [PATCH 07/10] :coffin: Remove dead code for RJSF wrapper We only have the Camunda options form making use of the wrapper left as something to clean up, so we cannot drop the dependency entirely yet, but almost there! --- .../js/components/admin/RJSFWrapper.js | 140 +----------------- 1 file changed, 1 insertion(+), 139 deletions(-) diff --git a/src/openforms/js/components/admin/RJSFWrapper.js b/src/openforms/js/components/admin/RJSFWrapper.js index 39ff3f8c21..4ce66d021a 100644 --- a/src/openforms/js/components/admin/RJSFWrapper.js +++ b/src/openforms/js/components/admin/RJSFWrapper.js @@ -1,11 +1,6 @@ -import Form from '@rjsf/core'; -import Widgets from '@rjsf/core/lib/components/widgets'; -import {isSelect, optionsList} from '@rjsf/core/lib/utils'; -import isEmpty from 'lodash/isEmpty'; +import {isSelect} from '@rjsf/core/lib/utils'; import PropTypes from 'prop-types'; -import React from 'react'; -import Field from './forms/Field'; import {FAIcon} from './icons'; /* @@ -70,137 +65,4 @@ CustomFieldTemplate.propTypes = { schema: PropTypes.object.isRequired, }; -/* -Adapted from: -https://github.com/rjsf-team/react-jsonschema-form/blob/master/packages/core/src/components/widgets/CheckboxWidget.js#L5 - */ -const CustomCheckboxWidget = ({ - schema, - id, - value, - disabled, - readonly, - label, - autofocus, - onChange, - ...props -}) => { - // if it's nullable, defer to a dropdown - if (isSelect(schema)) { - const enumOptions = optionsList(schema); - for (let option of enumOptions) { - option.value = JSON.stringify(option.value); - } - const stringValue = typeof value != 'string' ? JSON.stringify(value) : value; - const _onChange = value => { - onChange(JSON.parse(value)); - }; - return ( - - ); - } - - // The CustomCheckboxWidget is rendered as a children of the CustomFieldTemplate, which makes styling a bit trickier - return ( -
- - onChange(event.target.checked)} - /> -
- ); -}; - -CustomCheckboxWidget.propTypes = { - schema: PropTypes.object.isRequired, - id: PropTypes.string.isRequired, - value: PropTypes.any, - required: PropTypes.bool, - disabled: PropTypes.bool, - readonly: PropTypes.bool, - autofocus: PropTypes.bool, - multiple: PropTypes.bool, - onChange: PropTypes.func, -}; - -const FormRjsfWrapper = ({name, label, schema, uiSchema, formData, onChange, errors}) => { - let extraErrors = {}; - - /* - add backend validation errors in the correct format. RJSF takes nested objects, - even for array types, for example: - - const extraErrors = { - 'toEmails': { - 0: {__errors: ['error 1']}, - }, - }; - - */ - for (const [key, msg] of errors) { - if (key.includes('nonFieldErrors')) continue; - const bits = key.split('.'); - // create the nested structure. we can't use lodash, since it creates arrays for - // indices rather than nested objects. - let errObj = extraErrors; - for (const pathBit of bits) { - if (pathBit === '__proto__' || pathBit === 'prototype') - throw new Error('Prototype polution!'); - if (!errObj[pathBit]) errObj[pathBit] = {}; - errObj = errObj[pathBit]; - } - if (!errObj.__errors) errObj.__errors = []; - errObj.__errors.push(msg); - } - - return ( - -
- - ); -}; - -FormRjsfWrapper.propTypes = { - name: PropTypes.string.isRequired, - label: PropTypes.node.isRequired, - schema: PropTypes.shape({ - type: PropTypes.oneOf(['object']), // it's the JSON schema root, it has to be - properties: PropTypes.object, - required: PropTypes.arrayOf(PropTypes.string), - }), - uiSchema: PropTypes.object, - formData: PropTypes.object, - onChange: PropTypes.func.isRequired, - errors: PropTypes.array, -}; - export {CustomFieldTemplate}; -export default FormRjsfWrapper; From 0a2569a06ff483623fc7dbf5f1a946ec67e818bd Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Thu, 31 Oct 2024 22:44:51 +0100 Subject: [PATCH 08/10] :recycle: Refactor shared utilities to real shared general purpose location They're not limited to usage in registration backends, but also useful for the payment plugin configuration, and, at some point, probably also for prefill. --- .../ogone_legacy/OgoneLegacyOptionsForm.js | 7 ++----- .../registrations/demo/DemoOptionsForm.js | 2 +- .../registrations/email/EmailOptionsForm.js | 3 +-- .../email/EmailOptionsFormFields.js | 6 ++---- .../registrations/ms_graph/MSGraphOptionsForm.js | 2 +- .../objectsapi/ObjectsApiOptionsForm.js | 7 ++----- .../objectsapi/ObjectsApiOptionsFormFields.js | 2 +- .../form_design/registrations/shared/utils.js | 16 ---------------- .../registrations/stufzds/StufZDSOptionsForm.js | 3 +-- .../stufzds/StufZDSOptionsFormFields.js | 6 ++---- .../zgw/ManageVariableToPropertyMappings.js | 3 +-- .../registrations/zgw/ZGWOptionsForm.js | 7 ++----- .../registrations/zgw/ZGWOptionsFormFields.js | 2 +- .../components/admin/forms/ValidationErrors.js | 8 +++++++- src/openforms/js/utils/json-schema.js | 8 ++++++++ 15 files changed, 32 insertions(+), 50 deletions(-) delete mode 100644 src/openforms/js/components/admin/form_design/registrations/shared/utils.js create mode 100644 src/openforms/js/utils/json-schema.js diff --git a/src/openforms/js/components/admin/form_design/payments/ogone_legacy/OgoneLegacyOptionsForm.js b/src/openforms/js/components/admin/form_design/payments/ogone_legacy/OgoneLegacyOptionsForm.js index a0fab535f8..318502c71f 100644 --- a/src/openforms/js/components/admin/form_design/payments/ogone_legacy/OgoneLegacyOptionsForm.js +++ b/src/openforms/js/components/admin/form_design/payments/ogone_legacy/OgoneLegacyOptionsForm.js @@ -2,16 +2,13 @@ import PropTypes from 'prop-types'; import {useContext} from 'react'; import {FormattedMessage} from 'react-intl'; -// TODO: move into more generic location -import { - filterErrors, - getChoicesFromSchema, -} from 'components/admin/form_design/registrations/shared/utils'; import Fieldset from 'components/admin/forms/Fieldset'; import { ValidationErrorContext, ValidationErrorsProvider, + filterErrors, } from 'components/admin/forms/ValidationErrors'; +import {getChoicesFromSchema} from 'utils/json-schema'; import OptionsConfiguration from '../OptionsConfiguration'; import {MerchantID} from './fields'; diff --git a/src/openforms/js/components/admin/form_design/registrations/demo/DemoOptionsForm.js b/src/openforms/js/components/admin/form_design/registrations/demo/DemoOptionsForm.js index da983638c2..fa8a3758e9 100644 --- a/src/openforms/js/components/admin/form_design/registrations/demo/DemoOptionsForm.js +++ b/src/openforms/js/components/admin/form_design/registrations/demo/DemoOptionsForm.js @@ -4,7 +4,6 @@ import React, {useContext} from 'react'; import {FormattedMessage} from 'react-intl'; import OptionsConfiguration from 'components/admin/form_design/registrations/shared/OptionsConfiguration'; -import {filterErrors} from 'components/admin/form_design/registrations/shared/utils'; import Field from 'components/admin/forms/Field'; import Fieldset from 'components/admin/forms/Fieldset'; import FormRow from 'components/admin/forms/FormRow'; @@ -12,6 +11,7 @@ import {TextInput} from 'components/admin/forms/Inputs'; import { ValidationErrorContext, ValidationErrorsProvider, + filterErrors, } from 'components/admin/forms/ValidationErrors'; const ExtraLine = () => { diff --git a/src/openforms/js/components/admin/form_design/registrations/email/EmailOptionsForm.js b/src/openforms/js/components/admin/form_design/registrations/email/EmailOptionsForm.js index 5825843572..be80a92b23 100644 --- a/src/openforms/js/components/admin/form_design/registrations/email/EmailOptionsForm.js +++ b/src/openforms/js/components/admin/form_design/registrations/email/EmailOptionsForm.js @@ -3,8 +3,7 @@ import React, {useContext} from 'react'; import {FormattedMessage} from 'react-intl'; import OptionsConfiguration from 'components/admin/form_design/registrations/shared/OptionsConfiguration'; -import {filterErrors} from 'components/admin/form_design/registrations/shared/utils'; -import {ValidationErrorContext} from 'components/admin/forms/ValidationErrors'; +import {ValidationErrorContext, filterErrors} from 'components/admin/forms/ValidationErrors'; import EmailOptionsFormFields from './EmailOptionsFormFields'; diff --git a/src/openforms/js/components/admin/form_design/registrations/email/EmailOptionsFormFields.js b/src/openforms/js/components/admin/form_design/registrations/email/EmailOptionsFormFields.js index 40307acc0e..ae83aadfbd 100644 --- a/src/openforms/js/components/admin/form_design/registrations/email/EmailOptionsFormFields.js +++ b/src/openforms/js/components/admin/form_design/registrations/email/EmailOptionsFormFields.js @@ -2,15 +2,13 @@ import PropTypes from 'prop-types'; import {useContext} from 'react'; import {FormattedMessage} from 'react-intl'; -import { - filterErrors, - getChoicesFromSchema, -} from 'components/admin/form_design/registrations/shared/utils'; import Fieldset from 'components/admin/forms/Fieldset'; import { ValidationErrorContext, ValidationErrorsProvider, + filterErrors, } from 'components/admin/forms/ValidationErrors'; +import {getChoicesFromSchema} from 'utils/json-schema'; import EmailAttachmentFormatsSelect from './fields/EmailAttachmentFormatsSelect'; import EmailContentTemplateHTML from './fields/EmailContentTemplateHTML'; diff --git a/src/openforms/js/components/admin/form_design/registrations/ms_graph/MSGraphOptionsForm.js b/src/openforms/js/components/admin/form_design/registrations/ms_graph/MSGraphOptionsForm.js index 3501139856..f0090ebc5a 100644 --- a/src/openforms/js/components/admin/form_design/registrations/ms_graph/MSGraphOptionsForm.js +++ b/src/openforms/js/components/admin/form_design/registrations/ms_graph/MSGraphOptionsForm.js @@ -3,11 +3,11 @@ import React, {useContext} from 'react'; import {FormattedMessage} from 'react-intl'; import OptionsConfiguration from 'components/admin/form_design/registrations/shared/OptionsConfiguration'; -import {filterErrors} from 'components/admin/form_design/registrations/shared/utils'; import Fieldset from 'components/admin/forms/Fieldset'; import { ValidationErrorContext, ValidationErrorsProvider, + filterErrors, } from 'components/admin/forms/ValidationErrors'; import {DriveID, FolderPath} from './fields'; diff --git a/src/openforms/js/components/admin/form_design/registrations/objectsapi/ObjectsApiOptionsForm.js b/src/openforms/js/components/admin/form_design/registrations/objectsapi/ObjectsApiOptionsForm.js index f8e6f529c8..7970ee312b 100644 --- a/src/openforms/js/components/admin/form_design/registrations/objectsapi/ObjectsApiOptionsForm.js +++ b/src/openforms/js/components/admin/form_design/registrations/objectsapi/ObjectsApiOptionsForm.js @@ -3,11 +3,8 @@ import React, {useContext} from 'react'; import {FormattedMessage} from 'react-intl'; import OptionsConfiguration from 'components/admin/form_design/registrations/shared/OptionsConfiguration'; -import { - filterErrors, - getChoicesFromSchema, -} from 'components/admin/form_design/registrations/shared/utils'; -import {ValidationErrorContext} from 'components/admin/forms/ValidationErrors'; +import {ValidationErrorContext, filterErrors} from 'components/admin/forms/ValidationErrors'; +import {getChoicesFromSchema} from 'utils/json-schema'; import ObjectsApiOptionsFormFields from './ObjectsApiOptionsFormFields'; diff --git a/src/openforms/js/components/admin/form_design/registrations/objectsapi/ObjectsApiOptionsFormFields.js b/src/openforms/js/components/admin/form_design/registrations/objectsapi/ObjectsApiOptionsFormFields.js index 3483a8ae6a..58e45cbceb 100644 --- a/src/openforms/js/components/admin/form_design/registrations/objectsapi/ObjectsApiOptionsFormFields.js +++ b/src/openforms/js/components/admin/form_design/registrations/objectsapi/ObjectsApiOptionsFormFields.js @@ -5,10 +5,10 @@ import {FormattedMessage, useIntl} from 'react-intl'; import {TabList, TabPanel, Tabs} from 'react-tabs'; import Tab from 'components/admin/form_design/Tab'; -import {filterErrors} from 'components/admin/form_design/registrations/shared/utils'; import { ValidationErrorContext, ValidationErrorsProvider, + filterErrors, } from 'components/admin/forms/ValidationErrors'; import LegacyConfigFields from './LegacyConfigFields'; diff --git a/src/openforms/js/components/admin/form_design/registrations/shared/utils.js b/src/openforms/js/components/admin/form_design/registrations/shared/utils.js deleted file mode 100644 index e03ba951f6..0000000000 --- a/src/openforms/js/components/admin/form_design/registrations/shared/utils.js +++ /dev/null @@ -1,16 +0,0 @@ -const getChoicesFromSchema = (enums, enumNames) => { - const finalChoices = []; - Object.keys(enums).forEach(key => { - finalChoices.push([enums[key], enumNames[key]]); - }); - - return finalChoices; -}; - -const filterErrors = (name, errors) => { - return errors - .filter(([key]) => key.startsWith(`${name}.`)) - .map(([key, msg]) => [key.slice(name.length + 1), msg]); -}; - -export {getChoicesFromSchema, filterErrors}; diff --git a/src/openforms/js/components/admin/form_design/registrations/stufzds/StufZDSOptionsForm.js b/src/openforms/js/components/admin/form_design/registrations/stufzds/StufZDSOptionsForm.js index 7cb3f72542..3c55a7a763 100644 --- a/src/openforms/js/components/admin/form_design/registrations/stufzds/StufZDSOptionsForm.js +++ b/src/openforms/js/components/admin/form_design/registrations/stufzds/StufZDSOptionsForm.js @@ -3,8 +3,7 @@ import React, {useContext} from 'react'; import {FormattedMessage} from 'react-intl'; import OptionsConfiguration from 'components/admin/form_design/registrations/shared/OptionsConfiguration'; -import {filterErrors} from 'components/admin/form_design/registrations/shared/utils'; -import {ValidationErrorContext} from 'components/admin/forms/ValidationErrors'; +import {ValidationErrorContext, filterErrors} from 'components/admin/forms/ValidationErrors'; import StufZDSOptionsFormFields from './StufZDSOptionsFormFields'; diff --git a/src/openforms/js/components/admin/form_design/registrations/stufzds/StufZDSOptionsFormFields.js b/src/openforms/js/components/admin/form_design/registrations/stufzds/StufZDSOptionsFormFields.js index 9cd51215b6..2af598758f 100644 --- a/src/openforms/js/components/admin/form_design/registrations/stufzds/StufZDSOptionsFormFields.js +++ b/src/openforms/js/components/admin/form_design/registrations/stufzds/StufZDSOptionsFormFields.js @@ -4,15 +4,13 @@ import {FormattedMessage} from 'react-intl'; import {TabList, TabPanel, Tabs} from 'react-tabs'; import Tab from 'components/admin/form_design/Tab'; -import { - filterErrors, - getChoicesFromSchema, -} from 'components/admin/form_design/registrations/shared/utils'; import Fieldset from 'components/admin/forms/Fieldset'; import { ValidationErrorContext, ValidationErrorsProvider, + filterErrors, } from 'components/admin/forms/ValidationErrors'; +import {getChoicesFromSchema} from 'utils/json-schema'; import CaseTypeCode from './fields/CaseTypeCode'; import CaseTypeDescription from './fields/CaseTypeDescription'; diff --git a/src/openforms/js/components/admin/form_design/registrations/zgw/ManageVariableToPropertyMappings.js b/src/openforms/js/components/admin/form_design/registrations/zgw/ManageVariableToPropertyMappings.js index fed7389d08..3850c44a6d 100644 --- a/src/openforms/js/components/admin/form_design/registrations/zgw/ManageVariableToPropertyMappings.js +++ b/src/openforms/js/components/admin/form_design/registrations/zgw/ManageVariableToPropertyMappings.js @@ -5,13 +5,12 @@ import React, {useContext} from 'react'; import {FormattedMessage, useIntl} from 'react-intl'; import {FormContext} from 'components/admin/form_design/Context'; -import {filterErrors} from 'components/admin/form_design/registrations/shared/utils'; import {getComponentDatatype} from 'components/admin/form_design/variables/utils'; import ButtonContainer from 'components/admin/forms/ButtonContainer'; import ComponentSelection from 'components/admin/forms/ComponentSelection'; import Field from 'components/admin/forms/Field'; import {TextInput} from 'components/admin/forms/Inputs'; -import {ValidationErrorContext} from 'components/admin/forms/ValidationErrors'; +import {ValidationErrorContext, filterErrors} from 'components/admin/forms/ValidationErrors'; import {DeleteIcon} from 'components/admin/icons'; import {ChangelistTableWrapper, HeadColumn, TableRow} from 'components/admin/tables'; diff --git a/src/openforms/js/components/admin/form_design/registrations/zgw/ZGWOptionsForm.js b/src/openforms/js/components/admin/form_design/registrations/zgw/ZGWOptionsForm.js index 1de4338332..9b39119b68 100644 --- a/src/openforms/js/components/admin/form_design/registrations/zgw/ZGWOptionsForm.js +++ b/src/openforms/js/components/admin/form_design/registrations/zgw/ZGWOptionsForm.js @@ -3,11 +3,8 @@ import React, {useContext} from 'react'; import {FormattedMessage} from 'react-intl'; import OptionsConfiguration from 'components/admin/form_design/registrations/shared/OptionsConfiguration'; -import { - filterErrors, - getChoicesFromSchema, -} from 'components/admin/form_design/registrations/shared/utils'; -import {ValidationErrorContext} from 'components/admin/forms/ValidationErrors'; +import {ValidationErrorContext, filterErrors} from 'components/admin/forms/ValidationErrors'; +import {getChoicesFromSchema} from 'utils/json-schema'; import ZGWFormFields from './ZGWOptionsFormFields'; diff --git a/src/openforms/js/components/admin/form_design/registrations/zgw/ZGWOptionsFormFields.js b/src/openforms/js/components/admin/form_design/registrations/zgw/ZGWOptionsFormFields.js index 438e6c616f..69a381bf16 100644 --- a/src/openforms/js/components/admin/form_design/registrations/zgw/ZGWOptionsFormFields.js +++ b/src/openforms/js/components/admin/form_design/registrations/zgw/ZGWOptionsFormFields.js @@ -6,11 +6,11 @@ import {TabList, TabPanel, Tabs} from 'react-tabs'; import Tab from 'components/admin/form_design/Tab'; import {ContentJSON} from 'components/admin/form_design/registrations/objectsapi/LegacyConfigFields'; -import {filterErrors} from 'components/admin/form_design/registrations/shared/utils'; import Fieldset from 'components/admin/forms/Fieldset'; import { ValidationErrorContext, ValidationErrorsProvider, + filterErrors, } from 'components/admin/forms/ValidationErrors'; import BasicOptionsFieldset from './BasicOptionsFieldset'; diff --git a/src/openforms/js/components/admin/forms/ValidationErrors.js b/src/openforms/js/components/admin/forms/ValidationErrors.js index 9ab9d25a0c..1df7e12832 100644 --- a/src/openforms/js/components/admin/forms/ValidationErrors.js +++ b/src/openforms/js/components/admin/forms/ValidationErrors.js @@ -26,5 +26,11 @@ ValidationErrorsProvider.propTypes = { errors: PropTypes.arrayOf(PropTypes.arrayOf(errorArray)), }; -export {ValidationErrorsProvider, ValidationErrorContext}; +const filterErrors = (name, errors) => { + return errors + .filter(([key]) => key.startsWith(`${name}.`)) + .map(([key, msg]) => [key.slice(name.length + 1), msg]); +}; + +export {ValidationErrorsProvider, ValidationErrorContext, filterErrors}; export default ValidationErrorsProvider; diff --git a/src/openforms/js/utils/json-schema.js b/src/openforms/js/utils/json-schema.js new file mode 100644 index 0000000000..016520f505 --- /dev/null +++ b/src/openforms/js/utils/json-schema.js @@ -0,0 +1,8 @@ +export const getChoicesFromSchema = (enums, enumNames) => { + const finalChoices = []; + Object.keys(enums).forEach(key => { + finalChoices.push([enums[key], enumNames[key]]); + }); + + return finalChoices; +}; From f1886c5b2c9c86458f2dc5a8add7db425916f974 Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Fri, 8 Nov 2024 18:03:04 +0100 Subject: [PATCH 09/10] :ok_hand: Applied PR feedback --- .../form_design/RegistrationFields.stories.js | 28 +++++++++++++++++++ .../ms_graph/MSGraphOptionsForm.js | 9 +++--- .../shared/OptionsConfiguration.js | 1 + .../js/components/admin/modals/FormModal.js | 1 + 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js b/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js index 064b777da8..e4487210fc 100644 --- a/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js +++ b/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js @@ -309,6 +309,25 @@ export default { type: 'object', }, }, + { + id: 'microsoft-graph', + label: 'Microsoft Graph (OneDrive/SharePoint)', + schema: { + type: 'object', + properties: { + folderPath: { + type: 'string', + minLength: 1, + title: 'maplocatie', + }, + driveId: { + type: 'string', + minLength: 1, + title: 'drive-ID', + }, + }, + }, + }, ], configuredBackends: [], onChange: fn(), @@ -535,6 +554,15 @@ export const ConfiguredBackends = { backend: 'exception-demo', options: {extraLine: 'Filled out option'}, }, + { + key: 'backend9', + name: 'MS Graph', + backend: 'microsoft-graph', + options: { + folderPath: '/formSubmissions', + driveId: 'myDrive', + }, + }, ], validationErrors: [ ['form.registrationBackends.1.options.zgwApiGroup', 'You sure about this?'], diff --git a/src/openforms/js/components/admin/form_design/registrations/ms_graph/MSGraphOptionsForm.js b/src/openforms/js/components/admin/form_design/registrations/ms_graph/MSGraphOptionsForm.js index f0090ebc5a..18bfb73142 100644 --- a/src/openforms/js/components/admin/form_design/registrations/ms_graph/MSGraphOptionsForm.js +++ b/src/openforms/js/components/admin/form_design/registrations/ms_graph/MSGraphOptionsForm.js @@ -22,11 +22,11 @@ const MSGraphOptionsForm = ({name, label, formData, onChange}) => { numErrors={relevantErrors.length} modalTitle={ } - initialFormData={{extraLine: '', ...formData}} + initialFormData={{folderPath: '', driveId: '', ...formData}} onSubmit={values => onChange({formData: values})} modalSize="" > @@ -44,7 +44,8 @@ MSGraphOptionsForm.propTypes = { name: PropTypes.string.isRequired, label: PropTypes.node.isRequired, formData: PropTypes.shape({ - extraLine: PropTypes.string, + folderPath: PropTypes.string, + driveId: PropTypes.string, }), onChange: PropTypes.func.isRequired, }; diff --git a/src/openforms/js/components/admin/form_design/registrations/shared/OptionsConfiguration.js b/src/openforms/js/components/admin/form_design/registrations/shared/OptionsConfiguration.js index d2b53be39a..a446dc1566 100644 --- a/src/openforms/js/components/admin/form_design/registrations/shared/OptionsConfiguration.js +++ b/src/openforms/js/components/admin/form_design/registrations/shared/OptionsConfiguration.js @@ -103,6 +103,7 @@ OptionsConfiguration.propTypes = { numErrors: PropTypes.number.isRequired, initialFormData: PropTypes.object.isRequired, onSubmit: PropTypes.func.isRequired, + modalSize: PropTypes.oneOf(['', 'small', 'large']), }; export default OptionsConfiguration; diff --git a/src/openforms/js/components/admin/modals/FormModal.js b/src/openforms/js/components/admin/modals/FormModal.js index 09dd1d632d..7634100026 100644 --- a/src/openforms/js/components/admin/modals/FormModal.js +++ b/src/openforms/js/components/admin/modals/FormModal.js @@ -35,6 +35,7 @@ FormModal.propTypes = { closeModal: PropTypes.func.isRequired, onFormSubmit: PropTypes.func, children: PropTypes.node, + extraModifiers: PropTypes.arrayOf(PropTypes.oneOf(['small', 'large'])), }; export default FormModal; From f20a7f9e88096f1068acb729def8ed2ffaeb4739 Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Fri, 8 Nov 2024 18:21:22 +0100 Subject: [PATCH 10/10] :recycle: Merge payment/registration shared options config modal Replaced the duplicated code with a dedicated 'generic' component for both payment/registration plugin configuration option forms. --- .../payments/OptionsConfiguration.js | 111 +++--------------- .../ogone_legacy/OgoneLegacyOptionsForm.js | 4 +- .../payments/ogone_legacy/fields.js | 2 +- .../registrations/demo/DemoOptionsForm.js | 6 +- .../registrations/email/EmailOptionsForm.js | 6 +- .../ms_graph/MSGraphOptionsForm.js | 6 +- .../objectsapi/ObjectsApiOptionsForm.js | 6 +- .../stufzds/StufZDSOptionsForm.js | 6 +- .../registrations/zgw/ZGWOptionsForm.js | 6 +- .../ModalOptionsConfiguration.js} | 14 ++- 10 files changed, 50 insertions(+), 117 deletions(-) rename src/openforms/js/components/admin/{form_design/registrations/shared/OptionsConfiguration.js => forms/ModalOptionsConfiguration.js} (87%) diff --git a/src/openforms/js/components/admin/form_design/payments/OptionsConfiguration.js b/src/openforms/js/components/admin/form_design/payments/OptionsConfiguration.js index 1d38f5a98f..e04f198187 100644 --- a/src/openforms/js/components/admin/form_design/payments/OptionsConfiguration.js +++ b/src/openforms/js/components/admin/form_design/payments/OptionsConfiguration.js @@ -5,103 +5,28 @@ import {FormattedMessage, useIntl} from 'react-intl'; import {SubmitAction} from 'components/admin/forms/ActionButton'; import Field from 'components/admin/forms/Field'; +import ModalOptionsConfiguration from 'components/admin/forms/ModalOptionsConfiguration'; import SubmitRow from 'components/admin/forms/SubmitRow'; import {ErrorIcon} from 'components/admin/icons'; import {FormModal} from 'components/admin/modals'; -// TODO: see if we can merge this with the registration options configuration component? -const OptionsConfiguration = ({ - numErrors, - modalTitle, - initialFormData, - onSubmit, - modalSize = '', - children, -}) => { - const intl = useIntl(); - const [modalOpen, setModalOpen] = useState(false); - return ( - - } - > - <> - - - {numErrors > 0 && ( - - )} - - - setModalOpen(false)} - extraModifiers={modalSize ? [modalSize] : undefined} - > - { - onSubmit(values); - actions.setSubmitting(false); - setModalOpen(false); - }} - > - {({handleSubmit}) => ( - <> - {children} - - - { - event.preventDefault(); - handleSubmit(event); - }} - /> - - - )} - - - - - ); -}; +const OptionsConfiguration = ({numErrors, modalTitle, initialFormData, onSubmit, children}) => ( + + } + numErrors={numErrors} + modalTitle={modalTitle} + initialFormData={initialFormData} + onSubmit={onSubmit} + modalSize="" + children={children} + /> +); OptionsConfiguration.propTypes = { modalTitle: PropTypes.node.isRequired, diff --git a/src/openforms/js/components/admin/form_design/payments/ogone_legacy/OgoneLegacyOptionsForm.js b/src/openforms/js/components/admin/form_design/payments/ogone_legacy/OgoneLegacyOptionsForm.js index 318502c71f..7bc97308d0 100644 --- a/src/openforms/js/components/admin/form_design/payments/ogone_legacy/OgoneLegacyOptionsForm.js +++ b/src/openforms/js/components/admin/form_design/payments/ogone_legacy/OgoneLegacyOptionsForm.js @@ -52,10 +52,10 @@ OgoneLegacyOptionsForm.propTypes = { schema: PropTypes.shape({ type: PropTypes.oneOf(['object']), // it's the JSON schema root, it has to be properties: PropTypes.shape({ - merchantId: { + merchantId: PropTypes.shape({ enum: PropTypes.arrayOf(PropTypes.number), enumNames: PropTypes.arrayOf(PropTypes.string), - }, + }), }), required: PropTypes.arrayOf(PropTypes.string), }).isRequired, diff --git a/src/openforms/js/components/admin/form_design/payments/ogone_legacy/fields.js b/src/openforms/js/components/admin/form_design/payments/ogone_legacy/fields.js index 863821ee05..785817854b 100644 --- a/src/openforms/js/components/admin/form_design/payments/ogone_legacy/fields.js +++ b/src/openforms/js/components/admin/form_design/payments/ogone_legacy/fields.js @@ -30,7 +30,7 @@ export const MerchantID = ({options}) => ( MerchantID.propTypes = { options: PropTypes.arrayOf( PropTypes.shape({ - value: PropTypes.bool, + value: PropTypes.number, label: PropTypes.node.isRequired, }) ).isRequired, diff --git a/src/openforms/js/components/admin/form_design/registrations/demo/DemoOptionsForm.js b/src/openforms/js/components/admin/form_design/registrations/demo/DemoOptionsForm.js index fa8a3758e9..84b51e0400 100644 --- a/src/openforms/js/components/admin/form_design/registrations/demo/DemoOptionsForm.js +++ b/src/openforms/js/components/admin/form_design/registrations/demo/DemoOptionsForm.js @@ -3,11 +3,11 @@ import PropTypes from 'prop-types'; import React, {useContext} from 'react'; import {FormattedMessage} from 'react-intl'; -import OptionsConfiguration from 'components/admin/form_design/registrations/shared/OptionsConfiguration'; import Field from 'components/admin/forms/Field'; import Fieldset from 'components/admin/forms/Fieldset'; import FormRow from 'components/admin/forms/FormRow'; import {TextInput} from 'components/admin/forms/Inputs'; +import ModalOptionsConfiguration from 'components/admin/forms/ModalOptionsConfiguration'; import { ValidationErrorContext, ValidationErrorsProvider, @@ -37,7 +37,7 @@ const DemoOptionsForm = ({name, label, formData, onChange}) => { const validationErrors = useContext(ValidationErrorContext); const relevantErrors = filterErrors(name, validationErrors); return ( - { - + ); }; diff --git a/src/openforms/js/components/admin/form_design/registrations/email/EmailOptionsForm.js b/src/openforms/js/components/admin/form_design/registrations/email/EmailOptionsForm.js index be80a92b23..fb6a8d8608 100644 --- a/src/openforms/js/components/admin/form_design/registrations/email/EmailOptionsForm.js +++ b/src/openforms/js/components/admin/form_design/registrations/email/EmailOptionsForm.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import React, {useContext} from 'react'; import {FormattedMessage} from 'react-intl'; -import OptionsConfiguration from 'components/admin/form_design/registrations/shared/OptionsConfiguration'; +import ModalOptionsConfiguration from 'components/admin/forms/ModalOptionsConfiguration'; import {ValidationErrorContext, filterErrors} from 'components/admin/forms/ValidationErrors'; import EmailOptionsFormFields from './EmailOptionsFormFields'; @@ -12,7 +12,7 @@ const EmailOptionsForm = ({name, label, schema, formData, onChange}) => { const numErrors = filterErrors(name, validationErrors).length; return ( - { onSubmit={values => onChange({formData: values})} > - + ); }; diff --git a/src/openforms/js/components/admin/form_design/registrations/ms_graph/MSGraphOptionsForm.js b/src/openforms/js/components/admin/form_design/registrations/ms_graph/MSGraphOptionsForm.js index 18bfb73142..c500360907 100644 --- a/src/openforms/js/components/admin/form_design/registrations/ms_graph/MSGraphOptionsForm.js +++ b/src/openforms/js/components/admin/form_design/registrations/ms_graph/MSGraphOptionsForm.js @@ -2,8 +2,8 @@ import PropTypes from 'prop-types'; import React, {useContext} from 'react'; import {FormattedMessage} from 'react-intl'; -import OptionsConfiguration from 'components/admin/form_design/registrations/shared/OptionsConfiguration'; import Fieldset from 'components/admin/forms/Fieldset'; +import ModalOptionsConfiguration from 'components/admin/forms/ModalOptionsConfiguration'; import { ValidationErrorContext, ValidationErrorsProvider, @@ -16,7 +16,7 @@ const MSGraphOptionsForm = ({name, label, formData, onChange}) => { const validationErrors = useContext(ValidationErrorContext); const relevantErrors = filterErrors(name, validationErrors); return ( - { - + ); }; diff --git a/src/openforms/js/components/admin/form_design/registrations/objectsapi/ObjectsApiOptionsForm.js b/src/openforms/js/components/admin/form_design/registrations/objectsapi/ObjectsApiOptionsForm.js index 7970ee312b..e68c0508a6 100644 --- a/src/openforms/js/components/admin/form_design/registrations/objectsapi/ObjectsApiOptionsForm.js +++ b/src/openforms/js/components/admin/form_design/registrations/objectsapi/ObjectsApiOptionsForm.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import React, {useContext} from 'react'; import {FormattedMessage} from 'react-intl'; -import OptionsConfiguration from 'components/admin/form_design/registrations/shared/OptionsConfiguration'; +import ModalOptionsConfiguration from 'components/admin/forms/ModalOptionsConfiguration'; import {ValidationErrorContext, filterErrors} from 'components/admin/forms/ValidationErrors'; import {getChoicesFromSchema} from 'utils/json-schema'; @@ -16,7 +16,7 @@ const ObjectsApiOptionsForm = ({index, name, label, schema, formData, onChange}) const defaultGroup = apiGroupChoices.length === 1 ? apiGroupChoices[0][0] : undefined; return ( - onChange({formData: values})} > - + ); }; diff --git a/src/openforms/js/components/admin/form_design/registrations/stufzds/StufZDSOptionsForm.js b/src/openforms/js/components/admin/form_design/registrations/stufzds/StufZDSOptionsForm.js index 3c55a7a763..7e0442a575 100644 --- a/src/openforms/js/components/admin/form_design/registrations/stufzds/StufZDSOptionsForm.js +++ b/src/openforms/js/components/admin/form_design/registrations/stufzds/StufZDSOptionsForm.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import React, {useContext} from 'react'; import {FormattedMessage} from 'react-intl'; -import OptionsConfiguration from 'components/admin/form_design/registrations/shared/OptionsConfiguration'; +import ModalOptionsConfiguration from 'components/admin/forms/ModalOptionsConfiguration'; import {ValidationErrorContext, filterErrors} from 'components/admin/forms/ValidationErrors'; import StufZDSOptionsFormFields from './StufZDSOptionsFormFields'; @@ -33,7 +33,7 @@ const StufZDSOptionsForm = ({name, label, schema, formData, onChange}) => { } return ( - { onSubmit={values => onChange({formData: values})} > - + ); }; diff --git a/src/openforms/js/components/admin/form_design/registrations/zgw/ZGWOptionsForm.js b/src/openforms/js/components/admin/form_design/registrations/zgw/ZGWOptionsForm.js index 9b39119b68..e200b9fcea 100644 --- a/src/openforms/js/components/admin/form_design/registrations/zgw/ZGWOptionsForm.js +++ b/src/openforms/js/components/admin/form_design/registrations/zgw/ZGWOptionsForm.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import React, {useContext} from 'react'; import {FormattedMessage} from 'react-intl'; -import OptionsConfiguration from 'components/admin/form_design/registrations/shared/OptionsConfiguration'; +import ModalOptionsConfiguration from 'components/admin/forms/ModalOptionsConfiguration'; import {ValidationErrorContext, filterErrors} from 'components/admin/forms/ValidationErrors'; import {getChoicesFromSchema} from 'utils/json-schema'; @@ -22,7 +22,7 @@ const ZGWOptionsForm = ({name, label, schema, formData, onChange}) => { const defaultGroup = apiGroupChoices.length === 1 ? apiGroupChoices[0][0] : undefined; return ( - { apiGroupChoices={apiGroupChoices} confidentialityLevelChoices={confidentialityLevelChoices} /> - + ); }; diff --git a/src/openforms/js/components/admin/form_design/registrations/shared/OptionsConfiguration.js b/src/openforms/js/components/admin/forms/ModalOptionsConfiguration.js similarity index 87% rename from src/openforms/js/components/admin/form_design/registrations/shared/OptionsConfiguration.js rename to src/openforms/js/components/admin/forms/ModalOptionsConfiguration.js index a446dc1566..ccb22f553c 100644 --- a/src/openforms/js/components/admin/form_design/registrations/shared/OptionsConfiguration.js +++ b/src/openforms/js/components/admin/forms/ModalOptionsConfiguration.js @@ -9,7 +9,15 @@ import SubmitRow from 'components/admin/forms/SubmitRow'; import {ErrorIcon} from 'components/admin/icons'; import {FormModal} from 'components/admin/modals'; -const OptionsConfiguration = ({ +/** + * A generic container/wrapper for configuration options that display in a modal, + * after clicking the button to configure the options. Next to the button the number + * of validation errors is displayed. + * + * This relies on form state being managed with Formik. Pass the actual configuration + * fields as children. + */ +const ModalOptionsConfiguration = ({ name, label, numErrors, @@ -96,7 +104,7 @@ const OptionsConfiguration = ({ ); }; -OptionsConfiguration.propTypes = { +ModalOptionsConfiguration.propTypes = { name: PropTypes.string.isRequired, label: PropTypes.node.isRequired, modalTitle: PropTypes.node.isRequired, @@ -106,4 +114,4 @@ OptionsConfiguration.propTypes = { modalSize: PropTypes.oneOf(['', 'small', 'large']), }; -export default OptionsConfiguration; +export default ModalOptionsConfiguration;