212 lines
5.2 KiB
TypeScript
212 lines
5.2 KiB
TypeScript
import React from 'react'
|
|
import type { Meta, StoryObj } from '@storybook/react'
|
|
import { useForm } from 'react-hook-form'
|
|
import { zodResolver } from '@hookform/resolvers/zod'
|
|
import { z } from 'zod'
|
|
|
|
import {
|
|
Form,
|
|
FormField,
|
|
FormItem,
|
|
FormLabel,
|
|
FormControl,
|
|
FormDescription,
|
|
FormMessage,
|
|
} from '@/components/ui/form'
|
|
import { Input } from '@/components/ui/input'
|
|
import { Button } from '@/components/ui/button'
|
|
|
|
const meta = {
|
|
title: 'Form/Form',
|
|
component: Form,
|
|
tags: ['autodocs'],
|
|
parameters: { layout: 'centered' },
|
|
} satisfies Meta<typeof Form>
|
|
|
|
export default meta
|
|
type Story = StoryObj<typeof meta>
|
|
|
|
const profileSchema = z.object({
|
|
username: z
|
|
.string()
|
|
.min(2, { message: 'Username must be at least 2 characters.' })
|
|
.max(30, { message: 'Username must not be longer than 30 characters.' }),
|
|
email: z.string().email({ message: 'Please enter a valid email address.' }),
|
|
})
|
|
|
|
type ProfileValues = z.infer<typeof profileSchema>
|
|
|
|
function ProfileForm() {
|
|
const form = useForm<ProfileValues>({
|
|
resolver: zodResolver(profileSchema),
|
|
defaultValues: {
|
|
username: '',
|
|
email: '',
|
|
},
|
|
})
|
|
|
|
function onSubmit(data: ProfileValues) {
|
|
alert(JSON.stringify(data, null, 2))
|
|
}
|
|
|
|
return (
|
|
<Form {...form}>
|
|
<form onSubmit={form.handleSubmit(onSubmit)} className="w-100 space-y-6">
|
|
<FormField
|
|
control={form.control}
|
|
name="username"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Username</FormLabel>
|
|
<FormControl>
|
|
<Input placeholder="johndoe" {...field} />
|
|
</FormControl>
|
|
<FormDescription>
|
|
This is your public display name.
|
|
</FormDescription>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name="email"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Email</FormLabel>
|
|
<FormControl>
|
|
<Input type="email" placeholder="john@example.com" {...field} />
|
|
</FormControl>
|
|
<FormDescription>
|
|
We will never share your email with anyone.
|
|
</FormDescription>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<Button type="submit">Submit</Button>
|
|
</form>
|
|
</Form>
|
|
)
|
|
}
|
|
|
|
export const Default: Story = {
|
|
render: () => <ProfileForm />,
|
|
}
|
|
|
|
const loginSchema = z.object({
|
|
email: z.string().email({ message: 'Invalid email address.' }),
|
|
password: z
|
|
.string()
|
|
.min(8, { message: 'Password must be at least 8 characters.' }),
|
|
})
|
|
|
|
type LoginValues = z.infer<typeof loginSchema>
|
|
|
|
function LoginForm() {
|
|
const form = useForm<LoginValues>({
|
|
resolver: zodResolver(loginSchema),
|
|
defaultValues: {
|
|
email: '',
|
|
password: '',
|
|
},
|
|
})
|
|
|
|
function onSubmit(data: LoginValues) {
|
|
alert(JSON.stringify(data, null, 2))
|
|
}
|
|
|
|
return (
|
|
<Form {...form}>
|
|
<form onSubmit={form.handleSubmit(onSubmit)} className="w-100 space-y-6">
|
|
<FormField
|
|
control={form.control}
|
|
name="email"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Email</FormLabel>
|
|
<FormControl>
|
|
<Input type="email" placeholder="you@example.com" {...field} />
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name="password"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Password</FormLabel>
|
|
<FormControl>
|
|
<Input type="password" placeholder="Enter password" {...field} />
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<Button type="submit" className="w-full">
|
|
Sign in
|
|
</Button>
|
|
</form>
|
|
</Form>
|
|
)
|
|
}
|
|
|
|
export const Login: Story = {
|
|
render: () => <LoginForm />,
|
|
}
|
|
|
|
function PrefilledErrorForm() {
|
|
const form = useForm<ProfileValues>({
|
|
resolver: zodResolver(profileSchema),
|
|
defaultValues: {
|
|
username: 'a',
|
|
email: 'not-an-email',
|
|
},
|
|
})
|
|
|
|
// Trigger validation on mount
|
|
React.useEffect(() => {
|
|
form.trigger()
|
|
}, [form])
|
|
|
|
return (
|
|
<Form {...form}>
|
|
<form className="w-100 space-y-6">
|
|
<FormField
|
|
control={form.control}
|
|
name="username"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Username</FormLabel>
|
|
<FormControl>
|
|
<Input {...field} />
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name="email"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Email</FormLabel>
|
|
<FormControl>
|
|
<Input type="email" {...field} />
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<Button type="submit">Submit</Button>
|
|
</form>
|
|
</Form>
|
|
)
|
|
}
|
|
|
|
export const WithErrors: Story = {
|
|
render: () => <PrefilledErrorForm />,
|
|
}
|