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 ? (
{
@@ -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 = ({