From 8d5f9347fd4ebb0a041d2aa2147715a2ccab3e4d Mon Sep 17 00:00:00 2001 From: "codegen-sh[bot]" <131295404+codegen-sh[bot]@users.noreply.github.com> Date: Fri, 19 Sep 2025 17:14:27 +0000 Subject: [PATCH 1/2] feat(remix-hook-form): add HiddenField minimal component and Storybook docs\n\n- New HiddenField registers with remix-hook-form but renders only a native to avoid extra DOM\n- Export from remix-hook-form barrel\n- Add Storybook story demonstrating defaultValues submission and usage alongside visible fields --- .../remix-hook-form/hidden-field.stories.tsx | 98 +++++++++++++++++++ .../src/remix-hook-form/hidden-field.tsx | 35 +++++++ .../components/src/remix-hook-form/index.ts | 1 + 3 files changed, 134 insertions(+) create mode 100644 apps/docs/src/remix-hook-form/hidden-field.stories.tsx create mode 100644 packages/components/src/remix-hook-form/hidden-field.tsx diff --git a/apps/docs/src/remix-hook-form/hidden-field.stories.tsx b/apps/docs/src/remix-hook-form/hidden-field.stories.tsx new file mode 100644 index 00000000..86406cf3 --- /dev/null +++ b/apps/docs/src/remix-hook-form/hidden-field.stories.tsx @@ -0,0 +1,98 @@ +import { zodResolver } from '@hookform/resolvers/zod'; +import { HiddenField } from '@lambdacurry/forms/remix-hook-form/hidden-field'; +import { TextField } from '@lambdacurry/forms/remix-hook-form/text-field'; +import { Button } from '@lambdacurry/forms/ui/button'; +import type { Meta, StoryObj } from '@storybook/react-vite'; +import { type ActionFunctionArgs, useFetcher } from 'react-router'; +import { RemixFormProvider, getValidatedFormData, useRemixForm } from 'remix-hook-form'; +import { z } from 'zod'; +import { withReactRouterStubDecorator } from '../lib/storybook/react-router-stub'; + +const formSchema = z.object({ + // Hidden field still participates in validation and submission + lineItemId: z.string().min(1, 'Missing line item id'), + note: z.string().optional(), +}); + +type FormData = z.infer; + +const DEFAULT_ID = 'abc-123'; + +const HiddenFieldExample = () => { + const fetcher = useFetcher<{ message: string; submittedId: string }>(); + const methods = useRemixForm({ + resolver: zodResolver(formSchema), + defaultValues: { + lineItemId: DEFAULT_ID, + note: '', + }, + fetcher, + submitConfig: { + action: '/', + method: 'post', + }, + }); + + return ( + + + {/* This registers the field without rendering any extra DOM */} + + + {/* A visible field just to demonstrate normal layout around the hidden input */} +
+ + + {fetcher.data?.message && ( +

+ {fetcher.data.message} – submittedId: {fetcher.data.submittedId} +

+ )} +
+
+
+ ); +}; + +const handleFormSubmission = async (request: Request) => { + const { data, errors } = await getValidatedFormData(request, zodResolver(formSchema)); + + if (errors) { + return { errors }; + } + + return { message: 'Form submitted successfully', submittedId: data.lineItemId }; +}; + +const meta: Meta = { + title: 'RemixHookForm/HiddenField', + component: HiddenField, + parameters: { layout: 'centered' }, + tags: ['autodocs'], + decorators: [ + withReactRouterStubDecorator({ + routes: [ + { + path: '/', + Component: HiddenFieldExample, + action: async ({ request }: ActionFunctionArgs) => handleFormSubmission(request), + }, + ], + }), + ], +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + parameters: { + docs: { + description: { + story: + 'HiddenField renders a plain registered with remix-hook-form. Use it to submit values that should not impact layout or be visible to users.', + }, + }, + }, +}; diff --git a/packages/components/src/remix-hook-form/hidden-field.tsx b/packages/components/src/remix-hook-form/hidden-field.tsx new file mode 100644 index 00000000..a2ffb446 --- /dev/null +++ b/packages/components/src/remix-hook-form/hidden-field.tsx @@ -0,0 +1,35 @@ +import type * as React from 'react'; +import type { FieldPath, FieldValues, RegisterOptions } from 'react-hook-form'; +import { useRemixFormContext } from 'remix-hook-form'; + +export interface HiddenFieldProps extends Omit, 'name' | 'type'> { + /** + * The field name to register with react-hook-form + */ + name: FieldPath; + /** + * Optional register options to pass to react-hook-form's register + */ + registerOptions?: RegisterOptions; +} + +/** + * HiddenField + * + * A minimal field that only renders a native hidden input and registers it with remix-hook-form. + * This avoids any extra DOM wrappers so it won't affect page layout. + */ +export const HiddenField = function HiddenField({ + name, + registerOptions, + ref, + ...props +}: HiddenFieldProps & { ref?: React.Ref }) { + const { register } = useRemixFormContext(); + + // Intentionally render a plain input to avoid any additional wrappers or styling + return ; +}; + +HiddenField.displayName = 'HiddenField'; +export type { HiddenFieldProps as HiddenInputProps }; // legacy-friendly alias diff --git a/packages/components/src/remix-hook-form/index.ts b/packages/components/src/remix-hook-form/index.ts index 1d40c1d9..7ac43f64 100644 --- a/packages/components/src/remix-hook-form/index.ts +++ b/packages/components/src/remix-hook-form/index.ts @@ -22,3 +22,4 @@ export * from './use-data-table-url-state'; export * from './select'; export * from './us-state-select'; export * from './canada-province-select'; +export * from './hidden-field'; From 6533d4bbfa208a66e6902ff596ba999fe5372fd5 Mon Sep 17 00:00:00 2001 From: "codegen-sh[bot]" <131295404+codegen-sh[bot]@users.noreply.github.com> Date: Fri, 19 Sep 2025 17:57:06 +0000 Subject: [PATCH 2/2] chore(components): bump @lambdacurry/forms to 0.21.0 (add HiddenField component)\n\nSummary: Adds new HiddenField that registers with remix-hook-form but renders only a native to avoid layout impact. Also exports from barrel and adds Storybook usage doc. --- packages/components/package.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/components/package.json b/packages/components/package.json index 921df27d..46964cd6 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@lambdacurry/forms", - "version": "0.20.1", + "version": "0.21.0", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -26,9 +26,7 @@ "import": "./dist/ui/*.js" } }, - "files": [ - "dist" - ], + "files": ["dist"], "scripts": { "prepublishOnly": "yarn run build", "build": "vite build",