Skip to content

Commit 977bb49

Browse files
feat: implement comprehensive FormError component with stories, tests, and documentation
- Add FormError component for standardized form-level error handling - Create FormErrorField base component in ui/ directory - Add remix-hook-form wrapper with automatic context integration - Implement comprehensive Storybook stories with 4 scenarios: - BasicFormError: Simple server validation failure - MixedErrors: Field + form-level errors together - CustomStyling: Branded error components with icons - PlacementVariations: Different positioning options - Add extensive test coverage for all component functionality - Create comprehensive documentation guide with best practices - Add LLM implementation guide with FormError patterns - Update exports to include new FormError component - Follow existing architectural patterns and component override system - Use '_form' as standard error key convention - Support flexible placement and custom styling - Maintain accessibility and TypeScript support Implements all phases of the form-level error handling gameplan: Phase 1: FormError component with consistent API Phase 2: Server action patterns for form-level errors Phase 3: Stories and tests demonstrating real-world usage Phase 4: Documentation and migration guidance
1 parent 0bf5592 commit 977bb49

8 files changed

Lines changed: 2071 additions & 0 deletions

File tree

apps/docs/src/remix-hook-form/form-error.stories.tsx

Lines changed: 507 additions & 0 deletions
Large diffs are not rendered by default.

apps/docs/src/remix-hook-form/form-error.test.tsx

Lines changed: 401 additions & 0 deletions
Large diffs are not rendered by default.

docs/form-error-guide.md

Lines changed: 401 additions & 0 deletions
Large diffs are not rendered by default.

llms.txt

Lines changed: 702 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import * as React from 'react';
2+
import { useRemixFormContext } from 'remix-hook-form';
3+
import { FormErrorField } from '../ui/form-error-field';
4+
import type { FormErrorFieldProps } from '../ui/form-error-field';
5+
6+
export type FormErrorProps = Omit<FormErrorFieldProps, 'control'> & {
7+
name?: string;
8+
};
9+
10+
export function FormError({ name = '_form', ...props }: FormErrorProps) {
11+
const { control } = useRemixFormContext();
12+
13+
return <FormErrorField control={control} name={name} {...props} />;
14+
}
15+
16+
FormError.displayName = 'FormError';

packages/components/src/remix-hook-form/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from './checkbox';
22
export * from './form';
3+
export * from './form-error';
34
export * from './date-picker';
45
export * from './dropdown-menu-select';
56
export * from './text-field';
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import * as React from 'react';
2+
import type { FieldPath, FieldValues, Control } from 'react-hook-form';
3+
import { FormField, FormItem, FormMessage } from './form';
4+
import type { FieldComponents } from './form';
5+
6+
export interface FormErrorFieldProps<
7+
TFieldValues extends FieldValues = FieldValues,
8+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
9+
> {
10+
control?: Control<TFieldValues>;
11+
name: TName;
12+
className?: string;
13+
components?: Partial<FieldComponents>;
14+
}
15+
16+
export const FormErrorField = <
17+
TFieldValues extends FieldValues = FieldValues,
18+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
19+
>({
20+
control,
21+
name,
22+
className,
23+
components,
24+
}: FormErrorFieldProps<TFieldValues, TName>) => {
25+
return (
26+
<FormField
27+
control={control}
28+
name={name}
29+
render={({ fieldState }) => (
30+
<FormItem className={className}>
31+
{fieldState.error && (
32+
<FormMessage Component={components?.FormMessage}>
33+
{fieldState.error.message}
34+
</FormMessage>
35+
)}
36+
</FormItem>
37+
)}
38+
/>
39+
);
40+
};
41+
42+
FormErrorField.displayName = 'FormErrorField';

packages/components/src/ui/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export * from './date-picker-field';
66
export * from './dropdown-menu';
77
export * from './dropdown-menu-select-field';
88
export * from './form';
9+
export * from './form-error-field';
910
export * from './label';
1011
export * from './otp-input';
1112
export * from './otp-input-field';

0 commit comments

Comments
 (0)