11import { zodResolver } from '@hookform/resolvers/zod' ;
22import { DatePicker } from '@lambdacurry/forms/remix-hook-form/date-picker' ;
33import { Button } from '@lambdacurry/forms/ui/button' ;
4- import type { ActionFunctionArgs } from '@ remix-run/node ' ;
5- import { Form , useFetcher } from '@ remix-run/react ' ;
6- import type { Meta , StoryContext , StoryObj } from '@storybook/react' ;
7- import { expect , userEvent , waitFor , within } from '@storybook/test' ;
8- import { RemixFormProvider , getValidatedFormData , useRemixForm } from 'remix-hook-form' ;
4+ import type { ActionFunctionArgs } from '../lib/storybook/ remix-mock ' ;
5+ import { Form , useFetcher } from '../lib/storybook/ remix-mock ' ;
6+ import type { Meta , StoryObj } from '@storybook/react' ;
7+ import { expect , userEvent , within } from '@storybook/test' ;
8+ import { RemixFormProvider , createFormData , getValidatedFormData , useRemixForm } from 'remix-hook-form' ;
99import { z } from 'zod' ;
1010import { withRemixStubDecorator } from '../lib/storybook/remix-stub' ;
1111
1212const formSchema = z . object ( {
13- eventDate : z . coerce . date ( ) ,
13+ date : z . date ( {
14+ required_error : 'Please select a date' ,
15+ } ) ,
1416} ) ;
1517
1618type FormData = z . infer < typeof formSchema > ;
1719
18- const DatePickerExample = ( ) => {
19- const fetcher = useFetcher < { message ? : string } > ( ) ;
20+ const ControlledDatePickerExample = ( ) => {
21+ const fetcher = useFetcher < { message : string ; date : string } > ( ) ;
2022 const methods = useRemixForm < FormData > ( {
2123 resolver : zodResolver ( formSchema ) ,
2224 defaultValues : {
23- eventDate : undefined ,
25+ date : undefined ,
2426 } ,
2527 fetcher,
2628 submitConfig : {
2729 action : '/' ,
2830 method : 'post' ,
2931 } ,
32+ submitHandlers : {
33+ onValid : ( data ) => {
34+ fetcher . submit (
35+ createFormData ( {
36+ date : data . date . toISOString ( ) ,
37+ } ) ,
38+ {
39+ method : 'post' ,
40+ action : '/' ,
41+ } ,
42+ ) ;
43+ } ,
44+ } ,
3045 } ) ;
3146
3247 return (
3348 < RemixFormProvider { ...methods } >
3449 < Form onSubmit = { methods . handleSubmit } >
35- < DatePicker name = "eventDate" label = "Event Date" description = "Choose the date for your event." />
36- < Button type = "submit" className = "mt-4" >
37- Submit
38- </ Button >
39- { fetcher . data ?. message && < p className = "mt-2 text-green-600" > { fetcher . data . message } </ p > }
50+ < div className = "space-y-4" >
51+ < DatePicker name = "date" label = "Select a date" />
52+ < Button type = "submit" className = "mt-4" >
53+ Submit
54+ </ Button >
55+ { fetcher . data ?. date && (
56+ < div className = "mt-4" >
57+ < p className = "text-sm font-medium" > Submitted with date:</ p >
58+ < p className = "text-sm text-gray-500" > { new Date ( fetcher . data . date ) . toLocaleDateString ( ) } </ p >
59+ </ div >
60+ ) }
61+ </ div >
4062 </ Form >
4163 </ RemixFormProvider >
4264 ) ;
4365} ;
4466
45- // Action function for form submission
4667const handleFormSubmission = async ( request : Request ) => {
47- const { errors, receivedValues : defaultValues } = await getValidatedFormData < FormData > (
48- request ,
49- zodResolver ( formSchema ) ,
50- ) ;
68+ const { data, errors } = await getValidatedFormData < FormData > ( request , zodResolver ( formSchema ) ) ;
5169
5270 if ( errors ) {
53- return { errors, defaultValues } ;
71+ return { errors } ;
5472 }
5573
56- return { message : 'Form submitted successfully' } ;
74+ return { message : 'Date selected successfully' , date : data . date . toISOString ( ) } ;
5775} ;
5876
59- // Storybook configuration
6077const meta : Meta < typeof DatePicker > = {
6178 title : 'RemixHookForm/DatePicker' ,
6279 component : DatePicker ,
@@ -65,7 +82,7 @@ const meta: Meta<typeof DatePicker> = {
6582 decorators : [
6683 withRemixStubDecorator ( {
6784 root : {
68- Component : DatePickerExample ,
85+ Component : ControlledDatePickerExample ,
6986 action : async ( { request } : ActionFunctionArgs ) => handleFormSubmission ( request ) ,
7087 } ,
7188 } ) ,
@@ -75,48 +92,33 @@ const meta: Meta<typeof DatePicker> = {
7592export default meta ;
7693type Story = StoryObj < typeof meta > ;
7794
78- const testDefaultValues = ( { canvas } : StoryContext ) => {
79- const datePickerButton = canvas . getByRole ( 'button' , { name : 'Event Date' } ) ;
80- expect ( datePickerButton ) . toHaveTextContent ( 'Event Date' ) ;
81- } ;
82-
83- const testDateSelection = async ( { canvas } : StoryContext ) => {
84- const datePickerButton = canvas . getByRole ( 'button' , { name : 'Event Date' } ) ;
85- await userEvent . click ( datePickerButton ) ;
86-
87- await waitFor ( async ( ) => {
88- const popover = document . querySelector ( '[role="dialog"]' ) ;
89- expect ( popover ) . not . toBeNull ( ) ;
90-
91- if ( popover ) {
92- const calendar = within ( popover as HTMLElement ) . getByRole ( 'grid' ) ;
93- expect ( calendar ) . toBeInTheDocument ( ) ;
94-
95- const dateCell = within ( calendar ) . getByText ( '15' ) ;
96- expect ( dateCell ) . toBeInTheDocument ( ) ;
97- await userEvent . click ( dateCell ) ;
98- }
99- } ) ;
100-
101- const dateToSelect = '15' ;
102- await waitFor ( ( ) => {
103- const updatedDatePickerButton = canvas . getByRole ( 'button' , { name : new RegExp ( dateToSelect , 'i' ) } ) ;
104- expect ( updatedDatePickerButton ) . toBeInTheDocument ( ) ;
105- } ) ;
106- } ;
107-
108- const testSubmission = async ( { canvas } : StoryContext ) => {
109- const submitButton = canvas . getByRole ( 'button' , { name : 'Submit' } ) ;
110- await userEvent . click ( submitButton ) ;
111-
112- await expect ( canvas . findByText ( 'Form submitted successfully' ) ) . resolves . toBeInTheDocument ( ) ;
113- } ;
114-
115- // Stories
116- export const Tests : Story = {
117- play : async ( storyContext ) => {
118- testDefaultValues ( storyContext ) ;
119- await testDateSelection ( storyContext ) ;
120- await testSubmission ( storyContext ) ;
95+ export const Default : Story = {
96+ parameters : {
97+ docs : {
98+ description : {
99+ story : 'A date picker component for selecting a date.' ,
100+ } ,
101+ } ,
102+ } ,
103+ play : async ( { canvasElement } ) => {
104+ const canvas = within ( canvasElement ) ;
105+
106+ // Open the date picker
107+ const datePickerButton = canvas . getByRole ( 'button' , { name : / s e l e c t a d a t e / i } ) ;
108+ await userEvent . click ( datePickerButton ) ;
109+
110+ // Select a date (today)
111+ const today = new Date ( ) ;
112+ const formattedDate = today . getDate ( ) . toString ( ) ;
113+ const dateButton = canvas . getByRole ( 'button' , { name : new RegExp ( `^${ formattedDate } $` ) } ) ;
114+ await userEvent . click ( dateButton ) ;
115+
116+ // Submit the form
117+ const submitButton = canvas . getByRole ( 'button' , { name : 'Submit' } ) ;
118+ await userEvent . click ( submitButton ) ;
119+
120+ // Check if the selected date is displayed
121+ const selectedDate = new Date ( today ) . toLocaleDateString ( ) ;
122+ await expect ( await canvas . findByText ( selectedDate ) ) . toBeInTheDocument ( ) ;
121123 } ,
122124} ;
0 commit comments