Skip to content

Commit

Permalink
Merge pull request #629 from open-formulieren/chore/address-nl-i18n
Browse files Browse the repository at this point in the history
Localize (validation error) messages
  • Loading branch information
sergei-maertens authored Jan 9, 2024
2 parents b014d03 + 2c38fe5 commit 4bfec86
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 83 deletions.
88 changes: 63 additions & 25 deletions src/formio/components/AddressNL.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import debounce from 'lodash/debounce';
import React, {useEffect} from 'react';
import {createRoot} from 'react-dom/client';
import {Formio} from 'react-formio';
import {FormattedMessage, IntlProvider, createIntl} from 'react-intl';
import {FormattedMessage, IntlProvider, defineMessages, useIntl} from 'react-intl';
import {z} from 'zod';
import {toFormikValidationSchema} from 'zod-formik-adapter';

Expand Down Expand Up @@ -136,7 +136,6 @@ export default class AddressNL extends Field {

renderReact() {
const required = this.component?.validate?.required || false;
const intl = createIntl(this.options.intl);
const initialValues = {...this.emptyValue, ...this.dataValue};

this.reactRoot.render(
Expand All @@ -147,16 +146,11 @@ export default class AddressNL extends Field {
requiredFieldsWithAsterisk: this.options.evalContext.requiredFieldsWithAsterisk,
}}
>
<Formik
<AddressNLForm
initialValues={initialValues}
initialTouched={{
postcode: true,
houseNumber: true,
}}
validationSchema={toFormikValidationSchema(addressNLSchema(required, intl))}
>
<FormikAddress required={required} setFormioValues={this.onFormikChange.bind(this)} />
</Formik>
required={required}
setFormioValues={this.onFormikChange.bind(this)}
/>
</ConfigContext.Provider>
</IntlProvider>
);
Expand All @@ -170,6 +164,17 @@ export default class AddressNL extends Field {
}
}

const FIELD_LABELS = defineMessages({
postcode: {
description: 'Label for addressNL postcode input',
defaultMessage: 'Postcode',
},
houseNumber: {
description: 'Label for addressNL houseNumber input',
defaultMessage: 'House number',
},
});

