Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[@mantine/form] Add superstruct support #2957

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 10 additions & 1 deletion docs/src/docs/form/validation.mdx
Expand Up @@ -60,7 +60,12 @@ from the validation results.

## Schema based validation

`@mantine/form` supports schema validation with [zod](https://www.npmjs.com/package/zod), [joi](https://www.npmjs.com/package/joi) and [yup](https://www.npmjs.com/package/yup) out of the box.
Out of the box `@mantine/form` supports schema validation with:
- [zod](https://www.npmjs.com/package/zod)
- [joi](https://www.npmjs.com/package/joi)
- [yup](https://www.npmjs.com/package/yup)
- [superstruct](https://www.npmjs.com/package/superstruct)

You will need to install one of the libraries yourself, as `@mantine/form` package does not depend on any of them.
If you do not know what validation library to choose, we recommend choosing [zod](https://www.npmjs.com/package/zod)
as the most modern and developer-friendly library.
Expand All @@ -77,6 +82,10 @@ as the most modern and developer-friendly library.

<Demo data={FormDemos.yup} demoProps={{ toggle: true }} />

### superstruct

<Demo data={FormDemos.superstruct} demoProps={{ toggle: true }} />

## Validate fields on change

To validate all fields on change set `validateInputOnChange` option to `true`:
Expand Down
65 changes: 65 additions & 0 deletions src/mantine-demos/src/demos/form/Form.demo.superstruct.tsx
@@ -0,0 +1,65 @@
/* eslint-disable no-console */
import { MantineDemo } from '@mantine/ds';
import { SchemaBase } from './_schema-base';

const code = `
import { useForm, superstructResolver } from '@mantine/form';
import { NumberInput, TextInput, Button, Box, Group } from '@mantine/core';
import * as s from 'superstruct';
import isEmail from 'is-email';

const emailString = s.define('email', isEmail);
const schema = s.object({
name: s.size(s.string(), 2, 30),
email: emailString,
age: s.min(s.number(), 18)
});

function Demo() {
const form = useForm({
validate: superstructResolver(schema),
initialValues: {
name: '',
email: '',
age: 18
}
});

return (
<Box sx={{ maxWidth: 340 }} mx="auto">
<form onSubmit={form.onSubmit((values) => console.log(values))}>
<TextInput
withAsterisk
label="Email"
placeholder="example@mail.com"
{...form.getInputProps('email')}
/>
<TextInput
withAsterisk
label="Name"
placeholder="John Doe"
mt="sm"
{...form.getInputProps('name')}
/>
<NumberInput
withAsterisk
label="Age"
placeholder="Your age"
mt="sm"
{...form.getInputProps('age')}
/>

<Group position="right" mt="xl">
<Button type="submit">Submit</Button>
</Group>
</form>
</Box>
);
}
`;

export const superstruct: MantineDemo = {
type: 'demo',
component: SchemaBase,
code,
};
1 change: 1 addition & 0 deletions src/mantine-form/src/index.ts
Expand Up @@ -3,6 +3,7 @@ export { createFormContext } from './FormProvider/FormProvider';
export { FORM_INDEX } from './form-index';

export { zodResolver } from './resolvers/zod-resolver/zod-resolver';
export { superstructResolver } from './resolvers/superstruct-resolver/superstruct-resolver';
export { yupResolver } from './resolvers/yup-resolver/yup-resolver';
export { joiResolver } from './resolvers/joi-resolver/joi-resolver';

Expand Down
@@ -0,0 +1,36 @@
import type { FormErrors } from '../../types';

type StructFailure = {
value: any;
key: any;
type: string;
refinement: string | undefined;
message: string;
explanation?: string;
branch: Array<any>;
path: Array<any>;
};

type StructValidaationError = {
failures: () => Array<StructFailure>;
};

export function superstructResolver(schema: any) {
function structValidation(values: Record<string, any>): FormErrors {
const formErrors: FormErrors = {};

const [err]: [StructValidaationError | null, unknown] = schema.validate(values);
if (!err) {
return formErrors;
}

err.failures().forEach((fieldFailure) => {
const fieldName = fieldFailure.path.join(' ');
formErrors[fieldFailure.path.join('.')] = `${fieldName}: ${fieldFailure.message}`;
});

return formErrors;
}

return structValidation;
}