diff --git a/src/formio/components/AddressNL.jsx b/src/formio/components/AddressNL.jsx
index e2ab54c93..2302795ad 100644
--- a/src/formio/components/AddressNL.jsx
+++ b/src/formio/components/AddressNL.jsx
@@ -3,7 +3,7 @@
*/
import {Formik, useFormikContext} from 'formik';
import debounce from 'lodash/debounce';
-import {useContext, useEffect} from 'react';
+import {useContext, useEffect, useState} from 'react';
import {createRoot} from 'react-dom/client';
import {Formio} from 'react-formio';
import {FormattedMessage, IntlProvider, defineMessages, useIntl} from 'react-intl';
@@ -77,6 +77,7 @@ export default class AddressNL extends Field {
city: '',
streetName: '',
secretStreetCity: '',
+ autoPopulated: false,
};
}
@@ -199,6 +200,14 @@ const FIELD_LABELS = defineMessages({
description: 'Label for addressNL houseNumber input',
defaultMessage: 'House number',
},
+ streetName: {
+ description: 'Label for addressNL streetName input',
+ defaultMessage: 'Street name',
+ },
+ city: {
+ description: 'Label for addressNL city input',
+ defaultMessage: 'City',
+ },
});
const addressNLSchema = (required, intl, {postcode = {}, city = {}}) => {
@@ -216,6 +225,7 @@ const addressNLSchema = (required, intl, {postcode = {}, city = {}}) => {
});
let postcodeSchema = z.string().regex(postcodeRegex, {message: postcodeErrorMessage});
+ let streetNameSchema = z.string();
const {pattern: cityPattern = '', errorMessage: cityErrorMessage = ''} = city;
let citySchema = z.string();
if (cityPattern) {
@@ -235,12 +245,15 @@ const addressNLSchema = (required, intl, {postcode = {}, city = {}}) => {
if (!required) {
postcodeSchema = postcodeSchema.optional();
houseNumberSchema = houseNumberSchema.optional();
+ streetNameSchema = streetNameSchema.optional();
+ citySchema = citySchema.optional();
}
return z
.object({
postcode: postcodeSchema,
- city: citySchema.optional(),
+ streetName: streetNameSchema,
+ city: citySchema,
houseNumber: houseNumberSchema,
houseLetter: z
.string()
@@ -300,13 +313,15 @@ const AddressNLForm = ({initialValues, required, deriveAddress, layout, setFormi
openForms: {components: nestedComponents},
},
} = useContext(ConfigContext);
- const {postcode, city} = nestedComponents || {};
+ const {postcode, city, streetName} = nestedComponents || {};
const postcodePattern = postcode?.validate?.pattern;
const postcodeError = postcode?.translatedErrors[intl.locale].pattern;
const cityPattern = city?.validate?.pattern;
const cityError = city?.translatedErrors[intl.locale].pattern;
+ const streetNameError = streetName?.translatedErrors[intl.locale].pattern;
+ console.log(streetNameError, cityError);
const errorMap = (issue, ctx) => {
switch (issue.code) {
case z.ZodIssueCode.invalid_type: {
@@ -340,6 +355,7 @@ const AddressNLForm = ({initialValues, required, deriveAddress, layout, setFormi
postcode: true,
houseNumber: true,
city: true,
+ streetName: true,
}}
validationSchema={toFormikValidationSchema(
addressNLSchema(required, intl, {
@@ -351,6 +367,9 @@ const AddressNLForm = ({initialValues, required, deriveAddress, layout, setFormi
pattern: cityPattern,
errorMessage: cityError,
},
+ streetName: {
+ errorMessage: streetNameError,
+ },
}),
{errorMap}
)}
@@ -368,6 +387,7 @@ const AddressNLForm = ({initialValues, required, deriveAddress, layout, setFormi
const FormikAddress = ({required, setFormioValues, deriveAddress, layout}) => {
const {values, isValid, setFieldValue} = useFormikContext();
const {baseUrl} = useContext(ConfigContext);
+ const [isAddressDisabled, setAddressDisabled] = useState(true);
const useColumns = layout === 'doubleColumn';
useEffect(() => {
@@ -394,6 +414,12 @@ const FormikAddress = ({required, setFormioValues, deriveAddress, layout}) => {
setFieldValue('city', data['city']);
setFieldValue('streetName', data['streetName']);
setFieldValue('secretStreetCity', data['secretStreetCity']);
+
+ // mark the auto-filled fields as populated and disabled when they have been both
+ // retrieved from the API and they do have a value
+ const dataRetrieved = !!(data['city'] && data['streetName']);
+ setAddressDisabled(dataRetrieved);
+ setFieldValue('autoPopulated', dataRetrieved);
};
return (
@@ -430,21 +456,23 @@ const FormikAddress = ({required, setFormioValues, deriveAddress, layout}) => {
name="streetName"
label={
}
- disabled
+ disabled={isAddressDisabled}
+ isRequired={required}
/>
}
- disabled
+ disabled={isAddressDisabled}
+ isRequired={required}
/>
>
)}
diff --git a/src/formio/components/AddressNL.stories.js b/src/formio/components/AddressNL.stories.js
index 20bd619c8..1b1d12276 100644
--- a/src/formio/components/AddressNL.stories.js
+++ b/src/formio/components/AddressNL.stories.js
@@ -195,7 +195,7 @@ export const WithFailedBRKValidation = {
// },
};
-export const WithDeriveCityStreetNameWithData = {
+export const WithDeriveCityStreetNameWithDataNotRequired = {
render: SingleFormioComponent,
parameters: {
msw: {
@@ -246,6 +246,115 @@ export const WithDeriveCityStreetNameWithData = {
},
};
+export const WithDeriveCityStreetNameWithNoDataNotRequired = {
+ render: SingleFormioComponent,
+ parameters: {
+ msw: {
+ handlers: [mockBAGNoDataGet],
+ },
+ },
+ args: {
+ type: 'addressNL',
+ key: 'addressNL',
+ label: 'Address NL',
+ extraComponentProperties: {
+ validate: {
+ required: false,
+ },
+ deriveAddress: true,
+ openForms: {
+ components: {
+ city: {
+ validate: {pattern: 'Amsterdam'},
+ translatedErrors: {
+ nl: {
+ pattern: 'De stad moet Amsterdam zijn',
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ play: async ({canvasElement}) => {
+ const canvas = within(canvasElement);
+
+ const postcodeInput = await canvas.findByLabelText('Postcode');
+ await userEvent.type(postcodeInput, '1234AB');
+
+ const houseNumberInput = await canvas.findByLabelText('Huisnummer');
+ await userEvent.type(houseNumberInput, '1');
+
+ const city = await canvas.findByLabelText('Stad');
+ const streetName = await canvas.findByLabelText('Straatnaam');
+
+ await userEvent.tab();
+
+ await waitFor(() => {
+ expect(city).toHaveValue('');
+ expect(city).not.toBeDisabled();
+ expect(streetName).toHaveValue('');
+ expect(streetName).not.toBeDisabled();
+ });
+ },
+};
+export const WithDeriveCityStreeNameNoDataAndRequired = {
+ render: SingleFormioComponent,
+ parameters: {
+ msw: {
+ handlers: [mockBAGNoDataGet],
+ },
+ },
+ args: {
+ type: 'addressNL',
+ key: 'addressNL',
+ label: 'Address NL',
+ extraComponentProperties: {
+ validate: {
+ required: true,
+ },
+ deriveAddress: true,
+ openForms: {
+ components: {
+ city: {
+ validate: {pattern: 'Amsterdam'},
+ translatedErrors: {
+ nl: {
+ pattern: 'De stad moet Amsterdam zijn',
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ play: async ({canvasElement}) => {
+ const canvas = within(canvasElement);
+
+ const postcodeInput = await canvas.findByLabelText('Postcode');
+ postcodeInput.focus();
+ await userEvent.tab();
+
+ const city = await canvas.findByLabelText('Stad');
+ const streetName = await canvas.findByLabelText('Straatnaam');
+ const postcode_error = await canvas.findByText('Postcode is verplicht.');
+ const house_number_error = await canvas.findByText('Huisnummer is verplicht.');
+ const city_error = await canvas.findByText('Stad is verplicht.');
+ const street_name_error = await canvas.findByText('Straatnaam is verplicht.');
+
+ await waitFor(() => {
+ expect(city).toHaveValue('');
+ expect(city).toBeDisabled();
+ expect(streetName).toHaveValue('');
+ expect(streetName).toBeDisabled();
+ expect(postcode_error).toBeVisible();
+ expect(house_number_error).toBeVisible();
+ expect(city_error).toBeVisible();
+ expect(street_name_error).toBeVisible();
+ });
+ },
+};
+
export const IncorrectPostcode = {
render: SingleFormioComponent,
args: {
diff --git a/src/i18n/compiled/en.json b/src/i18n/compiled/en.json
index 69d4d9e5d..0f0fad705 100644
--- a/src/i18n/compiled/en.json
+++ b/src/i18n/compiled/en.json
@@ -397,6 +397,12 @@
"value": "Check and confirm"
}
],
+ "AKhmW+": [
+ {
+ "type": 0,
+ "value": "Street name"
+ }
+ ],
"AM6xqd": [
{
"type": 0,
@@ -491,12 +497,6 @@
"value": "Remove"
}
],
- "DEetjI": [
- {
- "type": 0,
- "value": "Street name"
- }
- ],
"DK2ewv": [
{
"type": 0,
@@ -1375,6 +1375,12 @@
"value": "Form temporarily unavailable"
}
],
+ "ZNkl8Q": [
+ {
+ "type": 0,
+ "value": "City"
+ }
+ ],
"ZVQeut": [
{
"type": 1,
@@ -1789,6 +1795,12 @@
"value": "Product"
}
],
+ "leZlV+": [
+ {
+ "type": 0,
+ "value": "Street name"
+ }
+ ],
"lmWBQT": [
{
"type": 0,
@@ -1925,12 +1937,6 @@
"value": "."
}
],
- "osSl3z": [
- {
- "type": 0,
- "value": "City"
- }
- ],
"ovI+W7": [
{
"type": 0,
@@ -2069,6 +2075,12 @@
"value": "Send code"
}
],
+ "s4+4p2": [
+ {
+ "type": 0,
+ "value": "City"
+ }
+ ],
"sSmY1N": [
{
"type": 0,
diff --git a/src/i18n/compiled/nl.json b/src/i18n/compiled/nl.json
index a57a85100..87bfc6c12 100644
--- a/src/i18n/compiled/nl.json
+++ b/src/i18n/compiled/nl.json
@@ -397,6 +397,12 @@
"value": "Controleer en bevestig"
}
],
+ "AKhmW+": [
+ {
+ "type": 0,
+ "value": "Straatnaam"
+ }
+ ],
"AM6xqd": [
{
"type": 0,
@@ -491,12 +497,6 @@
"value": "Verwijderen"
}
],
- "DEetjI": [
- {
- "type": 0,
- "value": "Straatnaam"
- }
- ],
"DK2ewv": [
{
"type": 0,
@@ -1375,6 +1375,12 @@
"value": "Formulier tijdelijk onbeschikbaar"
}
],
+ "ZNkl8Q": [
+ {
+ "type": 0,
+ "value": "Stad"
+ }
+ ],
"ZVQeut": [
{
"type": 0,
@@ -1793,6 +1799,12 @@
"value": "Product"
}
],
+ "leZlV+": [
+ {
+ "type": 0,
+ "value": "Straatnaam"
+ }
+ ],
"lmWBQT": [
{
"type": 0,
@@ -1929,12 +1941,6 @@
"value": " zijn."
}
],
- "osSl3z": [
- {
- "type": 0,
- "value": "Stad"
- }
- ],
"ovI+W7": [
{
"type": 0,
@@ -2073,6 +2079,12 @@
"value": "Verstuur code"
}
],
+ "s4+4p2": [
+ {
+ "type": 0,
+ "value": "Stad"
+ }
+ ],
"sSmY1N": [
{
"type": 0,
diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json
index 965f9c360..292c94a44 100644
--- a/src/i18n/messages/en.json
+++ b/src/i18n/messages/en.json
@@ -174,6 +174,11 @@
"description": "Check overview and confirm",
"originalDefault": "Check and confirm"
},
+ "AKhmW+": {
+ "defaultMessage": "Street name",
+ "description": "Label for addressNL streetName input",
+ "originalDefault": "Street name"
+ },
"AM6xqd": {
"defaultMessage": "House number must be a number with up to five digits (e.g. 456).",
"description": "ZOD error message when AddressNL house number does not match the house number regular expression",
@@ -249,11 +254,6 @@
"description": "Appointments: remove product/service button text",
"originalDefault": "Remove"
},
- "DEetjI": {
- "defaultMessage": "Street name",
- "description": "Label for addressNL streetName read only result",
- "originalDefault": "Street name"
- },
"DK2ewv": {
"defaultMessage": "Authentication problem",
"description": "'Permission denied' error title",
@@ -639,6 +639,11 @@
"description": "'Maintenance mode form' error title",
"originalDefault": "Form temporarily unavailable"
},
+ "ZNkl8Q": {
+ "defaultMessage": "City",
+ "description": "Label for addressNL city result or entered value",
+ "originalDefault": "City"
+ },
"ZVQeut": {
"defaultMessage": "{field} is a required field.",
"description": "Required field error message",
@@ -874,6 +879,11 @@
"description": "Appointments products step page title",
"originalDefault": "Product"
},
+ "leZlV+": {
+ "defaultMessage": "Street name",
+ "description": "Label for addressNL streetName result or entered value",
+ "originalDefault": "Street name"
+ },
"lmWBQT": {
"defaultMessage": "The code is exactly six characters long and consists of only uppercase letters and numbers.",
"description": "Email verification: code input field description",
@@ -934,11 +944,6 @@
"description": "ZOD 'too_big' error message, for BigInt",
"originalDefault": "BigInt must be {exact, select, true {exactly equal to} other {{inclusive, select, true {less than or equal to} other {less than}}} } {maximum}."
},
- "osSl3z": {
- "defaultMessage": "City",
- "description": "Label for addressNL city read only result",
- "originalDefault": "City"
- },
"ovI+W7": {
"defaultMessage": "Use ⌘ + scroll to zoom the map",
"description": "Gesturehandeling mac scroll message.",
@@ -984,6 +989,11 @@
"description": "Email verification: send code button text",
"originalDefault": "Send code"
},
+ "s4+4p2": {
+ "defaultMessage": "City",
+ "description": "Label for addressNL city input",
+ "originalDefault": "City"
+ },
"sSmY1N": {
"defaultMessage": "Find address",
"description": "The leaflet map's input fields placeholder message.",
diff --git a/src/i18n/messages/nl.json b/src/i18n/messages/nl.json
index 4ef35f536..e9a86dc94 100644
--- a/src/i18n/messages/nl.json
+++ b/src/i18n/messages/nl.json
@@ -176,6 +176,11 @@
"description": "Check overview and confirm",
"originalDefault": "Check and confirm"
},
+ "AKhmW+": {
+ "defaultMessage": "Straatnaam",
+ "description": "Label for addressNL streetName input",
+ "originalDefault": "Street name"
+ },
"AM6xqd": {
"defaultMessage": "Huisnummer moet een nummer zijn met maximaal 5 cijfers (bijv. 456).",
"description": "ZOD error message when AddressNL house number does not match the house number regular expression",
@@ -252,11 +257,6 @@
"description": "Appointments: remove product/service button text",
"originalDefault": "Remove"
},
- "DEetjI": {
- "defaultMessage": "Straatnaam",
- "description": "Label for addressNL streetName read only result",
- "originalDefault": "Street name"
- },
"DK2ewv": {
"defaultMessage": "Inlogprobleem",
"description": "'Permission denied' error title",
@@ -647,6 +647,11 @@
"description": "'Maintenance mode form' error title",
"originalDefault": "Form temporarily unavailable"
},
+ "ZNkl8Q": {
+ "defaultMessage": "Stad",
+ "description": "Label for addressNL city result or entered value",
+ "originalDefault": "City"
+ },
"ZVQeut": {
"defaultMessage": "Het verplichte veld {field} is niet ingevuld.",
"description": "Required field error message",
@@ -886,6 +891,11 @@
"isTranslated": true,
"originalDefault": "Product"
},
+ "leZlV+": {
+ "defaultMessage": "Straatnaam",
+ "description": "Label for addressNL streetName result or entered value",
+ "originalDefault": "Street name"
+ },
"lmWBQT": {
"defaultMessage": "De bevestigingscode is precies zes tekens lang en bestaat uit hoofdletters en getallen.",
"description": "Email verification: code input field description",
@@ -946,11 +956,6 @@
"description": "ZOD 'too_big' error message, for BigInt",
"originalDefault": "BigInt must be {exact, select, true {exactly equal to} other {{inclusive, select, true {less than or equal to} other {less than}}} } {maximum}."
},
- "osSl3z": {
- "defaultMessage": "Stad",
- "description": "Label for addressNL city read only result",
- "originalDefault": "City"
- },
"ovI+W7": {
"defaultMessage": "Gebruik ⌘ + scroll om te zoomen in de kaart",
"description": "Gesturehandeling mac scroll message.",
@@ -996,6 +1001,11 @@
"description": "Email verification: send code button text",
"originalDefault": "Send code"
},
+ "s4+4p2": {
+ "defaultMessage": "Stad",
+ "description": "Label for addressNL city input",
+ "originalDefault": "City"
+ },
"sSmY1N": {
"defaultMessage": "Zoek adres",
"description": "The leaflet map's input fields placeholder message.",