diff --git a/src/openforms/js/components/admin/form_design/variables/prefill/PrefillConfigurationForm.js b/src/openforms/js/components/admin/form_design/variables/prefill/PrefillConfigurationForm.js index 3a25d33491..8421b86a19 100644 --- a/src/openforms/js/components/admin/form_design/variables/prefill/PrefillConfigurationForm.js +++ b/src/openforms/js/components/admin/form_design/variables/prefill/PrefillConfigurationForm.js @@ -8,6 +8,7 @@ import Field from 'components/admin/forms/Field'; import Fieldset from 'components/admin/forms/Fieldset'; import FormRow from 'components/admin/forms/FormRow'; import SubmitRow from 'components/admin/forms/SubmitRow'; +import {ValidationErrorsProvider} from 'components/admin/forms/ValidationErrors'; import PluginField from './PluginField'; import PLUGIN_COMPONENT_MAPPING from './constants'; @@ -17,11 +18,12 @@ const PrefillConfigurationForm = ({ plugin = '', attribute = '', identifierRole = 'main', - // TODO: find a better way to specify this based on the selected plugin + // Plugins are responsible for setting up the default values, since we don't know the + // plugin-specific shape here. options = {}, errors, -}) => { - return ( +}) => ( + } - errors={errors.plugin} > <> @@ -76,7 +77,6 @@ const PrefillConfigurationForm = ({ - ); -}; + +); PrefillConfigurationForm.propTypes = { onSubmit: PropTypes.func.isRequired, diff --git a/src/openforms/js/components/admin/form_design/variables/prefill/default/DefaultFields.js b/src/openforms/js/components/admin/form_design/variables/prefill/default/DefaultFields.js index 0d1c80d5dc..4efaf73818 100644 --- a/src/openforms/js/components/admin/form_design/variables/prefill/default/DefaultFields.js +++ b/src/openforms/js/components/admin/form_design/variables/prefill/default/DefaultFields.js @@ -29,7 +29,7 @@ const getAttributes = async plugin => { * Default (legacy) prefill configuration - after selecting the plugin, the user * selects which attribute to use to grab the prefill value from. */ -const DefaultFields = ({errors}) => { +const DefaultFields = () => { const { values: {plugin = ''}, } = useFormikContext(); @@ -60,7 +60,6 @@ const DefaultFields = ({errors}) => { defaultMessage="Attribute" /> } - errors={errors.attribute} > @@ -75,7 +74,6 @@ const DefaultFields = ({errors}) => { defaultMessage="Identifier role" /> } - errors={errors.identifierRole} > @@ -84,12 +82,6 @@ const DefaultFields = ({errors}) => { ); }; -DefaultFields.propTypes = { - errors: PropTypes.shape({ - plugin: ErrorsType, - attribute: ErrorsType, - identifierRole: ErrorsType, - }).isRequired, -}; +DefaultFields.propTypes = {}; export default DefaultFields; diff --git a/src/openforms/js/components/admin/form_design/variables/prefill/objects_api/ObjectsAPIFields.js b/src/openforms/js/components/admin/form_design/variables/prefill/objects_api/ObjectsAPIFields.js index 09cdf150a4..141060eb59 100644 --- a/src/openforms/js/components/admin/form_design/variables/prefill/objects_api/ObjectsAPIFields.js +++ b/src/openforms/js/components/admin/form_design/variables/prefill/objects_api/ObjectsAPIFields.js @@ -11,10 +11,10 @@ import useAsync from 'react-use/esm/useAsync'; import {FormContext} from 'components/admin/form_design/Context'; import useConfirm from 'components/admin/form_design/useConfirm'; -import {normalizeErrors} from 'components/admin/forms/Field'; import Fieldset from 'components/admin/forms/Fieldset'; import FormRow from 'components/admin/forms/FormRow'; import {LOADING_OPTION} from 'components/admin/forms/Select'; +import {ValidationErrorContext} from 'components/admin/forms/ValidationErrors'; import VariableMapping from 'components/admin/forms/VariableMapping'; import { AuthAttributePath, @@ -26,7 +26,7 @@ import {FAIcon} from 'components/admin/icons'; import ErrorBoundary from 'components/errors/ErrorBoundary'; import {get} from 'utils/fetch'; -import {ErrorsType} from '../types'; +import ValidationErrorsProvider from '../../../../forms/ValidationErrors'; import CopyConfigurationFromRegistrationBackend from './CopyConfigurationFromRegistrationBackend'; const PLUGIN_ID = 'objects_api'; @@ -40,7 +40,7 @@ const onApiGroupChange = prevValues => ({ ...prevValues.options, objecttypeUuid: '', objecttypeVersion: undefined, - authAttributePath: [], + authAttributePath: undefined, variablesMapping: [], }, }); @@ -56,40 +56,34 @@ const getProperties = async (objectsApiGroup, objecttypeUuid, objecttypeVersion) return response.data.map(property => [property.targetPath, property.targetPath.join(' > ')]); }; -const ObjectsAPIFields = ({errors, showCopyButton, setShowCopyButton}) => { +const ObjectsAPIFields = ({showCopyButton, setShowCopyButton}) => { const intl = useIntl(); + // Object with keys the plugin/attribute/options, we process these further to set up + // the required context for the fields. + const errors = Object.fromEntries(useContext(ValidationErrorContext)); + const optionsErrors = Object.entries(errors.options ?? {}).map(([key, errs]) => [ + `options.${key}`, + errs, + ]); + const {values, setFieldValue, setValues} = useFormikContext(); const { - values, - values: { - plugin, - options: { - objecttypeUuid, - objecttypeVersion, - objectsApiGroup, - authAttributePath, - variablesMapping, - }, - }, - setFieldValue, - setValues, - } = useFormikContext(); + plugin, + options: {objecttypeUuid, objecttypeVersion, objectsApiGroup}, + } = values; const defaults = { objectsApiGroup: null, objecttypeUuid: '', objecttypeVersion: null, - authAttributePath: [], + authAttributePath: undefined, variablesMapping: [], }; // Merge defaults into options if not already set useEffect(() => { - if (!values.options) { - setFieldValue('options', defaults); - } else { - setFieldValue('options', {...defaults, ...values.options}); - } + const options = values.options ?? {}; + setFieldValue('options', {...defaults, ...options}); }, []); const { @@ -129,13 +123,8 @@ const ObjectsAPIFields = ({errors, showCopyButton, setShowCopyButton}) => { if (error) throw error; const prefillProperties = loading ? LOADING_OPTION : value; - const [, objectsApiGroupErrors] = normalizeErrors(errors.options?.objectsApiGroup, intl); - const [, objecttypeUuidErrors] = normalizeErrors(errors.options?.objecttypeUuid, intl); - const [, objecttypeVersionErrors] = normalizeErrors(errors.options?.objecttypeVersion, intl); - const [, authAttributePathErrors] = normalizeErrors(errors.options?.authAttributePath, intl); - return ( - <> + {showCopyButton ? ( {
{ if (!objecttypeUuid) return true; const confirmSwitch = await openApiGroupConfirmationModal(); @@ -180,7 +168,6 @@ const ObjectsAPIFields = ({errors, showCopyButton, setShowCopyButton}) => { name="options.objecttypeUuid" apiGroupFieldName="options.objectsApiGroup" versionFieldName="options.objecttypeVersion" - errors={objecttypeUuidErrors} label={ { defaultMessage="Version" /> } - errors={objecttypeVersionErrors} apiGroupFieldName="options.objectsApiGroup" objectTypeFieldName="options.objecttypeUuid" /> @@ -241,7 +227,6 @@ const ObjectsAPIFields = ({errors, showCopyButton, setShowCopyButton}) => { objecttypeUuid={objecttypeUuid} objecttypeVersion={objecttypeVersion} style={{maxWidth: '10em'}} - errors={authAttributePathErrors} />
@@ -294,14 +279,13 @@ const ObjectsAPIFields = ({errors, showCopyButton, setShowCopyButton}) => { /> } /> - +
); }; ObjectsAPIFields.propTypes = { - errors: PropTypes.shape({ - plugin: ErrorsType, - }).isRequired, + showCopyButton: PropTypes.bool.isRequired, + setShowCopyButton: PropTypes.func.isRequired, }; export default ObjectsAPIFields; diff --git a/src/openforms/js/components/admin/forms/Field.js b/src/openforms/js/components/admin/forms/Field.js index 23e99583fd..862a66e1cf 100644 --- a/src/openforms/js/components/admin/forms/Field.js +++ b/src/openforms/js/components/admin/forms/Field.js @@ -6,6 +6,28 @@ import {useIntl} from 'react-intl'; import {PrefixContext} from './Context'; import ErrorList from './ErrorList'; +/** + * @typedef {Object} IntlErrorMessage + * @property {string} defaultMessage + * @property {string} description + */ + +/** + * @typedef {[string, string]} NamedErrorMessage + * @property {string} 0 - The form field name with the error. + * @property {string} 1 - The error message itself. + */ + +/** + * @typedef {string | IntlErrorMessage | NamedErrorMessage} ErrorMessage + */ + +/** + * + * @param {ErrorMessage | ErrorMessage[]} errors A single error instance or array of errors. + * @param {IntlShape} intl The intl object from react-intl (useIntl() hook return value). + * @return {[boolean, string[]]} A tuple indicating if there are errors and the list of error messages. + */ export const normalizeErrors = (errors = [], intl) => { if (!Array.isArray(errors)) { errors = [errors]; @@ -55,6 +77,7 @@ const Field = ({ modifiedChildren = React.cloneElement(children, childProps); } const [hasErrors, formattedErrors] = normalizeErrors(errors, intl); + // console.log(name, hasErrors, formattedErrors); const className = classNames({ fieldBox: fieldBox, errors: hasErrors, diff --git a/src/openforms/js/components/admin/forms/ValidationErrors.js b/src/openforms/js/components/admin/forms/ValidationErrors.js index 1df7e12832..5b316c29c2 100644 --- a/src/openforms/js/components/admin/forms/ValidationErrors.js +++ b/src/openforms/js/components/admin/forms/ValidationErrors.js @@ -26,6 +26,12 @@ ValidationErrorsProvider.propTypes = { errors: PropTypes.arrayOf(PropTypes.arrayOf(errorArray)), }; +/** + * Only return the errors that have the $name prefix. + * @param {string} name Field name prefix + * @param {Array<[string, T>} errors List of all validation errors. + * @return {Array<[string, T>} List of errors that match the name prefix. + */ const filterErrors = (name, errors) => { return errors .filter(([key]) => key.startsWith(`${name}.`)) diff --git a/src/openforms/js/components/admin/forms/objects_api/ObjectTypeSelect.js b/src/openforms/js/components/admin/forms/objects_api/ObjectTypeSelect.js index d4d8e9d050..a7c19eee49 100644 --- a/src/openforms/js/components/admin/forms/objects_api/ObjectTypeSelect.js +++ b/src/openforms/js/components/admin/forms/objects_api/ObjectTypeSelect.js @@ -26,7 +26,6 @@ const ObjectTypeSelect = ({ label, helpText, versionFieldName = 'objecttypeVersion', - errors = [], }) => { const [fieldProps, , fieldHelpers] = useField(name); const { @@ -69,14 +68,7 @@ const ObjectTypeSelect = ({ return ( - + { const {getFieldProps} = useFormikContext(); @@ -56,7 +55,7 @@ const ObjectTypeVersionSelect = ({ const options = choices.map(([value, label]) => ({value, label})); return ( - + { const [{onChange: onChangeFormik, ...fieldProps}, , {setValue}] = useField(name); const {setValues} = useFormikContext(); @@ -41,7 +40,6 @@ const ObjectsAPIGroup = ({