const addressNLSchema = (required, intl) => {
let postcodeSchema = z.string().regex(/^[1-9][0-9]{3} ?(?!sa|sd|ss|SA|SD|SS)[a-zA-Z]{2}$/);
let houseNumberSchema = z.string().regex(/^\d{1,5}$/);
Expand Down Expand Up @@ -197,7 +202,7 @@ const addressNLSchema = (required, intl) => {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: intl.formatMessage({
descripion:
description:
'ZOD error message when AddressNL postcode is provided but not houseNumber',
defaultMessage: 'You must provide a house number.',
}),
Expand All @@ -209,7 +214,7 @@ const addressNLSchema = (required, intl) => {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: intl.formatMessage({
descripion:
description:
'ZOD error message when AddressNL houseNumber is provided but not postcode',
defaultMessage: 'You must provide a postcode.',
}),
Expand All @@ -220,6 +225,49 @@ const addressNLSchema = (required, intl) => {
});
};

const AddressNLForm = ({initialValues, required, setFormioValues}) => {
const intl = useIntl();

const errorMap = (issue, ctx) => {
switch (issue.code) {
case z.ZodIssueCode.invalid_type: {
if (issue.received === z.ZodParsedType.undefined) {
const fieldName = issue.path.join('.');
const fieldLabel = intl.formatMessage(FIELD_LABELS[fieldName]);
const message = intl.formatMessage(
{
description: 'Required field error message',
defaultMessage: '{field} is required.',
},
{
field: fieldLabel,
}
);
return {message};
}
break;
}
default: {
break;
}
}
return {message: ctx.defaultError}; // use global schema as fallback
};

return (
<Formik
initialValues={initialValues}
initialTouched={{
postcode: true,
houseNumber: true,
}}
validationSchema={toFormikValidationSchema(addressNLSchema(required, intl), {errorMap})}
>
<FormikAddress required={required} setFormioValues={setFormioValues} />
</Formik>
);
};

const FormikAddress = ({required, setFormioValues}) => {
const {values, isValid} = useFormikContext();

Expand All @@ -242,12 +290,7 @@ const FormikAddress = ({required, setFormioValues}) => {
<div className="column column--span-6 openforms-form-field-container">
<TextField
name="houseNumber"
label={
<FormattedMessage
description="Label for addressNL houseNumber input"
defaultMessage="House number"
/>
}
label={<FormattedMessage {...FIELD_LABELS.houseNumber} />}
placeholder="123"
isRequired={required}
/>
Expand Down Expand Up @@ -299,12 +342,7 @@ const PostCodeField = ({required}) => {
return (
<TextField
name="postcode"
label={
<FormattedMessage
description="Label for addressNL postcode input"
defaultMessage="Postcode"
/>
}
label={<FormattedMessage {...FIELD_LABELS.postcode} />}
placeholder="1234 AB"
isRequired={required}
onBlur={onBlur}
Expand Down
22 changes: 11 additions & 11 deletions src/formio/components/AddressNL.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ export const ClientSideValidation = {
const canvas = within(canvasElement);

const postcodeInput = await canvas.findByLabelText('Postcode');
const houseNumberInput = await canvas.findByLabelText('Huis nummer');
const houseLetter = await canvas.findByLabelText('Huis letter');
const houseNumberAddition = await canvas.findByLabelText('Huis nummer toevoeging');
const houseNumberInput = await canvas.findByLabelText('Huisnummer');
const houseLetter = await canvas.findByLabelText('Huisletter');
const houseNumberAddition = await canvas.findByLabelText('Huisnummertoevoeging');

await step('Fill only postcode - client side validation error', async () => {
userEvent.type(postcodeInput, '1234AB');
expect(await canvas.findByText('Required')).toBeVisible();
expect(await canvas.findByText('Huisnummer is verplicht.')).toBeVisible();
});

await step('Fill house number field', async () => {
Expand All @@ -61,13 +61,13 @@ export const ClientSideValidation = {

await waitFor(() => {
expect(houseNumberAddition).not.toHaveFocus();
expect(canvas.queryByText('Required')).not.toBeInTheDocument();
expect(canvas.queryByText('/is verplicht/')).not.toBeInTheDocument();
});
});

await step('Clear postcode field, keep house number field', async () => {
userEvent.clear(postcodeInput);
expect(await canvas.findByText('Required')).toBeVisible();
expect(await canvas.findByText('Postcode is verplicht.')).toBeVisible();
});
},
};
Expand All @@ -85,17 +85,17 @@ export const NotRequired = {
const canvas = within(canvasElement);

const postcodeInput = await canvas.findByLabelText('Postcode');
const houseNumberInput = await canvas.findByLabelText('Huis nummer');
const houseNumberInput = await canvas.findByLabelText('Huisnummer');

await step('Enter only postcode, without house number', async () => {
userEvent.type(postcodeInput, '1234AB');
expect(await canvas.findByText('You must provide a house number.')).toBeVisible();
expect(await canvas.findByText('Huisnummer is verplicht.')).toBeVisible();
});

await step('Enter only house number, without postcode', async () => {
userEvent.clear(postcodeInput);
userEvent.type(houseNumberInput, '1');
expect(await canvas.findByText('You must provide a postcode.')).toBeVisible();
expect(await canvas.findByText('Postcode is verplicht.')).toBeVisible();
});
},
};
Expand Down Expand Up @@ -126,7 +126,7 @@ export const WithPassingBRKValidation = {
const postcodeInput = await canvas.findByLabelText('Postcode');
userEvent.type(postcodeInput, '1234AB');

const houseNumberInput = await canvas.findByLabelText('Huis nummer');
const houseNumberInput = await canvas.findByLabelText('Huisnummer');
userEvent.type(houseNumberInput, '1');

// this assertion is not worth much due to the async nature of the validators...
Expand Down Expand Up @@ -166,7 +166,7 @@ export const WithFailedBRKValidation = {
// expect(postcodeInput).toHaveDisplayValue('1234AB');
// });

// const houseNumberInput = await canvas.findByLabelText('Huis nummer');
// const houseNumberInput = await canvas.findByLabelText('Huisnummer');
// userEvent.type(houseNumberInput, '1');
// await waitFor(() => {
// expect(houseNumberInput).toHaveDisplayValue('1');
Expand Down
1 change: 0 additions & 1 deletion src/formio/validators/plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {post} from '../../api';
export const pluginsAPIValidator = {
key: `validate.backendApi`,
check(component, setting, value) {
console.log('validator check method', value);
const checkIsEmpty = component.component?.openForms?.checkIsEmptyBeforePluginValidate || false;
const shortCutBecauseEmpty = checkIsEmpty && isEmpty(value);
if (!value || shortCutBecauseEmpty) return true;
Expand Down
34 changes: 22 additions & 12 deletions src/i18n/compiled/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,16 @@
"value": "Please accept the privacy policy before submitting"
}
],
"8cxbC6": [
{
"type": 1,
"value": "field"
},
{
"type": 0,
"value": " is required."
}
],
"8uBESg": [
{
"type": 0,
Expand Down Expand Up @@ -487,6 +497,12 @@
"value": "Contact details"
}
],
"GbPef0": [
{
"type": 0,
"value": "You must provide a house number."
}
],
"GiRKAS": [
{
"type": 0,
Expand Down Expand Up @@ -1193,12 +1209,6 @@
"value": "House letter"
}
],
"cHn60V": [
{
"type": 0,
"value": "You must provide a house number."
}
],
"cKFCTI": [
{
"type": 0,
Expand Down Expand Up @@ -1487,6 +1497,12 @@
"value": "Product"
}
],
"lKBAu3": [
{
"type": 0,
"value": "You must provide a postcode."
}
],
"lVNV/d": [
{
"type": 0,
Expand Down Expand Up @@ -1619,12 +1635,6 @@
"value": "Use ⌘ + scroll to zoom the map"
}
],
"p+11YF": [
{
"type": 0,
"value": "You must provide a postcode."
}
],
"pguTkQ": [
{
"type": 0,
Expand Down
40 changes: 25 additions & 15 deletions src/i18n/compiled/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,16 @@
"value": "U moet akkoord gaan met het privacybeleid om door te gaan"
}
],
"8cxbC6": [
{
"type": 1,
"value": "field"
},
{
"type": 0,
"value": " is verplicht."
}
],
"8uBESg": [
{
"type": 0,
Expand Down Expand Up @@ -487,6 +497,12 @@
"value": "Contactgegevens"
}
],
"GbPef0": [
{
"type": 0,
"value": "Huisnummer is verplicht."
}
],
"GiRKAS": [
{
"type": 0,
Expand Down Expand Up @@ -814,7 +830,7 @@
"PCv4sQ": [
{
"type": 0,
"value": "Huis nummer"
"value": "Huisnummer"
}
],
"PjYrw0": [
Expand Down Expand Up @@ -1194,13 +1210,7 @@
"cBsrax": [
{
"type": 0,
"value": "Huis letter"
}
],
"cHn60V": [
{
"type": 0,
"value": "You must provide a house number."
"value": "Huisletter"
}
],
"cKFCTI": [
Expand Down Expand Up @@ -1491,10 +1501,16 @@
"value": "Product"
}
],
"lKBAu3": [
{
"type": 0,
"value": "Postcode is verplicht."
}
],
"lVNV/d": [
{
"type": 0,
"value": "Huis nummer toevoeging"
"value": "Huisnummertoevoeging"
}
],
"lY+Mza": [
Expand Down Expand Up @@ -1623,12 +1639,6 @@
"value": "Gebruik ⌘ + scroll om te zoomen in de kaart"
}
],
"p+11YF": [
{
"type": 0,
"value": "You must provide a postcode."
}
],
"pguTkQ": [
{
"type": 0,
Expand Down
Loading

0 comments on commit 4bfec86

Please sign in to comment.