@@ -20,18 +20,22 @@ type FormData = z.infer<typeof formSchema>;
2020const AlertErrorMessage = ( props : React . ComponentProps < typeof FormMessage > ) => (
2121 < div className = "flex items-center p-4 bg-red-50 border-l-4 border-red-400 rounded-md" >
2222 < svg className = "h-5 w-5 text-red-400 mr-3" viewBox = "0 0 20 20" fill = "currentColor" >
23- < path fillRule = "evenodd" d = "M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clipRule = "evenodd" />
23+ < path
24+ fillRule = "evenodd"
25+ d = "M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
26+ clipRule = "evenodd"
27+ />
2428 </ svg >
2529 < FormMessage className = "text-red-800 font-medium" { ...props } />
2630 </ div >
2731) ;
2832
2933const CustomStyledFormErrorExample = ( ) => {
30- const fetcher = useFetcher < {
31- message ?: string ;
32- errors ?: Record < string , { message : string } >
34+ const fetcher = useFetcher < {
35+ message ?: string ;
36+ errors ?: Record < string , { message : string } > ;
3337 } > ( ) ;
34-
38+
3539 const methods = useRemixForm < FormData > ( {
3640 resolver : zodResolver ( formSchema ) ,
3741 defaultValues : {
@@ -51,34 +55,34 @@ const CustomStyledFormErrorExample = () => {
5155 < RemixFormProvider { ...methods } >
5256 < fetcher . Form onSubmit = { methods . handleSubmit } className = "max-w-md mx-auto p-6 space-y-4" >
5357 < h2 className = "text-xl font-semibold text-gray-900" > Sign In</ h2 >
54-
58+
5559 { /* Custom styled form error */ }
56- < FormError
60+ < FormError
5761 components = { {
5862 FormMessage : AlertErrorMessage ,
5963 } }
6064 />
61-
65+
6266 < TextField
6367 name = "email"
6468 type = "email"
6569 label = "Email Address"
6670 placeholder = "Enter your email"
6771 disabled = { isSubmitting }
6872 />
69-
73+
7074 < TextField
7175 name = "password"
7276 type = "password"
7377 label = "Password"
7478 placeholder = "Enter your password"
7579 disabled = { isSubmitting }
7680 />
77-
81+
7882 < Button type = "submit" disabled = { isSubmitting } className = "w-full" >
7983 { isSubmitting ? 'Signing In...' : 'Sign In' }
8084 </ Button >
81-
85+
8286 { fetcher . data ?. message && (
8387 < div className = "mt-4 p-4 bg-green-50 border border-green-200 rounded-md" >
8488 < p className = "text-green-700 font-medium" > { fetcher . data . message } </ p >
@@ -91,16 +95,16 @@ const CustomStyledFormErrorExample = () => {
9195
9296const handleFormSubmission = async ( request : Request ) => {
9397 const { data, errors } = await getValidatedFormData < FormData > ( request , zodResolver ( formSchema ) ) ;
94-
98+
9599 if ( errors ) {
96100 return { errors } ;
97101 }
98-
102+
99103 // Always show form error for demo purposes
100104 return {
101105 errors : {
102- _form : { message : 'Authentication service is temporarily unavailable. Please try again in a few minutes.' }
103- }
106+ _form : { message : 'Authentication service is temporarily unavailable. Please try again in a few minutes.' } ,
107+ } ,
104108 } ;
105109} ;
106110
@@ -188,21 +192,29 @@ The custom component receives all the same props as the default FormMessage comp
188192 await userEvent . click ( submitButton ) ;
189193
190194 // Wait for error to appear
191- await new Promise ( resolve => setTimeout ( resolve , 500 ) ) ;
192-
195+ await new Promise ( ( resolve ) => setTimeout ( resolve , 500 ) ) ;
196+
193197 // Check for error message
194198 const errorMessage = canvas . queryByText ( / a u t h e n t i c a t i o n s e r v i c e i s t e m p o r a r i l y u n a v a i l a b l e / i) ;
195199 expect ( errorMessage ) . toBeInTheDocument ( ) ;
196200 } ) ;
197201
198202 await step ( 'Verify custom error styling and structure' , async ( ) => {
199203 // Wait for error message to be available
200- await new Promise ( resolve => setTimeout ( resolve , 500 ) ) ;
204+ await new Promise ( ( resolve ) => setTimeout ( resolve , 500 ) ) ;
201205 const errorMessage = canvas . queryByText ( / a u t h e n t i c a t i o n s e r v i c e i s t e m p o r a r i l y u n a v a i l a b l e / i) ;
202206 expect ( errorMessage ) . toBeInTheDocument ( ) ;
203207
204208 const errorContainer = errorMessage ?. closest ( 'div' ) ;
205- expect ( errorContainer ) . toHaveClass ( 'flex' , 'items-center' , 'p-4' , 'bg-red-50' , 'border-l-4' , 'border-red-400' , 'rounded-md' ) ;
209+ expect ( errorContainer ) . toHaveClass (
210+ 'flex' ,
211+ 'items-center' ,
212+ 'p-4' ,
213+ 'bg-red-50' ,
214+ 'border-l-4' ,
215+ 'border-red-400' ,
216+ 'rounded-md' ,
217+ ) ;
206218
207219 const icon = errorContainer ?. querySelector ( 'svg' ) ;
208220 expect ( icon ) . toBeInTheDocument ( ) ;
0 commit comments