Skip to content

Commit 5662004

Browse files
feat: Integrate Joi validation and fix: live demo error (#161) (#164)
1 parent 351fad3 commit 5662004

32 files changed

+882
-19
lines changed

apps/docs/components/landing/interactive-demo.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,21 @@ function InteractiveDemo() {
1919
name: z.string(),
2020
age: z.coerce.number(),
2121
isHuman: z.boolean(),
22-
}),
22+
})
2323
);
2424
const [schemaProvider, setSchemaProvider] = React.useState<SchemaProvider>(
25-
() => new ZodProvider(schema),
25+
() => new ZodProvider(schema)
2626
);
2727
const [data, setData] = useState("");
2828

2929
useEffect(() => {
3030
try {
3131
const z = globalZod;
3232
const parsedSchema = eval(code);
33+
const provider = new ZodProvider(parsedSchema);
34+
provider.parseSchema();
3335
setSchema(parsedSchema);
34-
setSchemaProvider(new ZodProvider(parsedSchema));
36+
setSchemaProvider(provider);
3537
} catch (error) {
3638
console.error(error);
3739
}
@@ -56,7 +58,9 @@ function InteractiveDemo() {
5658
/>
5759

5860
{data && (
59-
<pre className="bg-gray-100 p-4 rounded-lg text-sm mt-4">{data}</pre>
61+
<pre className="bg-gray-100 p-4 rounded-lg text-sm mt-4 text-gray-800">
62+
{data}
63+
</pre>
6064
)}
6165
</div>
6266
</div>

apps/docs/components/landing/sections/hero.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"use client";
2+
import { useState, useEffect } from "react";
23
import { Badge } from "@/components/ui/badge";
34
import { Button } from "@/components/ui/button";
45
import { ArrowRight } from "lucide-react";
@@ -8,7 +9,17 @@ import Link from "next/link";
89
import InteractiveDemo from "../interactive-demo";
910

1011
export const HeroSection = () => {
12+
const [isMounted, setIsMounted] = useState(false);
1113
const { theme } = useTheme();
14+
15+
useEffect(() => {
16+
setIsMounted(true);
17+
}, []);
18+
19+
if (!isMounted) {
20+
return null;
21+
}
22+
1223
return (
1324
<section className="container w-full">
1425
<div className="grid place-items-center lg:max-w-screen-xl gap-8 mx-auto py-20 md:py-32">

apps/docs/pages/docs/react/api.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## schema: SchemaProvider
44

5-
The `schema` prop is used to pass the schema to the form. It can be a `ZodProvider` or a `YupProvider`.
5+
The `schema` prop is used to pass the schema to the form. It can be a `ZodProvider`, `YupProvider` or `JoiProvider`.
66

77
## onSubmit?: (values: T, form: FormInstance) => void
88

apps/docs/pages/docs/react/customization.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@ The customization of the components is done by providing a `fieldConfig` to your
44

55
With zod, you can use the `superRefine` method to add a `fieldConfig` to your schema field. For more information, see the [Zod documentation](/docs/schema/zod).
66

7-
With yup, you can use the `transform` method to add a `fieldConfig` to your schema field. For more information, see the [Yup documentation](/docs/schema/yup). In these examples, we will use Zod.
7+
With yup, you can use the `transform` method to add a `fieldConfig` to your schema field. For more information, see the [Yup documentation](/docs/schema/yup).
88

9-
You should import `buildZodFieldConfig` or `buildYupFieldConfig` from `@autoform/react` and customize it.
9+
With joi, you can use the `meta` method to add a `fieldConfig` to your schema field. For more information, see the [Joi documentation](/docs/schema/joi).
10+
11+
In these examples, we will use Zod.
12+
13+
Depending on the validation library you are using, import `buildZodFieldConfig`, `buildYupFieldConfig`, or `buildJoiFieldConfig` from `@autoform/react` and customize it.
1014

1115
```tsx
1216
import * as z from "zod";

apps/docs/pages/docs/react/getting-started.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,5 +173,6 @@ All children passed to the `AutoForm` component will be rendered below the form.
173173

174174
- [How to define your zod schema](/docs/schema/zod)
175175
- [How to define your yup schema](/docs/schema/yup)
176+
- [How to define your joi schema](/docs/schema/joi)
176177
- [How to customize your form](/docs/react/customization)
177178
- [How to use AutoForm with other UI libraries](/docs/react/custom-integration)

apps/docs/pages/docs/schema/joi.md

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
# Joi
2+
3+
Basic usage:
4+
5+
```tsx
6+
"use client";
7+
import { JoiProvider } from "@autoform/joi";
8+
import Joi from "joi";
9+
import { buildJoiFieldConfig } from "@autoform/react";
10+
import { AutoForm, FieldTypes } from "@autoform/mui"; // use any UI library
11+
12+
const fieldConfig = buildJoiFieldConfig<
13+
FieldTypes,
14+
{
15+
// You can define custom props here
16+
isImportant?: boolean;
17+
}
18+
>();
19+
20+
// Define your form schema using Joi
21+
const joiFormSchema = Joi.object({
22+
// Use the "label" method to set the label
23+
// You can set a default value
24+
name: Joi.string().required().label("Your Name").default("John Doe"),
25+
26+
age: Joi.number().required().positive().integer().messages({
27+
"any.required":
28+
"We need your age to verify you're old enough to use this form",
29+
}),
30+
31+
email: Joi.string()
32+
.email({ tlds: { allow: false } })
33+
// You can add additional config for a field using fieldConfig
34+
.meta(
35+
fieldConfig({
36+
inputProps: {
37+
type: "email",
38+
},
39+
customData: {
40+
// You can add custom data here
41+
isImportant: true,
42+
},
43+
})
44+
),
45+
46+
website: Joi.string().uri().allow(null),
47+
48+
// Date will show a date picker
49+
birthday: Joi.date().optional(),
50+
51+
// You can use arrays and sub-objects
52+
guests: Joi.array().items(
53+
Joi.object({
54+
name: Joi.string().required(),
55+
})
56+
),
57+
hobbies: Joi.array().items(Joi.string()),
58+
59+
// You can use enums, will show a select field
60+
color: Joi.any().valid("red", "green", "blue"),
61+
});
62+
63+
export const joiSchemaProvider = new JoiProvider(joiFormSchema);
64+
65+
function App() {
66+
return (
67+
<AutoForm
68+
schema={joiSchemaProvider}
69+
onSubmit={(data) => {
70+
console.log(data);
71+
}}
72+
withSubmit
73+
/>
74+
);
75+
}
76+
```
77+
78+
### Joi configuration
79+
80+
#### Validations
81+
82+
Your form schema can use any of Joi's validation methods.
83+
84+
#### Label
85+
86+
You can use the `label` method to set a label for each field. If no label is set, the field name will be used and un-camel-cased.
87+
88+
```tsx
89+
const formSchema = Joi.object({
90+
username: Joi.string().label("Your username"),
91+
someValue: Joi.string(), // Will be "Some Value"
92+
});
93+
```
94+
95+
#### Default values
96+
97+
You can set a default value for a field using the `default` method.
98+
99+
```tsx
100+
const formSchema = Joi.object({
101+
favouriteNumber: Joi.number().default(5),
102+
});
103+
```
104+
105+
If you want to set default value of date, convert it to Date first using `new Date(val)`.
106+
107+
#### Select/Enums
108+
109+
You can use `any().valid` to create a select field.
110+
111+
```tsx
112+
enum BreadTypes {
113+
White = "White bread",
114+
Brown = "Brown bread",
115+
Wholegrain = "Wholegrain bread",
116+
Other = "Other",
117+
}
118+
119+
const formSchema = Joi.object({
120+
breadType: Joi.any().valid(...Object.values(BreadTypes)),
121+
});
122+
```
123+
124+
#### Arrays
125+
126+
AutoForm supports arrays:
127+
128+
```tsx
129+
const formSchema = Joi.object({
130+
invitedGuests: Joi.array()
131+
.items(
132+
// Define the fields for each item
133+
Joi.object({
134+
name: Joi.string(),
135+
age: Joi.number(),
136+
})
137+
)
138+
// Optionally set a custom label - otherwise this will be inferred from the field name
139+
.label("Guests invited to the party"),
140+
hobbies: Joi.array().items(Joi.string()),
141+
});
142+
```
143+
144+
Arrays are not supported as the root element of the form schema.
145+
146+
You also can set default value of an array using .default(), but please make sure the array element has same structure with the schema.
147+
148+
```tsx
149+
// Add array default value example
150+
const formSchema = Joi.object({
151+
invitedGuests: Joi.array()
152+
.items(
153+
Joi.object({
154+
name: Joi.string().required(),
155+
age: Joi.number(),
156+
})
157+
)
158+
.default([
159+
{ name: "John", age: 24 },
160+
{ name: "Jane", age: 20 },
161+
])
162+
.label("Guests invited to the party"),
163+
});
164+
```
165+
166+
#### Sub-objects
167+
168+
You may use sub-objects to group fields together. These will be rendered with their own title.
169+
170+
```tsx
171+
const formSchema = Joi.object({
172+
guestDetails: Joi.object({
173+
name: Joi.string(),
174+
age: Joi.number(),
175+
}),
176+
});
177+
```
178+
179+
#### Field configuration
180+
181+
You can use the `fieldConfig` function to set additional configuration for how a field should be rendered. This function is independent of the UI library you use so you can provide the FieldTypes that are supported by your UI library.
182+
183+
It's recommended that you create your own fieldConfig function that uses the base fieldConfig function from `@autoform/react` and adds your own customizations:
184+
185+
```tsx
186+
import { buildJoiFieldConfig } from "@autoform/react";
187+
import { FieldTypes } from "@autoform/mui";
188+
189+
const fieldConfig = buildJoiFieldConfig<
190+
FieldTypes, // You should provide the "FieldTypes" type from the UI library you use
191+
{
192+
isImportant?: boolean; // You can add custom props here
193+
}
194+
>();
195+
```

apps/docs/pages/docs/technical/todo.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { Callout } from "nextra/components";
2525
- Schema libraries
2626
- [x] Zod
2727
- [x] Yup
28-
- [ ] Joi
28+
- [x] Joi
2929
- [ ] Clean up documentation
3030
- [ ] Dependencies
3131
- [x] Custom field types via prop to AutoForm

apps/web/app/page.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use client";
22
import { Card, CardContent, Container, Typography } from "@mui/material";
3-
// import Ant from "../components/Ant";
4-
import Chakra from "components/Chakra";
3+
import Ant from "../components/Ant";
4+
// import Chakra from "components/Chakra";
55
// import Mantine from "components/Mantine";
66
// import Array from "components/Array";
77
// import Basics from "../components/Basics";
@@ -19,8 +19,8 @@ export default function Home() {
1919
{/* <Array /> */}
2020
{/* <Mantine /> */}
2121
{/* <Shadcn /> */}
22-
{/* <Ant /> */}
23-
<Chakra/>
22+
<Ant />
23+
{/* <Chakra /> */}
2424
</CardContent>
2525
</Card>
2626
</Container>

apps/web/components/Ant.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
"use client";
22
import { AutoForm } from "@autoform/ant";
3-
import { zodSchemaProvider } from "./utils";
3+
import { joiSchemaProvider } from "./utils";
44

55
const Ant = () => {
66
return (
77
<AutoForm
8-
schema={zodSchemaProvider}
8+
schema={joiSchemaProvider}
99
onSubmit={(data) => {
1010
console.log(JSON.stringify(data, null, 2));
1111
}}

0 commit comments

Comments
 (0)