Skip to content

Commit

Permalink
✨ [open-formulieren/open-forms#4420] Field validation for AddressNL
Browse files Browse the repository at this point in the history
the following components can now be validated using regex:
* postcode
* city
  • Loading branch information
stevenbal committed Jul 30, 2024
1 parent 9cda8f3 commit 78c9ade
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 4 deletions.
10 changes: 10 additions & 0 deletions src/components/ComponentConfiguration.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2222,6 +2222,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',
Expand Down
6 changes: 3 additions & 3 deletions src/components/builder/validate/i18n.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import {BuilderContext} from '@/context';
import {DataMap, Panel, Tab, TabList, TabPanel, Tabs, TextField} from '../../formio';

export function useManageValidatorsTranslations<S extends SchemaWithValidation>(
keys: PossibleValidatorErrorKeys<S>[]
keys: PossibleValidatorErrorKeys<S>[],
field: string = 'translatedErrors'
): void {
const {supportedLanguageCodes} = useContext(BuilderContext);
const [{value}, , {setValue}] = useField<S['translatedErrors']>('translatedErrors');
const [{value}, , {setValue}] = useField<S['translatedErrors']>(field);

// set any missing translations
useEffect(() => {
const newValue = value
? {...value}
Expand Down
146 changes: 145 additions & 1 deletion src/registry/addressNL/edit.tsx
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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;
}

const SubcomponentValidation: React.FC<SubcomponentValidationProps> = ({
component,
label,
tooltip,
placeholder,
}) => {
const {supportedLanguageCodes} = useContext(BuilderContext);
return (
<>
<TextField
name={`ofComponents.${component}.validate.pattern`}
label={label}
tooltip={tooltip}
placeholder={placeholder}
/>
<Tabs>
<TabList>
{supportedLanguageCodes.map(code => (
<Tab key={code}>{code.toUpperCase()}</Tab>
))}
</TabList>

{supportedLanguageCodes.map(code => (
<TabPanel key={code}>
<DataMap
name={`ofComponents.${component}.translatedErrors.${code}`}
keyLabel={
<FormattedMessage
description="Label for translation of validation error code"
defaultMessage="Error code"
/>
}
valueComponent={
<TextField
name="message"
label={
<FormattedMessage
description="Label for translation message for validation error code"
defaultMessage="Error message"
/>
}
/>
}
/>
</TabPanel>
))}
</Tabs>
</>
);
};

const DeriveAddress = () => {
const intl = useIntl();
const tooltip = intl.formatMessage({
Expand Down Expand Up @@ -81,6 +142,9 @@ const EditForm: EditFormDefinition<AddressNLComponentSchema> = () => {
const [isKeyManuallySetRef, generatedKey] = useDeriveComponentKey();
const {hasAnyError} = useErrorChecker<AddressNLComponentSchema>();
Validate.useManageValidatorsTranslations<AddressNLComponentSchema>(['required']);
Validate.useManageValidatorsTranslations(['pattern'], `ofComponents.postcode.translatedErrors`);
Validate.useManageValidatorsTranslations(['pattern'], `ofComponents.city.translatedErrors`);

return (
<Tabs>
<TabList>
Expand Down Expand Up @@ -128,6 +192,76 @@ const EditForm: EditFormDefinition<AddressNLComponentSchema> = () => {
<Validate.Required />
<Validate.ValidatorPluginSelect />
<Validate.ValidationErrorTranslations />

{/* Postcode field validation */}
<Panel
title={
<FormattedMessage
description="Title of postcode field validation panel"
defaultMessage="Postcode"
/>
}
tooltip={intl.formatMessage({
description: 'Tooltip postcode field validation panel',
defaultMessage: 'Validation for the postcode field',
})}
collapsible
initialCollapsed
>
<SubcomponentValidation
component="postcode"
label={
<FormattedMessage
description="Label for 'validate.pattern' builder field"
defaultMessage="Regular expression for postcode"
/>
}
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',
})}
/>
</Panel>

{/* City field validation */}
<Panel
title={
<FormattedMessage
description="Title of city field validation panel"
defaultMessage="City"
/>
}
tooltip={intl.formatMessage({
description: 'Tooltip city field validation panel',
defaultMessage: 'Validation for the city field',
})}
collapsible
initialCollapsed
>
<SubcomponentValidation
component="city"
label={
<FormattedMessage
description="Label for 'validate.pattern' builder field"
defaultMessage="Regular expression for city"
/>
}
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',
})}
/>
</Panel>
</TabPanel>

{/* Registration tab */}
Expand Down Expand Up @@ -192,6 +326,16 @@ EditForm.defaultValues = {
registration: {
attribute: '',
},
ofComponents: {

Check failure on line 329 in src/registry/addressNL/edit.tsx

View workflow job for this annotation

GitHub Actions / Create 'production' build

Type '{ label: string; key: string; description: string; tooltip: string; showInSummary: true; showInEmail: false; showInPDF: true; hidden: false; clearOnHide: true; isSensitiveData: true; deriveAddress: false; layout: "doubleColumn"; defaultValue: { postcode: string; houseNumber: string; houseLetter: string; houseNumberAddition: string; }; conditional: { show: undefined; when: string; eq: string; }; validate: { required: false; plugins: never[]; }; translatedErrors: {}; registration: { attribute: string; }; ofComponents: { postcode: { validate: { pattern: string; }; translatedErrors: {}; }; city: { validate: { pattern: string; }; translatedErrors: {}; }; }; }' is not assignable to type 'Omit<AddressNLComponentSchema, "type" | "id">'.
postcode: {
validate: {pattern: ''},
translatedErrors: {},
},
city: {
validate: {pattern: ''},
translatedErrors: {},
},
},
};

export default EditForm;

0 comments on commit 78c9ade

Please sign in to comment.