@open-formulieren/formio-builder
Version:
An opinionated Formio webform builder for Open Forms
99 lines (98 loc) • 3.66 kB
JavaScript
/**
* 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;
};