From 28b52b720b417eb942e6a28a6eab6e78657cca1a Mon Sep 17 00:00:00 2001 From: Ben Fein Date: Thu, 27 Feb 2025 12:32:55 -0500 Subject: [PATCH 01/14] refactor: rename remix form components to form components --- .../components/src/form/form-checkbox.tsx | 18 +++++ .../form-date-picker.tsx} | 4 +- .../form-dropdown-menu-select.tsx} | 4 +- .../form-otp-input.tsx} | 4 +- .../form-radio-group.tsx} | 4 +- .../remix-switch.tsx => form/form-switch.tsx} | 14 ++-- .../components/src/form/form-text-field.tsx | 18 +++++ .../components/src/form/form-textarea.tsx | 18 +++++ packages/components/src/form/index.ts | 9 +++ packages/components/src/remix/index.ts | 9 --- .../components/src/remix/remix-checkbox.tsx | 18 ----- packages/components/src/remix/remix-form.tsx | 76 ------------------- .../components/src/remix/remix-text-field.tsx | 18 ----- .../components/src/remix/remix-textarea.tsx | 18 ----- 14 files changed, 78 insertions(+), 154 deletions(-) create mode 100644 packages/components/src/form/form-checkbox.tsx rename packages/components/src/{remix/remix-date-picker.tsx => form/form-date-picker.tsx} (64%) rename packages/components/src/{remix/remix-dropdown-menu-select.tsx => form/form-dropdown-menu-select.tsx} (62%) rename packages/components/src/{remix/remix-otp-input.tsx => form/form-otp-input.tsx} (71%) rename packages/components/src/{remix/remix-radio-group.tsx => form/form-radio-group.tsx} (62%) rename packages/components/src/{remix/remix-switch.tsx => form/form-switch.tsx} (50%) create mode 100644 packages/components/src/form/form-text-field.tsx create mode 100644 packages/components/src/form/form-textarea.tsx create mode 100644 packages/components/src/form/index.ts delete mode 100644 packages/components/src/remix/index.ts delete mode 100644 packages/components/src/remix/remix-checkbox.tsx delete mode 100644 packages/components/src/remix/remix-form.tsx delete mode 100644 packages/components/src/remix/remix-text-field.tsx delete mode 100644 packages/components/src/remix/remix-textarea.tsx diff --git a/packages/components/src/form/form-checkbox.tsx b/packages/components/src/form/form-checkbox.tsx new file mode 100644 index 00000000..b8bc3bc4 --- /dev/null +++ b/packages/components/src/form/form-checkbox.tsx @@ -0,0 +1,18 @@ +import { useRemixFormContext } from 'remix-hook-form'; +import { Checkbox, type CheckboxProps } from '../ui/checkbox-field'; +import { FormControl, FormDescription, FormLabel, FormMessage } from '../ui/form'; + +export type FormCheckboxProps = Omit; + +export function FormCheckbox(props: FormCheckboxProps) { + const { control } = useRemixFormContext(); + + const components = { + FormDescription: FormDescription, + FormControl: FormControl, + FormLabel: FormLabel, + FormMessage: FormMessage, + }; + + return ; +} diff --git a/packages/components/src/remix/remix-date-picker.tsx b/packages/components/src/form/form-date-picker.tsx similarity index 64% rename from packages/components/src/remix/remix-date-picker.tsx rename to packages/components/src/form/form-date-picker.tsx index 01cf1ffd..69429877 100644 --- a/packages/components/src/remix/remix-date-picker.tsx +++ b/packages/components/src/form/form-date-picker.tsx @@ -1,9 +1,9 @@ import { useRemixFormContext } from 'remix-hook-form'; import { DatePickerField, type DatePickerFieldProps } from '../ui/date-picker-field'; -export type RemixDatePickerProps = Omit; +export type FormDatePickerProps = Omit; -export function RemixDatePicker(props: RemixDatePickerProps) { +export function FormDatePicker(props: FormDatePickerProps) { const { control } = useRemixFormContext(); return ; diff --git a/packages/components/src/remix/remix-dropdown-menu-select.tsx b/packages/components/src/form/form-dropdown-menu-select.tsx similarity index 62% rename from packages/components/src/remix/remix-dropdown-menu-select.tsx rename to packages/components/src/form/form-dropdown-menu-select.tsx index 9da336cb..0dd03177 100644 --- a/packages/components/src/remix/remix-dropdown-menu-select.tsx +++ b/packages/components/src/form/form-dropdown-menu-select.tsx @@ -1,9 +1,9 @@ import { useRemixFormContext } from 'remix-hook-form'; import { DropdownMenuSelectField, type DropdownMenuSelectProps } from '../ui/dropdown-menu-select-field'; -export type RemixDropdownMenuSelectProps = Omit; +export type FormDropdownMenuSelectProps = Omit; -export function RemixDropdownMenuSelect(props: RemixDropdownMenuSelectProps) { +export function FormDropdownMenuSelect(props: FormDropdownMenuSelectProps) { const { control } = useRemixFormContext(); return ; diff --git a/packages/components/src/remix/remix-otp-input.tsx b/packages/components/src/form/form-otp-input.tsx similarity index 71% rename from packages/components/src/remix/remix-otp-input.tsx rename to packages/components/src/form/form-otp-input.tsx index dc09bf3a..8f5c777f 100644 --- a/packages/components/src/remix/remix-otp-input.tsx +++ b/packages/components/src/form/form-otp-input.tsx @@ -1,9 +1,9 @@ import { useRemixFormContext } from 'remix-hook-form'; import { OTPInputField, type OTPInputFieldProps } from '../ui/otp-input-field'; -export type RemixOTPInputFieldProps = Omit; +export type FormOTPInputFieldProps = Omit; -export function RemixOTPInputField(props: RemixOTPInputFieldProps) { +export function FormOTPInputField(props: FormOTPInputFieldProps) { const { control } = useRemixFormContext(); const { name, ...restProps } = props; diff --git a/packages/components/src/remix/remix-radio-group.tsx b/packages/components/src/form/form-radio-group.tsx similarity index 62% rename from packages/components/src/remix/remix-radio-group.tsx rename to packages/components/src/form/form-radio-group.tsx index 908eab9e..3cc09788 100644 --- a/packages/components/src/remix/remix-radio-group.tsx +++ b/packages/components/src/form/form-radio-group.tsx @@ -1,9 +1,9 @@ import { useRemixFormContext } from 'remix-hook-form'; import { RadioGroupField, type RadioGroupFieldProps } from '../ui/radio-group-field'; -export type RemixRadioGroupFieldProps = Omit; +export type FormRadioGroupFieldProps = Omit; -export function RemixRadioGroupField(props: RemixRadioGroupFieldProps) { +export function FormRadioGroupField(props: FormRadioGroupFieldProps) { const { control } = useRemixFormContext(); return ; diff --git a/packages/components/src/remix/remix-switch.tsx b/packages/components/src/form/form-switch.tsx similarity index 50% rename from packages/components/src/remix/remix-switch.tsx rename to packages/components/src/form/form-switch.tsx index 0140fb8b..6e0cefdc 100644 --- a/packages/components/src/remix/remix-switch.tsx +++ b/packages/components/src/form/form-switch.tsx @@ -1,22 +1,22 @@ import type * as React from 'react'; import { useRemixFormContext } from 'remix-hook-form'; import { SwitchField } from '../ui/switch-field'; -import { RemixFormControl, RemixFormDescription, RemixFormLabel, RemixFormMessage } from './remix-form'; +import { FormControl, FormDescription, FormLabel, FormMessage } from '../ui/form'; -export interface RemixSwitchProps extends Omit, 'control'> { +export interface FormSwitchProps extends Omit, 'control'> { name: string; label?: string; description?: string; } -export function RemixSwitch({ name, label, description, className, ...props }: RemixSwitchProps) { +export function FormSwitch({ name, label, description, className, ...props }: FormSwitchProps) { const { control } = useRemixFormContext(); const components = { - FormDescription: RemixFormDescription, - FormControl: RemixFormControl, - FormLabel: RemixFormLabel, - FormMessage: RemixFormMessage, + FormDescription: FormDescription, + FormControl: FormControl, + FormLabel: FormLabel, + FormMessage: FormMessage, }; return ( diff --git a/packages/components/src/form/form-text-field.tsx b/packages/components/src/form/form-text-field.tsx new file mode 100644 index 00000000..cb8e8725 --- /dev/null +++ b/packages/components/src/form/form-text-field.tsx @@ -0,0 +1,18 @@ +import { useRemixFormContext } from 'remix-hook-form'; +import { TextField, type TextFieldProps } from '../ui/text-field'; +import { FormControl, FormDescription, FormLabel, FormMessage } from '../ui/form'; + +export type FormTextFieldProps = Omit; + +export function FormTextField(props: FormTextFieldProps) { + const { control } = useRemixFormContext(); + + const components = { + FormControl: FormControl, + FormLabel: FormLabel, + FormDescription: FormDescription, + FormMessage: FormMessage, + }; + + return ; +} diff --git a/packages/components/src/form/form-textarea.tsx b/packages/components/src/form/form-textarea.tsx new file mode 100644 index 00000000..4369ae96 --- /dev/null +++ b/packages/components/src/form/form-textarea.tsx @@ -0,0 +1,18 @@ +import { useRemixFormContext } from 'remix-hook-form'; +import { TextareaField, type TextareaFieldProps } from '../ui/textarea-field'; +import { FormControl, FormDescription, FormLabel, FormMessage } from '../ui/form'; + +export type FormTextareaProps = Omit; + +export function FormTextarea(props: FormTextareaProps) { + const { control } = useRemixFormContext(); + + const components = { + FormControl: FormControl, + FormLabel: FormLabel, + FormDescription: FormDescription, + FormMessage: FormMessage, + }; + + return ; +} diff --git a/packages/components/src/form/index.ts b/packages/components/src/form/index.ts new file mode 100644 index 00000000..795641a6 --- /dev/null +++ b/packages/components/src/form/index.ts @@ -0,0 +1,9 @@ +export * from './form-checkbox'; +export * from './form-date-picker'; +export * from './form-dropdown-menu-select'; +export * from './remix-form'; +export * from './form-otp-input'; +export * from './form-radio-group'; +export * from './form-switch'; +export * from './form-text-field'; +export * from './form-textarea'; diff --git a/packages/components/src/remix/index.ts b/packages/components/src/remix/index.ts deleted file mode 100644 index 15f916af..00000000 --- a/packages/components/src/remix/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -export * from './remix-checkbox'; -export * from './remix-date-picker'; -export * from './remix-dropdown-menu-select'; -export * from './remix-form'; -export * from './remix-otp-input'; -export * from './remix-radio-group'; -export * from './remix-switch'; -export * from './remix-text-field'; -export * from './remix-textarea'; diff --git a/packages/components/src/remix/remix-checkbox.tsx b/packages/components/src/remix/remix-checkbox.tsx deleted file mode 100644 index f49a6700..00000000 --- a/packages/components/src/remix/remix-checkbox.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { useRemixFormContext } from 'remix-hook-form'; -import { Checkbox, type CheckboxProps } from '../ui/checkbox-field'; -import { RemixFormControl, RemixFormDescription, RemixFormLabel, RemixFormMessage } from './remix-form'; - -export type RemixCheckboxProps = Omit; - -export function RemixCheckbox(props: RemixCheckboxProps) { - const { control } = useRemixFormContext(); - - const components = { - FormDescription: RemixFormDescription, - FormControl: RemixFormControl, - FormLabel: RemixFormLabel, - FormMessage: RemixFormMessage, - }; - - return ; -} diff --git a/packages/components/src/remix/remix-form.tsx b/packages/components/src/remix/remix-form.tsx deleted file mode 100644 index 1f23b8e8..00000000 --- a/packages/components/src/remix/remix-form.tsx +++ /dev/null @@ -1,76 +0,0 @@ -// biome-ignore lint/style/noNamespaceImport: prevents React undefined errors when exporting as a component library -import * as React from 'react'; -import type { FieldValues, KeepStateOptions, UseFormRegister, UseFormReturn } from 'react-hook-form'; -import { useRemixFormContext } from 'remix-hook-form'; -import { FormControl, FormDescription, FormFieldContext, FormItemContext, FormLabel, FormMessage } from '../ui/form'; - -export interface RemixFormProviderProps - extends Omit, 'handleSubmit' | 'reset'> { - children: React.ReactNode; - handleSubmit: (e?: React.BaseSyntheticEvent) => Promise; - register: UseFormRegister; - reset: (values?: T, keepStateOptions?: KeepStateOptions) => void; -} - -export const useRemixFormField = () => { - const fieldContext = React.useContext(FormFieldContext); - const itemContext = React.useContext(FormItemContext); - const { getFieldState, formState } = useRemixFormContext(); - - const fieldState = getFieldState(fieldContext.name, formState); - - if (!fieldContext) { - throw new Error('useFormField should be used within '); - } - - const { id, formItemId, formDescriptionId, formMessageId } = itemContext; - - return { - id, - name: fieldContext.name, - formItemId, - formDescriptionId, - formMessageId, - ...fieldState, - }; -}; - -export const RemixFormLabel = React.forwardRef>( - (props, ref) => , -); -RemixFormLabel.displayName = 'RemixFormLabel'; - -export const RemixFormControl = React.forwardRef>( - (props, ref) => { - const { error, formItemId, formDescriptionId, formMessageId } = useRemixFormField(); - return ( - - ); - }, -); -RemixFormControl.displayName = 'RemixFormControl'; - -export const RemixFormDescription = React.forwardRef< - HTMLParagraphElement, - React.ComponentPropsWithoutRef ->((props, ref) => { - const { formDescriptionId } = useRemixFormField(); - return ; -}); -RemixFormDescription.displayName = 'RemixFormDescription'; - -export const RemixFormMessage = React.forwardRef< - HTMLParagraphElement, - React.ComponentPropsWithoutRef ->((props, ref) => { - const { error, formMessageId } = useRemixFormField(); - return ; -}); -RemixFormMessage.displayName = 'RemixFormMessage'; diff --git a/packages/components/src/remix/remix-text-field.tsx b/packages/components/src/remix/remix-text-field.tsx deleted file mode 100644 index 663f2d22..00000000 --- a/packages/components/src/remix/remix-text-field.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { useRemixFormContext } from 'remix-hook-form'; -import { TextField, type TextFieldProps } from '../ui/text-field'; -import { RemixFormControl, RemixFormDescription, RemixFormLabel, RemixFormMessage } from './remix-form'; - -export type RemixTextFieldProps = Omit; - -export function RemixTextField(props: RemixTextFieldProps) { - const { control } = useRemixFormContext(); - - const components = { - FormControl: RemixFormControl, - FormLabel: RemixFormLabel, - FormDescription: RemixFormDescription, - FormMessage: RemixFormMessage, - }; - - return ; -} diff --git a/packages/components/src/remix/remix-textarea.tsx b/packages/components/src/remix/remix-textarea.tsx deleted file mode 100644 index c87ed660..00000000 --- a/packages/components/src/remix/remix-textarea.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { useRemixFormContext } from 'remix-hook-form'; -import { TextareaField, type TextareaFieldProps } from '../ui/textarea-field'; -import { RemixFormControl, RemixFormDescription, RemixFormLabel, RemixFormMessage } from './remix-form'; - -export type RemixTextareaProps = Omit; - -export function RemixTextarea(props: RemixTextareaProps) { - const { control } = useRemixFormContext(); - - const components = { - FormControl: RemixFormControl, - FormLabel: RemixFormLabel, - FormDescription: RemixFormDescription, - FormMessage: RemixFormMessage, - }; - - return ; -} From 177300aa319c4c6cbced3f26db100b19a3d34ce3 Mon Sep 17 00:00:00 2001 From: Ben Fein Date: Thu, 27 Feb 2025 12:34:35 -0500 Subject: [PATCH 02/14] chore: update index export from remix to form --- packages/components/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index 9cfd6693..aea35983 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -1,2 +1,2 @@ export * from './ui'; -export * from './remix'; +export * from './form'; From 46485f4f910b6f3efd18d10f2ce3eb59597ed94d Mon Sep 17 00:00:00 2001 From: Ben Fein Date: Thu, 27 Feb 2025 12:52:48 -0500 Subject: [PATCH 03/14] chore: remove Remix-specific documentation and stories --- .../src/{remix => form}/0.1 Remix Stub.mdx | 0 .../0.2 Custom Submissions.mdx | 0 .../form-checkbox-list.stories.tsx} | 12 +++++----- .../form-checkbox.stories.tsx} | 22 +++++++++---------- .../form-date-picker.stories.tsx} | 16 +++++++------- .../form-dropdown-menu-select.stories.tsx} | 18 +++++++-------- .../form-otp-input.stories.tsx} | 16 +++++++------- .../form-radio-group.stories.tsx} | 18 +++++++-------- .../form-switch.stories.tsx} | 14 ++++++------ .../form-text-field.stories.tsx} | 12 +++++----- .../form-textarea.stories.tsx} | 12 +++++----- packages/components/src/form/index.ts | 1 - 12 files changed, 70 insertions(+), 71 deletions(-) rename apps/docs/src/{remix => form}/0.1 Remix Stub.mdx (100%) rename apps/docs/src/{remix => form}/0.2 Custom Submissions.mdx (100%) rename apps/docs/src/{remix/remix-checkbox-list.stories.tsx => form/form-checkbox-list.stories.tsx} (93%) rename apps/docs/src/{remix/remix-checkbox.stories.tsx => form/form-checkbox.stories.tsx} (85%) rename apps/docs/src/{remix/remix-date-picker.stories.tsx => form/form-date-picker.stories.tsx} (89%) rename apps/docs/src/{remix/remix-dropdown-menu-select.stories.tsx => form/form-dropdown-menu-select.stories.tsx} (91%) rename apps/docs/src/{remix/remix-otp-input.stories.tsx => form/form-otp-input.stories.tsx} (90%) rename apps/docs/src/{remix/remix-radio-group.stories.tsx => form/form-radio-group.stories.tsx} (90%) rename apps/docs/src/{remix/remix-switch.stories.tsx => form/form-switch.stories.tsx} (89%) rename apps/docs/src/{remix/remix-text-field.stories.tsx => form/form-text-field.stories.tsx} (93%) rename apps/docs/src/{remix/remix-textarea.stories.tsx => form/form-textarea.stories.tsx} (92%) diff --git a/apps/docs/src/remix/0.1 Remix Stub.mdx b/apps/docs/src/form/0.1 Remix Stub.mdx similarity index 100% rename from apps/docs/src/remix/0.1 Remix Stub.mdx rename to apps/docs/src/form/0.1 Remix Stub.mdx diff --git a/apps/docs/src/remix/0.2 Custom Submissions.mdx b/apps/docs/src/form/0.2 Custom Submissions.mdx similarity index 100% rename from apps/docs/src/remix/0.2 Custom Submissions.mdx rename to apps/docs/src/form/0.2 Custom Submissions.mdx diff --git a/apps/docs/src/remix/remix-checkbox-list.stories.tsx b/apps/docs/src/form/form-checkbox-list.stories.tsx similarity index 93% rename from apps/docs/src/remix/remix-checkbox-list.stories.tsx rename to apps/docs/src/form/form-checkbox-list.stories.tsx index abcff10c..ed7587bc 100644 --- a/apps/docs/src/remix/remix-checkbox-list.stories.tsx +++ b/apps/docs/src/form/form-checkbox-list.stories.tsx @@ -1,5 +1,5 @@ import { zodResolver } from '@hookform/resolvers/zod'; -import { RemixCheckbox } from '@lambdacurry/forms/remix/remix-checkbox'; +import { FormCheckbox } from '@lambdacurry/forms/form/form-checkbox'; import { Button } from '@lambdacurry/forms/ui/button'; import { FormMessage } from '@lambdacurry/forms/ui/form'; import type { ActionFunctionArgs } from '@remix-run/node'; @@ -67,7 +67,7 @@ const ControlledCheckboxListExample = () => {

Select your favorite colors:

{AVAILABLE_COLORS.map(({ value, label }) => ( - + ))}
@@ -100,9 +100,9 @@ const handleFormSubmission = async (request: Request) => { return { message: 'Colors selected successfully', selectedColors }; }; -const meta: Meta = { - title: 'Remix/RemixCheckboxList', - component: RemixCheckbox, +const meta: Meta = { + title: 'Form/FormCheckboxList', + component: FormCheckbox, parameters: { layout: 'centered' }, tags: ['autodocs'], decorators: [ @@ -113,7 +113,7 @@ const meta: Meta = { }, }), ], -} satisfies Meta; +} satisfies Meta; export default meta; type Story = StoryObj; diff --git a/apps/docs/src/remix/remix-checkbox.stories.tsx b/apps/docs/src/form/form-checkbox.stories.tsx similarity index 85% rename from apps/docs/src/remix/remix-checkbox.stories.tsx rename to apps/docs/src/form/form-checkbox.stories.tsx index 99264cd1..b08ae299 100644 --- a/apps/docs/src/remix/remix-checkbox.stories.tsx +++ b/apps/docs/src/form/form-checkbox.stories.tsx @@ -1,5 +1,5 @@ import { zodResolver } from '@hookform/resolvers/zod'; -import { RemixCheckbox } from '@lambdacurry/forms/remix/remix-checkbox'; +import { FormCheckbox } from '@lambdacurry/forms/form/form-checkbox'; import { Button } from '@lambdacurry/forms/ui/button'; import type { ActionFunctionArgs } from '@remix-run/node'; import { useFetcher } from '@remix-run/react'; @@ -38,14 +38,14 @@ const ControlledCheckboxExample = () => {
- - + - +
@@ -57,20 +57,20 @@ const handleFormSubmission = async (request: Request) => { }; // Storybook configuration -const meta: Meta = { - title: 'Remix/RemixDatePicker', - component: RemixDatePicker, +const meta: Meta = { + title: 'Form/FormDatePicker', + component: FormDatePicker, parameters: { layout: 'centered' }, tags: ['autodocs'], decorators: [ withRemixStubDecorator({ root: { - Component: RemixDatePickerExample, + Component: FormDatePickerExample, action: async ({ request }: ActionFunctionArgs) => handleFormSubmission(request), }, }), ], -} satisfies Meta; +} satisfies Meta; export default meta; type Story = StoryObj; diff --git a/apps/docs/src/remix/remix-dropdown-menu-select.stories.tsx b/apps/docs/src/form/form-dropdown-menu-select.stories.tsx similarity index 91% rename from apps/docs/src/remix/remix-dropdown-menu-select.stories.tsx rename to apps/docs/src/form/form-dropdown-menu-select.stories.tsx index 5102a1d5..75fe7a35 100644 --- a/apps/docs/src/remix/remix-dropdown-menu-select.stories.tsx +++ b/apps/docs/src/form/form-dropdown-menu-select.stories.tsx @@ -1,5 +1,5 @@ import { zodResolver } from '@hookform/resolvers/zod'; -import { RemixDropdownMenuSelect } from '@lambdacurry/forms/remix/remix-dropdown-menu-select'; +import { FormDropdownMenuSelect } from '@lambdacurry/forms/form/form-dropdown-menu-select'; import { Button } from '@lambdacurry/forms/ui/button'; import { DropdownMenuItem } from '@lambdacurry/forms/ui/dropdown-menu'; import type { ActionFunctionArgs } from '@remix-run/node'; @@ -18,7 +18,7 @@ const formSchema = z.object({ type FormData = z.infer; // Component for the form -const RemixDropdownMenuSelectExample = () => { +const FormDropdownMenuSelectExample = () => { const fetcher = useFetcher<{ message?: string }>(); const methods = useRemixForm({ resolver: zodResolver(formSchema), @@ -35,7 +35,7 @@ const RemixDropdownMenuSelectExample = () => { return ( - { methods.setValue('favoriteColor', 'Red')}>Red methods.setValue('favoriteColor', 'Green')}>Green methods.setValue('favoriteColor', 'Blue')}>Blue - + @@ -69,20 +69,20 @@ const handleFormSubmission = async (request: Request) => { }; // Storybook configuration -const meta: Meta = { - title: 'Remix/RemixDropdownMenuSelect', - component: RemixDropdownMenuSelect, +const meta: Meta = { + title: 'Form/FormDropdownMenuSelect', + component: FormDropdownMenuSelect, parameters: { layout: 'centered' }, tags: ['autodocs'], decorators: [ withRemixStubDecorator({ root: { - Component: RemixDropdownMenuSelectExample, + Component: FormDropdownMenuSelectExample, action: async ({ request }: ActionFunctionArgs) => handleFormSubmission(request), }, }), ], -} satisfies Meta; +} satisfies Meta; export default meta; type Story = StoryObj; diff --git a/apps/docs/src/remix/remix-otp-input.stories.tsx b/apps/docs/src/form/form-otp-input.stories.tsx similarity index 90% rename from apps/docs/src/remix/remix-otp-input.stories.tsx rename to apps/docs/src/form/form-otp-input.stories.tsx index 3c5fe430..d1abae39 100644 --- a/apps/docs/src/remix/remix-otp-input.stories.tsx +++ b/apps/docs/src/form/form-otp-input.stories.tsx @@ -1,5 +1,5 @@ import { zodResolver } from '@hookform/resolvers/zod'; -import { RemixOTPInputField } from '@lambdacurry/forms/remix/remix-otp-input'; +import { FormOTPInputField } from '@lambdacurry/forms/form/form-otp-input'; import { Button } from '@lambdacurry/forms/ui/button'; import type { ActionFunctionArgs } from '@remix-run/node'; import { Form, useFetcher } from '@remix-run/react'; @@ -16,7 +16,7 @@ const formSchema = z.object({ type FormData = z.infer; -const RemixOTPInputExample = () => { +const FormOTPInputExample = () => { const fetcher = useFetcher<{ message?: string }>(); const methods = useRemixForm({ resolver: zodResolver(formSchema), @@ -33,7 +33,7 @@ const RemixOTPInputExample = () => { return ( - { }; // Storybook configuration -const meta: Meta = { - title: 'Remix/RemixOTPInput', - component: RemixOTPInputField, +const meta: Meta = { + title: 'Form/FormOTPInput', + component: FormOTPInputField, parameters: { layout: 'centered' }, tags: ['autodocs'], decorators: [ withRemixStubDecorator({ root: { - Component: RemixOTPInputExample, + Component: FormOTPInputExample, action: async ({ request }: ActionFunctionArgs) => handleFormSubmission(request), }, }), ], -} satisfies Meta; +} satisfies Meta; export default meta; type Story = StoryObj; diff --git a/apps/docs/src/remix/remix-radio-group.stories.tsx b/apps/docs/src/form/form-radio-group.stories.tsx similarity index 90% rename from apps/docs/src/remix/remix-radio-group.stories.tsx rename to apps/docs/src/form/form-radio-group.stories.tsx index 8d4ddefe..87a859d7 100644 --- a/apps/docs/src/remix/remix-radio-group.stories.tsx +++ b/apps/docs/src/form/form-radio-group.stories.tsx @@ -1,5 +1,5 @@ import { zodResolver } from '@hookform/resolvers/zod'; -import { RemixRadioGroupField } from '@lambdacurry/forms/remix/remix-radio-group'; +import { FormRadioGroupField } from '@lambdacurry/forms/form/form-radio-group'; import { Button } from '@lambdacurry/forms/ui/button'; import { RadioGroupItem } from '@lambdacurry/forms/ui/radio-group'; import type { ActionFunctionArgs } from '@remix-run/node'; @@ -18,7 +18,7 @@ const formSchema = z.object({ type FormData = z.infer; -const RemixRadioGroupExample = () => { +const FormRadioGroupExample = () => { const fetcher = useFetcher<{ message?: string }>(); const methods = useRemixForm({ resolver: zodResolver(formSchema), @@ -35,7 +35,7 @@ const RemixRadioGroupExample = () => { return ( - { - + @@ -77,20 +77,20 @@ const handleFormSubmission = async (request: Request) => { return { message: 'Plan selected successfully' }; }; -const meta: Meta = { - title: 'Remix/RemixRadioGroup', - component: RemixRadioGroupField, +const meta: Meta = { + title: 'Form/FormRadioGroup', + component: FormRadioGroupField, parameters: { layout: 'centered' }, tags: ['autodocs'], decorators: [ withRemixStubDecorator({ root: { - Component: RemixRadioGroupExample, + Component: FormRadioGroupExample, action: async ({ request }: ActionFunctionArgs) => handleFormSubmission(request), }, }), ], -} satisfies Meta; +} satisfies Meta; export default meta; type Story = StoryObj; diff --git a/apps/docs/src/remix/remix-switch.stories.tsx b/apps/docs/src/form/form-switch.stories.tsx similarity index 89% rename from apps/docs/src/remix/remix-switch.stories.tsx rename to apps/docs/src/form/form-switch.stories.tsx index 1e7f304d..3c5d2cb7 100644 --- a/apps/docs/src/remix/remix-switch.stories.tsx +++ b/apps/docs/src/form/form-switch.stories.tsx @@ -1,5 +1,5 @@ import { zodResolver } from '@hookform/resolvers/zod'; -import { RemixSwitch } from '@lambdacurry/forms/remix/remix-switch'; +import { FormSwitch } from '@lambdacurry/forms/form/form-switch'; import { Button } from '@lambdacurry/forms/ui/button'; import type { ActionFunctionArgs } from '@remix-run/node'; import { useFetcher } from '@remix-run/react'; @@ -36,8 +36,8 @@ const ControlledSwitchExample = () => {
- - + +
@@ -75,9 +75,9 @@ const handleFormSubmission = async (request: Request) => { }; // Storybook configuration -const meta: Meta = { - title: 'Remix/RemixTextField', - component: RemixTextField, +const meta: Meta = { + title: 'Form/FormTextField', + component: FormTextField, parameters: { layout: 'centered' }, tags: ['autodocs'], decorators: [ @@ -93,7 +93,7 @@ const meta: Meta = { ], }), ], -} satisfies Meta; +} satisfies Meta; export default meta; type Story = StoryObj; diff --git a/apps/docs/src/remix/remix-textarea.stories.tsx b/apps/docs/src/form/form-textarea.stories.tsx similarity index 92% rename from apps/docs/src/remix/remix-textarea.stories.tsx rename to apps/docs/src/form/form-textarea.stories.tsx index e6658f93..7ec1e31b 100644 --- a/apps/docs/src/remix/remix-textarea.stories.tsx +++ b/apps/docs/src/form/form-textarea.stories.tsx @@ -1,5 +1,5 @@ import { zodResolver } from '@hookform/resolvers/zod'; -import { RemixTextarea } from '@lambdacurry/forms/remix/remix-textarea'; +import { FormTextarea } from '@lambdacurry/forms/form/form-textarea'; import { Button } from '@lambdacurry/forms/ui/button'; import type { ActionFunctionArgs } from '@remix-run/node'; import { useFetcher } from '@remix-run/react'; @@ -36,7 +36,7 @@ const ControlledTextareaExample = () => { return ( - + @@ -74,9 +74,9 @@ const handleFormSubmission = async (request: Request) => { }; // Storybook configuration -const meta: Meta = { - title: 'Remix/RemixTextarea', - component: RemixTextarea, +const meta: Meta = { + title: 'Form/FormTextarea', + component: FormTextarea, parameters: { layout: 'centered' }, tags: ['autodocs'], decorators: [ @@ -87,7 +87,7 @@ const meta: Meta = { }, }), ], -} satisfies Meta; +} satisfies Meta; export default meta; type Story = StoryObj; diff --git a/packages/components/src/form/index.ts b/packages/components/src/form/index.ts index 795641a6..4c6dc537 100644 --- a/packages/components/src/form/index.ts +++ b/packages/components/src/form/index.ts @@ -1,7 +1,6 @@ export * from './form-checkbox'; export * from './form-date-picker'; export * from './form-dropdown-menu-select'; -export * from './remix-form'; export * from './form-otp-input'; export * from './form-radio-group'; export * from './form-switch'; From 4539c55bed2f5b5aa672c9c2d3e7f0917f26ba4f Mon Sep 17 00:00:00 2001 From: Ben Fein Date: Thu, 27 Feb 2025 13:27:25 -0500 Subject: [PATCH 04/14] refactor: update form stories to use new form item container selector --- .../src/form/form-checkbox-list.stories.tsx | 25 ++++++---- apps/docs/src/form/form-checkbox.stories.tsx | 46 ++++++++++------- apps/docs/src/form/form-switch.stories.tsx | 35 +++++++------ .../docs/src/form/form-text-field.stories.tsx | 49 +++++++++++-------- 4 files changed, 91 insertions(+), 64 deletions(-) diff --git a/apps/docs/src/form/form-checkbox-list.stories.tsx b/apps/docs/src/form/form-checkbox-list.stories.tsx index ed7587bc..33905be4 100644 --- a/apps/docs/src/form/form-checkbox-list.stories.tsx +++ b/apps/docs/src/form/form-checkbox-list.stories.tsx @@ -120,8 +120,8 @@ type Story = StoryObj; const testDefaultValues = ({ canvas }: StoryContext) => { AVAILABLE_COLORS.forEach(({ label }) => { - const checkbox = canvas.getByLabelText(label); - expect(checkbox).not.toBeChecked(); + const container = canvas.getByText(label).closest('.form-item'); + expect(container?.querySelector('[role="checkbox"]')).not.toBeChecked(); }); }; @@ -136,17 +136,22 @@ const testErrorState = async ({ canvas }: StoryContext) => { const testColorSelection = async ({ canvas }: StoryContext) => { // Select two colors - const redCheckbox = canvas.getByLabelText('Red'); - const blueCheckbox = canvas.getByLabelText('Blue'); + const redContainer = canvas.getByText('Red').closest('.form-item'); + const blueContainer = canvas.getByText('Blue').closest('.form-item'); + + const redCheckbox = redContainer?.querySelector('[role="checkbox"]'); + const blueCheckbox = blueContainer?.querySelector('[role="checkbox"]'); - await userEvent.click(redCheckbox); - await userEvent.click(blueCheckbox); + if (redCheckbox && blueCheckbox) { + await userEvent.click(redCheckbox); + await userEvent.click(blueCheckbox); - const submitButton = canvas.getByRole('button', { name: 'Submit' }); - await userEvent.click(submitButton); + const submitButton = canvas.getByRole('button', { name: 'Submit' }); + await userEvent.click(submitButton); - // Check if the selected colors are displayed - await expect(await canvas.findByText('Red, Blue')).toBeInTheDocument(); + // Check if the selected colors are displayed + await expect(await canvas.findByText('Red, Blue')).toBeInTheDocument(); + } }; export const Tests: Story = { diff --git a/apps/docs/src/form/form-checkbox.stories.tsx b/apps/docs/src/form/form-checkbox.stories.tsx index b08ae299..b838ca2f 100644 --- a/apps/docs/src/form/form-checkbox.stories.tsx +++ b/apps/docs/src/form/form-checkbox.stories.tsx @@ -23,9 +23,9 @@ const ControlledCheckboxExample = () => { const methods = useRemixForm({ resolver: zodResolver(formSchema), defaultValues: { - terms: false as true, // Note: ZOD Schema expects a true value + terms: false as true, marketing: false, - required: false as true, //Note: ZOD Schema expects a true value + required: false as true, }, fetcher, submitConfig: { @@ -38,14 +38,22 @@ const ControlledCheckboxExample = () => {
- + - +
@@ -57,20 +57,20 @@ const handleFormSubmission = async (request: Request) => { }; // Storybook configuration -const meta: Meta = { - title: 'Form/FormDatePicker', - component: FormDatePicker, +const meta: Meta = { + title: 'Form/DatePicker', + component: DatePicker, parameters: { layout: 'centered' }, tags: ['autodocs'], decorators: [ withRemixStubDecorator({ root: { - Component: FormDatePickerExample, + Component: DatePickerExample, action: async ({ request }: ActionFunctionArgs) => handleFormSubmission(request), }, }), ], -} satisfies Meta; +} satisfies Meta; export default meta; type Story = StoryObj; diff --git a/apps/docs/src/form/form-dropdown-menu-select.stories.tsx b/apps/docs/src/remix-hook-form/dropdown-menu-select.stories.tsx similarity index 91% rename from apps/docs/src/form/form-dropdown-menu-select.stories.tsx rename to apps/docs/src/remix-hook-form/dropdown-menu-select.stories.tsx index 75fe7a35..86a701ae 100644 --- a/apps/docs/src/form/form-dropdown-menu-select.stories.tsx +++ b/apps/docs/src/remix-hook-form/dropdown-menu-select.stories.tsx @@ -1,5 +1,5 @@ import { zodResolver } from '@hookform/resolvers/zod'; -import { FormDropdownMenuSelect } from '@lambdacurry/forms/form/form-dropdown-menu-select'; +import { DropdownMenuSelect } from '@lambdacurry/forms/remix-hook-form/dropdown-menu-select'; import { Button } from '@lambdacurry/forms/ui/button'; import { DropdownMenuItem } from '@lambdacurry/forms/ui/dropdown-menu'; import type { ActionFunctionArgs } from '@remix-run/node'; @@ -18,7 +18,7 @@ const formSchema = z.object({ type FormData = z.infer; // Component for the form -const FormDropdownMenuSelectExample = () => { +const DropdownMenuSelectExample = () => { const fetcher = useFetcher<{ message?: string }>(); const methods = useRemixForm({ resolver: zodResolver(formSchema), @@ -35,7 +35,7 @@ const FormDropdownMenuSelectExample = () => { return ( - { methods.setValue('favoriteColor', 'Red')}>Red methods.setValue('favoriteColor', 'Green')}>Green methods.setValue('favoriteColor', 'Blue')}>Blue - + @@ -69,20 +69,20 @@ const handleFormSubmission = async (request: Request) => { }; // Storybook configuration -const meta: Meta = { - title: 'Form/FormDropdownMenuSelect', - component: FormDropdownMenuSelect, +const meta: Meta = { + title: 'Form/DropdownMenuSelect', + component: DropdownMenuSelect, parameters: { layout: 'centered' }, tags: ['autodocs'], decorators: [ withRemixStubDecorator({ root: { - Component: FormDropdownMenuSelectExample, + Component: DropdownMenuSelectExample, action: async ({ request }: ActionFunctionArgs) => handleFormSubmission(request), }, }), ], -} satisfies Meta; +} satisfies Meta; export default meta; type Story = StoryObj; diff --git a/apps/docs/src/form/form-otp-input.stories.tsx b/apps/docs/src/remix-hook-form/otp-input.stories.tsx similarity index 90% rename from apps/docs/src/form/form-otp-input.stories.tsx rename to apps/docs/src/remix-hook-form/otp-input.stories.tsx index d1abae39..64a6107f 100644 --- a/apps/docs/src/form/form-otp-input.stories.tsx +++ b/apps/docs/src/remix-hook-form/otp-input.stories.tsx @@ -1,5 +1,5 @@ import { zodResolver } from '@hookform/resolvers/zod'; -import { FormOTPInputField } from '@lambdacurry/forms/form/form-otp-input'; +import { OTPInputField } from '@lambdacurry/forms/remix-hook-form/otp-input'; import { Button } from '@lambdacurry/forms/ui/button'; import type { ActionFunctionArgs } from '@remix-run/node'; import { Form, useFetcher } from '@remix-run/react'; @@ -16,7 +16,7 @@ const formSchema = z.object({ type FormData = z.infer; -const FormOTPInputExample = () => { +const OTPInputFieldExample = () => { const fetcher = useFetcher<{ message?: string }>(); const methods = useRemixForm({ resolver: zodResolver(formSchema), @@ -33,7 +33,7 @@ const FormOTPInputExample = () => { return ( - { }; // Storybook configuration -const meta: Meta = { - title: 'Form/FormOTPInput', - component: FormOTPInputField, +const meta: Meta = { + title: 'Form/FormOTPInputField', + component: OTPInputField, parameters: { layout: 'centered' }, tags: ['autodocs'], decorators: [ withRemixStubDecorator({ root: { - Component: FormOTPInputExample, + Component: OTPInputFieldExample, action: async ({ request }: ActionFunctionArgs) => handleFormSubmission(request), }, }), ], -} satisfies Meta; +} satisfies Meta; export default meta; type Story = StoryObj; diff --git a/apps/docs/src/form/form-radio-group.stories.tsx b/apps/docs/src/remix-hook-form/radio-group.stories.tsx similarity index 91% rename from apps/docs/src/form/form-radio-group.stories.tsx rename to apps/docs/src/remix-hook-form/radio-group.stories.tsx index 87a859d7..495b216d 100644 --- a/apps/docs/src/form/form-radio-group.stories.tsx +++ b/apps/docs/src/remix-hook-form/radio-group.stories.tsx @@ -1,5 +1,5 @@ import { zodResolver } from '@hookform/resolvers/zod'; -import { FormRadioGroupField } from '@lambdacurry/forms/form/form-radio-group'; +import { RadioGroupField } from '@lambdacurry/forms/remix-hook-form/radio-group'; import { Button } from '@lambdacurry/forms/ui/button'; import { RadioGroupItem } from '@lambdacurry/forms/ui/radio-group'; import type { ActionFunctionArgs } from '@remix-run/node'; @@ -18,7 +18,7 @@ const formSchema = z.object({ type FormData = z.infer; -const FormRadioGroupExample = () => { +const RadioGroupExample = () => { const fetcher = useFetcher<{ message?: string }>(); const methods = useRemixForm({ resolver: zodResolver(formSchema), @@ -35,7 +35,7 @@ const FormRadioGroupExample = () => { return ( - { - +
@@ -77,20 +77,20 @@ const handleFormSubmission = async (request: Request) => { return { message: 'Plan selected successfully' }; }; -const meta: Meta = { +const meta: Meta = { title: 'Form/FormRadioGroup', - component: FormRadioGroupField, + component: RadioGroupField, parameters: { layout: 'centered' }, tags: ['autodocs'], decorators: [ withRemixStubDecorator({ root: { - Component: FormRadioGroupExample, + Component: RadioGroupExample, action: async ({ request }: ActionFunctionArgs) => handleFormSubmission(request), }, }), ], -} satisfies Meta; +} satisfies Meta; export default meta; type Story = StoryObj; diff --git a/apps/docs/src/form/form-switch.stories.tsx b/apps/docs/src/remix-hook-form/switch.stories.tsx similarity index 90% rename from apps/docs/src/form/form-switch.stories.tsx rename to apps/docs/src/remix-hook-form/switch.stories.tsx index 8a09b870..997507a6 100644 --- a/apps/docs/src/form/form-switch.stories.tsx +++ b/apps/docs/src/remix-hook-form/switch.stories.tsx @@ -1,5 +1,5 @@ import { zodResolver } from '@hookform/resolvers/zod'; -import { FormSwitch } from '@lambdacurry/forms/form/form-switch'; +import { Switch } from '@lambdacurry/forms/remix-hook-form/switch'; import { Button } from '@lambdacurry/forms/ui/button'; import type { ActionFunctionArgs } from '@remix-run/node'; import { useFetcher } from '@remix-run/react'; @@ -36,8 +36,8 @@ const ControlledSwitchExample = () => {
- - + +
@@ -75,9 +75,9 @@ const handleFormSubmission = async (request: Request) => { }; // Storybook configuration -const meta: Meta = { - title: 'Form/FormTextField', - component: FormTextField, +const meta: Meta = { + title: 'Form/TextField', + component: TextField, parameters: { layout: 'centered' }, tags: ['autodocs'], decorators: [ @@ -93,7 +93,7 @@ const meta: Meta = { ], }), ], -} satisfies Meta; +} satisfies Meta; export default meta; type Story = StoryObj; diff --git a/apps/docs/src/form/form-textarea.stories.tsx b/apps/docs/src/remix-hook-form/textarea.stories.tsx similarity index 92% rename from apps/docs/src/form/form-textarea.stories.tsx rename to apps/docs/src/remix-hook-form/textarea.stories.tsx index 7ec1e31b..24602701 100644 --- a/apps/docs/src/form/form-textarea.stories.tsx +++ b/apps/docs/src/remix-hook-form/textarea.stories.tsx @@ -1,5 +1,5 @@ import { zodResolver } from '@hookform/resolvers/zod'; -import { FormTextarea } from '@lambdacurry/forms/form/form-textarea'; +import { Textarea } from '@lambdacurry/forms/remix-hook-form/textarea'; import { Button } from '@lambdacurry/forms/ui/button'; import type { ActionFunctionArgs } from '@remix-run/node'; import { useFetcher } from '@remix-run/react'; @@ -36,7 +36,7 @@ const ControlledTextareaExample = () => { return ( - +