From f14c4acebf50df511a1c984a61e5518a1b24c6ee Mon Sep 17 00:00:00 2001 From: Steven Bal Date: Mon, 29 Jul 2024 15:56:58 +0200 Subject: [PATCH 1/6] :sparkles: [open-formulieren/open-forms#4420] Field validation for AddressNL the following components can now be validated using regex: * postcode * city --- .../ComponentConfiguration.stories.tsx | 10 ++ src/components/builder/validate/i18n.tsx | 6 +- src/registry/addressNL/edit.tsx | 155 +++++++++++++++++- 3 files changed, 167 insertions(+), 4 deletions(-) diff --git a/src/components/ComponentConfiguration.stories.tsx b/src/components/ComponentConfiguration.stories.tsx index 8b4e5c8e..50687fa6 100644 --- a/src/components/ComponentConfiguration.stories.tsx +++ b/src/components/ComponentConfiguration.stories.tsx @@ -2241,6 +2241,16 @@ export const AddressNL: Story = { }, deriveAddress: false, layout: 'singleColumn', + ofComponents: { + postcode: { + validate: {pattern: '1015 [a-zA-Z]{2}'}, + translatedErrors: {}, + }, + city: { + validate: {pattern: 'Amsterdam'}, + translatedErrors: {}, + }, + }, }, builderInfo: { title: 'Address Field', diff --git a/src/components/builder/validate/i18n.tsx b/src/components/builder/validate/i18n.tsx index 1565c1f9..9f87ca36 100644 --- a/src/components/builder/validate/i18n.tsx +++ b/src/components/builder/validate/i18n.tsx @@ -9,12 +9,12 @@ import {BuilderContext} from '@/context'; import {DataMap, Panel, Tab, TabList, TabPanel, Tabs, TextField} from '../../formio'; export function useManageValidatorsTranslations( - keys: PossibleValidatorErrorKeys[] + keys: PossibleValidatorErrorKeys[], + field: string = 'translatedErrors' ): void { const {supportedLanguageCodes} = useContext(BuilderContext); - const [{value}, , {setValue}] = useField('translatedErrors'); + const [{value}, , {setValue}] = useField(field); - // set any missing translations useEffect(() => { const newValue = value ? {...value} diff --git a/src/registry/addressNL/edit.tsx b/src/registry/addressNL/edit.tsx index 3951c84f..15374687 100644 --- a/src/registry/addressNL/edit.tsx +++ b/src/registry/addressNL/edit.tsx @@ -1,4 +1,6 @@ import {AddressNLComponentSchema} from '@open-formulieren/types'; +import {TextField} from 'components/formio'; +import {useContext} from 'react'; import {FormattedMessage, useIntl} from 'react-intl'; import { @@ -19,12 +21,71 @@ import { } from '@/components/builder'; import {LABELS} from '@/components/builder/messages'; import {Checkbox} from '@/components/formio'; -import {TabList, TabPanel, Tabs} from '@/components/formio'; +import {DataMap, Panel, Tab, TabList, TabPanel, Tabs} from '@/components/formio'; import {Select} from '@/components/formio'; +import {BuilderContext} from '@/context'; import {useErrorChecker} from '@/utils/errors'; import {EditFormDefinition} from '../types'; +export interface SubcomponentValidationProps { + component: string; + label: React.ReactNode; + tooltip: string; + placeholder: string; +} + +export const SubcomponentValidation: React.FC = ({ + component, + label, + tooltip, + placeholder, +}) => { + const {supportedLanguageCodes} = useContext(BuilderContext); + return ( + <> + + + + {supportedLanguageCodes.map(code => ( + {code.toUpperCase()} + ))} + + + {supportedLanguageCodes.map(code => ( + + + } + valueComponent={ + + } + /> + } + /> + + ))} + + + ); +}; + const DeriveAddress = () => { const intl = useIntl(); const tooltip = intl.formatMessage({ @@ -81,6 +142,15 @@ const EditForm: EditFormDefinition = () => { const [isKeyManuallySetRef, generatedKey] = useDeriveComponentKey(); const {hasAnyError} = useErrorChecker(); Validate.useManageValidatorsTranslations(['required']); + Validate.useManageValidatorsTranslations( + ['pattern'], + `openForms.components.postcode.translatedErrors` + ); + Validate.useManageValidatorsTranslations( + ['pattern'], + `openForms.components.city.translatedErrors` + ); + return ( @@ -128,6 +198,76 @@ const EditForm: EditFormDefinition = () => { + + {/* Postcode field validation */} + + } + tooltip={intl.formatMessage({ + description: 'Tooltip postcode field validation panel', + defaultMessage: 'Validation for the postcode field', + })} + collapsible + initialCollapsed + > + + } + tooltip={intl.formatMessage({ + description: "Tooltip for 'validate.pattern' builder field", + defaultMessage: + 'The regular expression pattern test that the postcode field value must pass before the form can be submitted.', + })} + placeholder={intl.formatMessage({ + description: "Placeholder for 'validate.pattern' builder field", + defaultMessage: 'Regular expression for postcode', + })} + /> + + + {/* City field validation */} + + } + tooltip={intl.formatMessage({ + description: 'Tooltip city field validation panel', + defaultMessage: 'Validation for the city field', + })} + collapsible + initialCollapsed + > + + } + tooltip={intl.formatMessage({ + description: "Tooltip for 'validate.pattern' builder field", + defaultMessage: + 'The regular expression pattern test that the city field value must pass before the form can be submitted.', + })} + placeholder={intl.formatMessage({ + description: "Placeholder for 'validate.pattern' builder field", + defaultMessage: 'Regular expression for city', + })} + /> + {/* Registration tab */} @@ -192,6 +332,19 @@ EditForm.defaultValues = { registration: { attribute: '', }, + openForms: { + translations: {}, + components: { + postcode: { + validate: {pattern: ''}, + translatedErrors: {}, + }, + city: { + validate: {pattern: ''}, + translatedErrors: {}, + }, + }, + }, }; export default EditForm; From f802ab8e42d7156a4ca3fe8d61be121a92334fd2 Mon Sep 17 00:00:00 2001 From: Steven Bal Date: Mon, 12 Aug 2024 10:41:18 +0200 Subject: [PATCH 2/6] :white_check_mark: [open-formulieren/open-forms#4420] Stories for AddressNL validation --- .../ComponentConfiguration.stories.tsx | 18 +- src/registry/addressNL/edit.stories.ts | 160 ++++++++++++++++++ 2 files changed, 170 insertions(+), 8 deletions(-) create mode 100644 src/registry/addressNL/edit.stories.ts diff --git a/src/components/ComponentConfiguration.stories.tsx b/src/components/ComponentConfiguration.stories.tsx index 50687fa6..94a7679f 100644 --- a/src/components/ComponentConfiguration.stories.tsx +++ b/src/components/ComponentConfiguration.stories.tsx @@ -2241,14 +2241,16 @@ export const AddressNL: Story = { }, deriveAddress: false, layout: 'singleColumn', - ofComponents: { - postcode: { - validate: {pattern: '1015 [a-zA-Z]{2}'}, - translatedErrors: {}, - }, - city: { - validate: {pattern: 'Amsterdam'}, - translatedErrors: {}, + openForms: { + components: { + postcode: { + validate: {pattern: '1015 [a-zA-Z]{2}'}, + translatedErrors: {}, + }, + city: { + validate: {pattern: 'Amsterdam'}, + translatedErrors: {}, + }, }, }, }, diff --git a/src/registry/addressNL/edit.stories.ts b/src/registry/addressNL/edit.stories.ts new file mode 100644 index 00000000..8355648d --- /dev/null +++ b/src/registry/addressNL/edit.stories.ts @@ -0,0 +1,160 @@ +import {Meta, StoryObj} from '@storybook/react'; +import {expect, userEvent, waitFor, within} from '@storybook/test'; + +import ComponentEditForm from '@/components/ComponentEditForm'; + +export default { + title: 'Builder components/AddressNL', + component: ComponentEditForm, + parameters: {}, + args: { + isNew: true, + component: { + id: 'wekruya', + type: 'addressNL', + key: 'address', + label: 'An address field', + }, + builderInfo: { + title: 'Address NL', + icon: 'home', + group: 'basic', + weight: 10, + schema: {}, + }, + }, +} as Meta; + +type Story = StoryObj; + +export const PostcodeValidationTabWithoutConfiguration: Story = { + name: 'AddressNL postcode validation tab (no prior configuration)', + play: async ({canvasElement, step}) => { + const canvas = within(canvasElement); + + await step('Navigate to validation tab and open Postcode validation', async () => { + await userEvent.click(canvas.getByRole('link', {name: 'Validation'})); + await userEvent.click(canvas.getAllByText('Postcode')[0]); + await waitFor(async () => { + expect(await canvas.findByText('Regular expression for postcode')).toBeVisible(); + expect(await canvas.findByText('NL')).toBeVisible(); + expect(await canvas.findByText('EN')).toBeVisible(); + expect(await canvas.findByText('Error code')).toBeVisible(); + expect(await canvas.findByDisplayValue('pattern')).toBeVisible(); + }); + }); + }, +}; + +export const CityValidationTabWithoutConfiguration: Story = { + name: 'AddressNL city validation tab (no prior configuration)', + play: async ({canvasElement, step}) => { + const canvas = within(canvasElement); + + await step('Navigate to validation tab and open City validation', async () => { + await userEvent.click(canvas.getByRole('link', {name: 'Validation'})); + await userEvent.click(canvas.getAllByText('City')[0]); + await waitFor(async () => { + expect(await canvas.findByText('Regular expression for city')).toBeVisible(); + expect(await canvas.findByText('NL')).toBeVisible(); + expect(await canvas.findByText('EN')).toBeVisible(); + expect(await canvas.findByText('Error code')).toBeVisible(); + expect(await canvas.findByDisplayValue('pattern')).toBeVisible(); + }); + }); + }, +}; + +export const PostcodeValidationTabWithConfiguration: Story = { + name: 'AddressNL postcode validation tab (with prior configuration)', + args: { + component: { + id: 'wekruya', + type: 'addressNL', + key: 'address', + label: 'An address field', + openForms: { + components: { + postcode: { + validate: { + pattern: '1017 [A-Za-z]{2}', + }, + translatedErrors: { + nl: {pattern: 'Postcode moet 1017 XX zijn'}, + en: {pattern: 'Postal code must be 1017 XX'}, + }, + }, + }, + }, + }, + }, + play: async ({canvasElement, step}) => { + const canvas = within(canvasElement); + + await step('Navigate to validation tab and open Postcode validation', async () => { + await userEvent.click(canvas.getByRole('link', {name: 'Validation'})); + await userEvent.click(canvas.getAllByText('Postcode')[0]); + await waitFor(async () => { + const patternInput = canvas.getByLabelText( + 'Regular expression for postcode' + ) as HTMLInputElement; + expect(patternInput).toBeVisible(); + expect(patternInput.value).toBe('1017 [A-Za-z]{2}'); + + expect(await canvas.findByDisplayValue('pattern')).toBeVisible(); + expect(await canvas.findByDisplayValue('Postcode moet 1017 XX zijn')).toBeVisible(); + + await userEvent.click(await canvas.findByText('EN')); + expect(await canvas.findByDisplayValue('pattern')).toBeVisible(); + expect(await canvas.findByDisplayValue('Postal code must be 1017 XX')).toBeVisible(); + }); + }); + }, +}; + +export const CityValidationTabWithConfiguration: Story = { + name: 'AddressNL postcode validation tab (with prior configuration)', + args: { + component: { + id: 'wekruya', + type: 'addressNL', + key: 'address', + label: 'An address field', + openForms: { + components: { + city: { + validate: { + pattern: 'Amsterdam', + }, + translatedErrors: { + nl: {pattern: 'De stad moet Amsterdam zijn'}, + en: {pattern: 'The city must be Amsterdam'}, + }, + }, + }, + }, + }, + }, + play: async ({canvasElement, step}) => { + const canvas = within(canvasElement); + + await step('Navigate to validation tab and open City validation', async () => { + await userEvent.click(canvas.getByRole('link', {name: 'Validation'})); + await userEvent.click(canvas.getAllByText('City')[0]); + await waitFor(async () => { + const patternInput = canvas.getByLabelText( + 'Regular expression for city' + ) as HTMLInputElement; + expect(patternInput).toBeVisible(); + expect(patternInput.value).toBe('Amsterdam'); + + expect(await canvas.findByDisplayValue('pattern')).toBeVisible(); + expect(await canvas.findByDisplayValue('De stad moet Amsterdam zijn')).toBeVisible(); + + await userEvent.click(await canvas.findByText('EN')); + expect(await canvas.findByDisplayValue('pattern')).toBeVisible(); + expect(await canvas.findByDisplayValue('The city must be Amsterdam')).toBeVisible(); + }); + }); + }, +}; From 3ae0e774fe147e4083cf2ecf34a3f19c118d4f75 Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Thu, 15 Aug 2024 11:49:23 +0200 Subject: [PATCH 3/6] :label: Extract type information from schema definition * Use stricter types for the SubComponentValidation react component prop - not any string is accepted, it must be one of the two union members defined in the type * pass the correct validation schema to 'useManageValidatorsTranslations', using the subcomponent schemas defined in the type --- src/registry/addressNL/edit.tsx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/registry/addressNL/edit.tsx b/src/registry/addressNL/edit.tsx index 15374687..bdc3cf68 100644 --- a/src/registry/addressNL/edit.tsx +++ b/src/registry/addressNL/edit.tsx @@ -28,8 +28,17 @@ import {useErrorChecker} from '@/utils/errors'; import {EditFormDefinition} from '../types'; +/** + * Helper type to extract information from existing types. + */ +type AddressSubComponents = Required< + Required['openForms']>['components'] +>; +type PostcodeSchema = AddressSubComponents['postcode']; +type CitySchema = AddressSubComponents['city']; + export interface SubcomponentValidationProps { - component: string; + component: keyof AddressSubComponents; label: React.ReactNode; tooltip: string; placeholder: string; @@ -142,11 +151,12 @@ const EditForm: EditFormDefinition = () => { const [isKeyManuallySetRef, generatedKey] = useDeriveComponentKey(); const {hasAnyError} = useErrorChecker(); Validate.useManageValidatorsTranslations(['required']); - Validate.useManageValidatorsTranslations( + + Validate.useManageValidatorsTranslations( ['pattern'], `openForms.components.postcode.translatedErrors` ); - Validate.useManageValidatorsTranslations( + Validate.useManageValidatorsTranslations( ['pattern'], `openForms.components.city.translatedErrors` ); From ab472ef91f6aa0d863e044cc15fe31acaeb67407 Mon Sep 17 00:00:00 2001 From: Steven Bal Date: Fri, 16 Aug 2024 11:45:32 +0200 Subject: [PATCH 4/6] :ok_hand: [open-formulieren/open-forms#4420] PR feedback * use prefix for AddressNL SubcomponentValidation and useManageValidatorsTranslations * remove unnecessary waitFor blocks in AddressNL stories * add aria-label to datamap --- src/components/builder/validate/i18n.tsx | 11 ++-- src/components/formio/datamap.tsx | 13 ++++- src/registry/addressNL/edit.stories.ts | 68 ++++++++++-------------- src/registry/addressNL/edit.tsx | 21 +++++--- 4 files changed, 62 insertions(+), 51 deletions(-) diff --git a/src/components/builder/validate/i18n.tsx b/src/components/builder/validate/i18n.tsx index 9f87ca36..a19de2a5 100644 --- a/src/components/builder/validate/i18n.tsx +++ b/src/components/builder/validate/i18n.tsx @@ -2,7 +2,7 @@ import {PossibleValidatorErrorKeys, SchemaWithValidation} from '@open-formuliere import {useField} from 'formik'; import {isEqual} from 'lodash'; import {useContext, useEffect} from 'react'; -import {FormattedMessage, useIntl} from 'react-intl'; +import {FormattedMessage, defineMessage, useIntl} from 'react-intl'; import {BuilderContext} from '@/context'; @@ -10,10 +10,11 @@ import {DataMap, Panel, Tab, TabList, TabPanel, Tabs, TextField} from '../../for export function useManageValidatorsTranslations( keys: PossibleValidatorErrorKeys[], - field: string = 'translatedErrors' + prefix: string = '' ): void { + const fieldName = `${prefix}${prefix ? '.' : ''}translatedErrors`; const {supportedLanguageCodes} = useContext(BuilderContext); - const [{value}, , {setValue}] = useField(field); + const [{value}, , {setValue}] = useField(fieldName); useEffect(() => { const newValue = value @@ -68,6 +69,10 @@ const ValidationErrorTranslations = () => { defaultMessage="Error code" /> } + ariaLabelMessage={defineMessage({ + description: 'Accessible label for error message input field', + defaultMessage: 'Error message for "{key}"', + })} valueComponent={ = ({name, valueComponent, keyLabel = 'Key'}) => { +export const DataMap: React.FC = ({ + name, + valueComponent, + ariaLabelMessage, + keyLabel = 'Key', +}) => { + const intl = useIntl(); const [{value}, , {setValue}] = useField(name); const transformedValue = Object.entries(value).map(([key, value]) => ({key, value})); const columns = [keyLabel, valueComponent.props.label]; @@ -30,6 +38,9 @@ export const DataMap: React.FC = ({name, valueComponent, keyLabel const newValue = {...value, [item.key]: event.target.value}; setValue(newValue); }, + 'aria-label': ariaLabelMessage + ? intl.formatMessage(ariaLabelMessage, {key: item.key}) + : undefined, })} ))} diff --git a/src/registry/addressNL/edit.stories.ts b/src/registry/addressNL/edit.stories.ts index 8355648d..266b3724 100644 --- a/src/registry/addressNL/edit.stories.ts +++ b/src/registry/addressNL/edit.stories.ts @@ -35,13 +35,11 @@ export const PostcodeValidationTabWithoutConfiguration: Story = { await step('Navigate to validation tab and open Postcode validation', async () => { await userEvent.click(canvas.getByRole('link', {name: 'Validation'})); await userEvent.click(canvas.getAllByText('Postcode')[0]); - await waitFor(async () => { - expect(await canvas.findByText('Regular expression for postcode')).toBeVisible(); - expect(await canvas.findByText('NL')).toBeVisible(); - expect(await canvas.findByText('EN')).toBeVisible(); - expect(await canvas.findByText('Error code')).toBeVisible(); - expect(await canvas.findByDisplayValue('pattern')).toBeVisible(); - }); + expect(await canvas.findByLabelText('Regular expression for postcode')).toBeVisible(); + expect(await canvas.findByText('NL')).toBeVisible(); + expect(await canvas.findByText('EN')).toBeVisible(); + expect(await canvas.findByText('Error code')).toBeVisible(); + expect(canvas.getByLabelText('Error message for "pattern"')).toHaveDisplayValue(''); }); }, }; @@ -54,13 +52,11 @@ export const CityValidationTabWithoutConfiguration: Story = { await step('Navigate to validation tab and open City validation', async () => { await userEvent.click(canvas.getByRole('link', {name: 'Validation'})); await userEvent.click(canvas.getAllByText('City')[0]); - await waitFor(async () => { - expect(await canvas.findByText('Regular expression for city')).toBeVisible(); - expect(await canvas.findByText('NL')).toBeVisible(); - expect(await canvas.findByText('EN')).toBeVisible(); - expect(await canvas.findByText('Error code')).toBeVisible(); - expect(await canvas.findByDisplayValue('pattern')).toBeVisible(); - }); + expect(await canvas.findByLabelText('Regular expression for city')).toBeVisible(); + expect(await canvas.findByText('NL')).toBeVisible(); + expect(await canvas.findByText('EN')).toBeVisible(); + expect(await canvas.findByText('Error code')).toBeVisible(); + expect(canvas.getByLabelText('Error message for "pattern"')).toHaveDisplayValue(''); }); }, }; @@ -94,26 +90,23 @@ export const PostcodeValidationTabWithConfiguration: Story = { await step('Navigate to validation tab and open Postcode validation', async () => { await userEvent.click(canvas.getByRole('link', {name: 'Validation'})); await userEvent.click(canvas.getAllByText('Postcode')[0]); - await waitFor(async () => { - const patternInput = canvas.getByLabelText( - 'Regular expression for postcode' - ) as HTMLInputElement; - expect(patternInput).toBeVisible(); - expect(patternInput.value).toBe('1017 [A-Za-z]{2}'); + const patternInput = canvas.getByLabelText('Regular expression for postcode'); + expect(patternInput).toBeVisible(); + expect(patternInput).toHaveValue('1017 [A-Za-z]{2}'); - expect(await canvas.findByDisplayValue('pattern')).toBeVisible(); - expect(await canvas.findByDisplayValue('Postcode moet 1017 XX zijn')).toBeVisible(); + expect(await canvas.findByDisplayValue('pattern')).toBeVisible(); + expect(await canvas.findByDisplayValue('Postcode moet 1017 XX zijn')).toBeVisible(); - await userEvent.click(await canvas.findByText('EN')); - expect(await canvas.findByDisplayValue('pattern')).toBeVisible(); - expect(await canvas.findByDisplayValue('Postal code must be 1017 XX')).toBeVisible(); - }); + await userEvent.click(await canvas.findByText('EN')); + expect(canvas.getByLabelText('Error message for "pattern"')).toHaveDisplayValue( + 'Postal code must be 1017 XX' + ); }); }, }; export const CityValidationTabWithConfiguration: Story = { - name: 'AddressNL postcode validation tab (with prior configuration)', + name: 'AddressNL city validation tab (with prior configuration)', args: { component: { id: 'wekruya', @@ -141,20 +134,17 @@ export const CityValidationTabWithConfiguration: Story = { await step('Navigate to validation tab and open City validation', async () => { await userEvent.click(canvas.getByRole('link', {name: 'Validation'})); await userEvent.click(canvas.getAllByText('City')[0]); - await waitFor(async () => { - const patternInput = canvas.getByLabelText( - 'Regular expression for city' - ) as HTMLInputElement; - expect(patternInput).toBeVisible(); - expect(patternInput.value).toBe('Amsterdam'); + const patternInput = canvas.getByLabelText('Regular expression for city'); + expect(patternInput).toBeVisible(); + expect(patternInput).toHaveValue('Amsterdam'); - expect(await canvas.findByDisplayValue('pattern')).toBeVisible(); - expect(await canvas.findByDisplayValue('De stad moet Amsterdam zijn')).toBeVisible(); + expect(await canvas.findByDisplayValue('pattern')).toBeVisible(); + expect(await canvas.findByDisplayValue('De stad moet Amsterdam zijn')).toBeVisible(); - await userEvent.click(await canvas.findByText('EN')); - expect(await canvas.findByDisplayValue('pattern')).toBeVisible(); - expect(await canvas.findByDisplayValue('The city must be Amsterdam')).toBeVisible(); - }); + await userEvent.click(await canvas.findByText('EN')); + expect(canvas.getByLabelText('Error message for "pattern"')).toHaveDisplayValue( + 'The city must be Amsterdam' + ); }); }, }; diff --git a/src/registry/addressNL/edit.tsx b/src/registry/addressNL/edit.tsx index bdc3cf68..d87248f3 100644 --- a/src/registry/addressNL/edit.tsx +++ b/src/registry/addressNL/edit.tsx @@ -1,7 +1,7 @@ import {AddressNLComponentSchema} from '@open-formulieren/types'; import {TextField} from 'components/formio'; import {useContext} from 'react'; -import {FormattedMessage, useIntl} from 'react-intl'; +import {FormattedMessage, defineMessage, useIntl} from 'react-intl'; import { BuilderTabs, @@ -38,6 +38,7 @@ type PostcodeSchema = AddressSubComponents['postcode']; type CitySchema = AddressSubComponents['city']; export interface SubcomponentValidationProps { + prefix: string; component: keyof AddressSubComponents; label: React.ReactNode; tooltip: string; @@ -45,6 +46,7 @@ export interface SubcomponentValidationProps { } export const SubcomponentValidation: React.FC = ({ + prefix, component, label, tooltip, @@ -54,7 +56,7 @@ export const SubcomponentValidation: React.FC = ({ return ( <> = ({ {supportedLanguageCodes.map(code => ( } + ariaLabelMessage={defineMessage({ + description: 'Accessible label for error message input field', + defaultMessage: 'Error message for "{key}"', + })} valueComponent={ = () => { Validate.useManageValidatorsTranslations( ['pattern'], - `openForms.components.postcode.translatedErrors` - ); - Validate.useManageValidatorsTranslations( - ['pattern'], - `openForms.components.city.translatedErrors` + `openForms.components.postcode` ); + Validate.useManageValidatorsTranslations(['pattern'], `openForms.components.city`); return ( @@ -225,6 +228,7 @@ const EditForm: EditFormDefinition = () => { initialCollapsed > = () => { initialCollapsed > Date: Mon, 19 Aug 2024 16:26:23 +0200 Subject: [PATCH 5/6] :speech_balloon: [open-formulieren/open-forms#4420] Run makemessages --- i18n/messages/en.json | 55 +++++++++++++++++++++++++++++++++++++++++++ i18n/messages/nl.json | 55 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/i18n/messages/en.json b/i18n/messages/en.json index 2802a651..772ee91c 100644 --- a/i18n/messages/en.json +++ b/i18n/messages/en.json @@ -49,6 +49,16 @@ "description": "Tooltip for 'showInPDF' builder field", "originalDefault": "Whether to show this value in the confirmation PDF" }, + "0D+m56": { + "defaultMessage": "Regular expression for city", + "description": "Placeholder for 'validate.pattern' builder field", + "originalDefault": "Regular expression for city" + }, + "0FD2pY": { + "defaultMessage": "Validation for the postcode field", + "description": "Tooltip postcode field validation panel", + "originalDefault": "Validation for the postcode field" + }, "0OP7ho": { "defaultMessage": "Receives confirmation email", "description": "Label for 'confirmationRecipient' builder field", @@ -154,6 +164,11 @@ "description": "Label for 'delta.days' in relative delta date constraint validation", "originalDefault": "Days" }, + "4DrI94": { + "defaultMessage": "Regular expression for postcode", + "description": "Label for 'validate.pattern' builder field", + "originalDefault": "Regular expression for postcode" + }, "4HBnrF": { "defaultMessage": "Drag or select files to upload.", "description": "file component: drag/select files to upload text", @@ -774,6 +789,11 @@ "description": "Tooltip for 'prefill.identifierRole' builder field", "originalDefault": "In case that multiple identifiers are returned (in the case of eHerkenning bewindvoering and DigiD Machtigen), should the prefill data related to the main identifier be used, or that related to the authorised person?" }, + "WxwqZJ": { + "defaultMessage": "The regular expression pattern test that the postcode field value must pass before the form can be submitted.", + "description": "Tooltip for 'validate.pattern' builder field", + "originalDefault": "The regular expression pattern test that the postcode field value must pass before the form can be submitted." + }, "XMV3t2": { "defaultMessage": "Info", "description": "Label for content component CSS class 'info' option", @@ -929,6 +949,11 @@ "description": "'Remove item' screenreader button text", "originalDefault": "Remove item" }, + "dD9O3Q": { + "defaultMessage": "Regular expression for postcode", + "description": "Placeholder for 'validate.pattern' builder field", + "originalDefault": "Regular expression for postcode" + }, "dJNdhr": { "defaultMessage": "Cancel", "description": "Cancel component configuration button", @@ -1029,6 +1054,11 @@ "description": "Description for the 'openForms.itemsExpression' builder field", "originalDefault": "A JSON logic expression returning a variable (of array type) whose items should be used as the options for this component." }, + "fhVFUY": { + "defaultMessage": "City", + "description": "Title of city field validation panel", + "originalDefault": "City" + }, "gCQtSJ": { "defaultMessage": "Set the text of the 'Remove row' button.", "description": "Tooltip for 'removeRow' builder field", @@ -1049,6 +1079,11 @@ "description": "Fieldset preview content description", "originalDefault": "Fieldset content" }, + "hEVgKd": { + "defaultMessage": "Error message for \"{key}\"", + "description": "Accessible label for error message input field", + "originalDefault": "Error message for \"{key}\"" + }, "hJtTwo": { "defaultMessage": "Form", "description": "Component 'Rich' preview mode", @@ -1189,6 +1224,11 @@ "description": "Tooltip for option/choice description", "originalDefault": "Optionally provide additional information to explain the meaning of the option." }, + "mck25o": { + "defaultMessage": "Validation for the city field", + "description": "Tooltip city field validation panel", + "originalDefault": "Validation for the city field" + }, "mf9eF+": { "defaultMessage": "House number", "description": "Label for address housenumber", @@ -1254,6 +1294,11 @@ "description": "Label for addressNL city read only result", "originalDefault": "City" }, + "osl4X2": { + "defaultMessage": "The regular expression pattern test that the city field value must pass before the form can be submitted.", + "description": "Tooltip for 'validate.pattern' builder field", + "originalDefault": "The regular expression pattern test that the city field value must pass before the form can be submitted." + }, "p7g2h+": { "defaultMessage": "Add another", "description": "Add another option button label", @@ -1299,6 +1344,11 @@ "description": "Component edit form tab title for 'Registration' tab", "originalDefault": "Registration" }, + "rW1edF": { + "defaultMessage": "Postcode", + "description": "Title of postcode field validation panel", + "originalDefault": "Postcode" + }, "rkJLBr": { "defaultMessage": "'Add another' text", "description": "Label for 'addAnother' builder field", @@ -1324,6 +1374,11 @@ "description": "Edit grid preview content description", "originalDefault": "{groupLabel} 3" }, + "swKpQE": { + "defaultMessage": "Regular expression for city", + "description": "Label for 'validate.pattern' builder field", + "originalDefault": "Regular expression for city" + }, "tU1UVF": { "defaultMessage": "The earliest possible value that can be entered.", "description": "Tooltip for 'validate.minTime' builder field", diff --git a/i18n/messages/nl.json b/i18n/messages/nl.json index 6e6bbaa1..6270a48a 100644 --- a/i18n/messages/nl.json +++ b/i18n/messages/nl.json @@ -49,6 +49,16 @@ "description": "Tooltip for 'showInPDF' builder field", "originalDefault": "Whether to show this value in the confirmation PDF" }, + "0D+m56": { + "defaultMessage": "Regular expression for city", + "description": "Placeholder for 'validate.pattern' builder field", + "originalDefault": "Regular expression for city" + }, + "0FD2pY": { + "defaultMessage": "Validation for the postcode field", + "description": "Tooltip postcode field validation panel", + "originalDefault": "Validation for the postcode field" + }, "0OP7ho": { "defaultMessage": "Ontvangt bevestigingsmail", "description": "Label for 'confirmationRecipient' builder field", @@ -156,6 +166,11 @@ "description": "Label for 'delta.days' in relative delta date constraint validation", "originalDefault": "Days" }, + "4DrI94": { + "defaultMessage": "Regular expression for postcode", + "description": "Label for 'validate.pattern' builder field", + "originalDefault": "Regular expression for postcode" + }, "4HBnrF": { "defaultMessage": "Sleep of selecteer bestanden om te uploaden.", "description": "file component: drag/select files to upload text", @@ -784,6 +799,11 @@ "description": "Tooltip for 'prefill.identifierRole' builder field", "originalDefault": "In case that multiple identifiers are returned (in the case of eHerkenning bewindvoering and DigiD Machtigen), should the prefill data related to the main identifier be used, or that related to the authorised person?" }, + "WxwqZJ": { + "defaultMessage": "The regular expression pattern test that the postcode field value must pass before the form can be submitted.", + "description": "Tooltip for 'validate.pattern' builder field", + "originalDefault": "The regular expression pattern test that the postcode field value must pass before the form can be submitted." + }, "XMV3t2": { "defaultMessage": "Info", "description": "Label for content component CSS class 'info' option", @@ -941,6 +961,11 @@ "description": "'Remove item' screenreader button text", "originalDefault": "Remove item" }, + "dD9O3Q": { + "defaultMessage": "Regular expression for postcode", + "description": "Placeholder for 'validate.pattern' builder field", + "originalDefault": "Regular expression for postcode" + }, "dJNdhr": { "defaultMessage": "Annuleren", "description": "Cancel component configuration button", @@ -1042,6 +1067,11 @@ "description": "Description for the 'openForms.itemsExpression' builder field", "originalDefault": "A JSON logic expression returning a variable (of array type) whose items should be used as the options for this component." }, + "fhVFUY": { + "defaultMessage": "City", + "description": "Title of city field validation panel", + "originalDefault": "City" + }, "gCQtSJ": { "defaultMessage": "Geef de knoptekst om een groep te verwijderen op.", "description": "Tooltip for 'removeRow' builder field", @@ -1063,6 +1093,11 @@ "description": "Fieldset preview content description", "originalDefault": "Fieldset content" }, + "hEVgKd": { + "defaultMessage": "Error message for \"{key}\"", + "description": "Accessible label for error message input field", + "originalDefault": "Error message for \"{key}\"" + }, "hJtTwo": { "defaultMessage": "Formulierveld", "description": "Component 'Rich' preview mode", @@ -1205,6 +1240,11 @@ "description": "Tooltip for option/choice description", "originalDefault": "Optionally provide additional information to explain the meaning of the option." }, + "mck25o": { + "defaultMessage": "Validation for the city field", + "description": "Tooltip city field validation panel", + "originalDefault": "Validation for the city field" + }, "mf9eF+": { "defaultMessage": "Huisnummer", "description": "Label for address housenumber", @@ -1272,6 +1312,11 @@ "description": "Label for addressNL city read only result", "originalDefault": "City" }, + "osl4X2": { + "defaultMessage": "The regular expression pattern test that the city field value must pass before the form can be submitted.", + "description": "Tooltip for 'validate.pattern' builder field", + "originalDefault": "The regular expression pattern test that the city field value must pass before the form can be submitted." + }, "p7g2h+": { "defaultMessage": "Nog één toevoegen", "description": "Add another option button label", @@ -1317,6 +1362,11 @@ "description": "Component edit form tab title for 'Registration' tab", "originalDefault": "Registration" }, + "rW1edF": { + "defaultMessage": "Postcode", + "description": "Title of postcode field validation panel", + "originalDefault": "Postcode" + }, "rkJLBr": { "defaultMessage": "'Groep toevoegen'-tekst", "description": "Label for 'addAnother' builder field", @@ -1343,6 +1393,11 @@ "isTranslated": true, "originalDefault": "{groupLabel} 3" }, + "swKpQE": { + "defaultMessage": "Regular expression for city", + "description": "Label for 'validate.pattern' builder field", + "originalDefault": "Regular expression for city" + }, "tU1UVF": { "defaultMessage": "De minimale tijd die gekozen kan worden.", "description": "Tooltip for 'validate.minTime' builder field", From d54080810604925e1dd3361637fb0bc9f929104a Mon Sep 17 00:00:00 2001 From: Steven Bal Date: Tue, 20 Aug 2024 12:14:10 +0200 Subject: [PATCH 6/6] :white_check_mark: Make AddressNL storybook tests more robust by replacing getByLabelText with findByLabelText after userEvent.click(...) --- src/registry/addressNL/edit.stories.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/registry/addressNL/edit.stories.ts b/src/registry/addressNL/edit.stories.ts index 266b3724..65b61376 100644 --- a/src/registry/addressNL/edit.stories.ts +++ b/src/registry/addressNL/edit.stories.ts @@ -1,5 +1,5 @@ import {Meta, StoryObj} from '@storybook/react'; -import {expect, userEvent, waitFor, within} from '@storybook/test'; +import {expect, userEvent, within} from '@storybook/test'; import ComponentEditForm from '@/components/ComponentEditForm'; @@ -39,7 +39,8 @@ export const PostcodeValidationTabWithoutConfiguration: Story = { expect(await canvas.findByText('NL')).toBeVisible(); expect(await canvas.findByText('EN')).toBeVisible(); expect(await canvas.findByText('Error code')).toBeVisible(); - expect(canvas.getByLabelText('Error message for "pattern"')).toHaveDisplayValue(''); + const errorMessageInput = await canvas.findByLabelText('Error message for "pattern"'); + expect(errorMessageInput).toHaveDisplayValue(''); }); }, }; @@ -56,7 +57,8 @@ export const CityValidationTabWithoutConfiguration: Story = { expect(await canvas.findByText('NL')).toBeVisible(); expect(await canvas.findByText('EN')).toBeVisible(); expect(await canvas.findByText('Error code')).toBeVisible(); - expect(canvas.getByLabelText('Error message for "pattern"')).toHaveDisplayValue(''); + const errorMessageInput = await canvas.findByLabelText('Error message for "pattern"'); + expect(errorMessageInput).toHaveDisplayValue(''); }); }, }; @@ -90,7 +92,7 @@ export const PostcodeValidationTabWithConfiguration: Story = { await step('Navigate to validation tab and open Postcode validation', async () => { await userEvent.click(canvas.getByRole('link', {name: 'Validation'})); await userEvent.click(canvas.getAllByText('Postcode')[0]); - const patternInput = canvas.getByLabelText('Regular expression for postcode'); + const patternInput = await canvas.findByLabelText('Regular expression for postcode'); expect(patternInput).toBeVisible(); expect(patternInput).toHaveValue('1017 [A-Za-z]{2}'); @@ -98,9 +100,8 @@ export const PostcodeValidationTabWithConfiguration: Story = { expect(await canvas.findByDisplayValue('Postcode moet 1017 XX zijn')).toBeVisible(); await userEvent.click(await canvas.findByText('EN')); - expect(canvas.getByLabelText('Error message for "pattern"')).toHaveDisplayValue( - 'Postal code must be 1017 XX' - ); + const errorMessageInput = await canvas.findByLabelText('Error message for "pattern"'); + expect(errorMessageInput).toHaveDisplayValue('Postal code must be 1017 XX'); }); }, }; @@ -134,7 +135,7 @@ export const CityValidationTabWithConfiguration: Story = { await step('Navigate to validation tab and open City validation', async () => { await userEvent.click(canvas.getByRole('link', {name: 'Validation'})); await userEvent.click(canvas.getAllByText('City')[0]); - const patternInput = canvas.getByLabelText('Regular expression for city'); + const patternInput = await canvas.findByLabelText('Regular expression for city'); expect(patternInput).toBeVisible(); expect(patternInput).toHaveValue('Amsterdam'); @@ -142,9 +143,8 @@ export const CityValidationTabWithConfiguration: Story = { expect(await canvas.findByDisplayValue('De stad moet Amsterdam zijn')).toBeVisible(); await userEvent.click(await canvas.findByText('EN')); - expect(canvas.getByLabelText('Error message for "pattern"')).toHaveDisplayValue( - 'The city must be Amsterdam' - ); + const errorMessageInput = await canvas.findByLabelText('Error message for "pattern"'); + expect(errorMessageInput).toHaveDisplayValue('The city must be Amsterdam'); }); }, };