UNPKG

@open-formulieren/formio-builder

Version:

An opinionated Formio webform builder for Open Forms

99 lines (98 loc) 3.66 kB
/** * Zod schemas for builder form validation. * * TODO: check the zodErrorMap implementation & patterns in the SDK for a default error * map. */ import { defineMessages } from 'react-intl'; import { z } from 'zod'; /* Validation message definitions. */ const DEFAULT_MESSAGES = defineMessages({ required: { id: "dazu9j", defaultMessage: [{ type: 0, value: "Label is a required field." }] }, invalidKeyPattern: { id: "h0B9Fr", defaultMessage: [{ type: 0, value: "The property name must only contain alphanumeric characters, underscores, dots and dashes and should not be ended by dash or dot." }] } }); /* Public API */ /** * Construct the localised validation schema for the component.label property. * * Component label is (typically) a required text for Form.io components. */ export const buildLabelSchema = (intl) => z.string({ errorMap: getErrorMap((issue, ctx) => { const isRequiredError = issue.code === 'invalid_type' && ctx.data === undefined; if (isRequiredError) { return intl.formatMessage(DEFAULT_MESSAGES.required); } return; }), }); /** * Construct the localised validation schema for the component.key property. * * The component key is used in the submission data and support lodash.get/set patterns. * It must allow slug-characters, amended with underscores and dots for nested data * lookups/setters. It may not end with a dash or dot for nested-data reasons. */ export const buildKeySchema = (intl) => z .string({ errorMap: getErrorMap(issue => { if (isInvalidStringIssue(issue) && issue.validation === 'regex') { return intl.formatMessage(DEFAULT_MESSAGES.invalidKeyPattern); } return; }), }) .regex(new RegExp('^(\\w|\\w[\\w-.]*\\w)$')); /** * Construct the localised validation schema shared by (most) Form.io components. * * You can use the output of this base schema with the `.and(...)` chain for additional * component-type specific schema validations that are not/less generic in nature. */ export const buildCommonSchema = (intl) => z.object({ label: buildLabelSchema(intl), key: buildKeySchema(intl), }); // From https://zod.dev/?id=json-type const literalSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]); export const jsonSchema = z.lazy(() => z.union([literalSchema, z.array(jsonSchema), z.record(jsonSchema)])); // Maps to @open-formulieren/types common.ts Option type. const optionTranslationSchema = z.object({ label: z.string().optional(), }); export const optionSchema = (intl) => z.object({ value: z .string({ required_error: intl.formatMessage({ id: "BKe/DT", defaultMessage: [{ type: 0, value: "The option value is a required field." }] }), }) .min(1), label: z .string({ required_error: intl.formatMessage({ id: "arAMAF", defaultMessage: [{ type: 0, value: "The option label is a required field." }] }), }) .min(1), openForms: z .object({ // zod doesn't seem to be able to use our supportedLanguageCodes for z.object keys, // they need to be defined statically. So, 'record' it is. translations: z.record(z.string(), optionTranslationSchema.optional()), }) .optional(), }); /* Helpers */ export const isInvalidStringIssue = (issue) => { return issue.code === 'invalid_string'; }; export const getErrorMap = (builder) => { const errorMap = (issue, ctx) => { const message = builder(issue, ctx); if (message) { return { message }; } return { message: ctx.defaultError }; }; return errorMap; };