Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
38ddefa
Implement Data Table Component with ShadCN and TanStack (LC-187)
codegen-sh[bot] Apr 1, 2025
d6ca858
Implement DataTableRouterForm and remove Input component (LC-187)
codegen-sh[bot] Apr 1, 2025
5969c41
add tanstack to package json
jaruesink Apr 1, 2025
99c3e80
Refactor data table components to use type imports for React and TanS…
jaruesink Apr 1, 2025
0bdc864
Update dependencies in package.json and yarn.lock; add new Radix UI c…
jaruesink Apr 1, 2025
a447b90
Refactor ControlledTextFieldExample in text-field stories; remove unu…
jaruesink Apr 1, 2025
546f305
Implement DataTable with nuqs and zod filters
codegen-sh[bot] Apr 1, 2025
6582d67
Implement nuqs and zod filters for DataTableRouterForm
codegen-sh[bot] Apr 1, 2025
a10a823
Update nuqs to version 2.4.1; integrate nuqs for URL state management…
jaruesink Apr 2, 2025
64b88e8
Update story title for DataTableRouterForm to follow naming conventio…
jaruesink Apr 2, 2025
f160ce7
Merge branch 'codegen-data-table-lc187' of github.com:lambda-curry/fo…
jaruesink Apr 2, 2025
b93512e
merge conflicts
jaruesink Apr 2, 2025
decae8a
merge conflicts
jaruesink Apr 2, 2025
3221e2c
Merge pull request #48 from lambda-curry/codegen-data-table-nuqs-zod-…
jaruesink Apr 2, 2025
643eb29
Update package.json and turbo.json scripts; add new serve and prerele…
jaruesink Apr 2, 2025
4bbad97
Refactor custom components in form stories to use functional componen…
jaruesink Apr 4, 2025
2dae38f
Enhance radio group component with options prop for automatic item ge…
jaruesink Apr 5, 2025
4765a95
Refactor OTP input story to simplify OTP entry by directly typing int…
jaruesink Apr 5, 2025
fe3d78a
Enhance date picker story with improved button selection and dialog h…
jaruesink Apr 5, 2025
19f5069
Add build-storybook task to turbo.json and remove Storybook build ste…
jaruesink Apr 5, 2025
1ede9c2
Add build-storybook script to package.json for improved Storybook bui…
jaruesink Apr 5, 2025
7b3388a
Update remix-hook-form to v7.0.0 for React Router 7 compatibility
codegen-sh[bot] Apr 6, 2025
3cd7a1c
Remove middleware.ts file and update imports to use remix-hook-form/m…
codegen-sh[bot] Apr 6, 2025
0b4822e
Address PR review comments: Use direct imports from remix-hook-form/m…
codegen-sh[bot] Apr 6, 2025
f0c0fd5
Merge pull request #50 from lambda-curry/codegen-update-remix-hook-fo…
jaruesink Apr 8, 2025
2f065e2
Replace nuqs with React Router 7 features in data table component
codegen-sh[bot] Apr 16, 2025
fa05b58
Address PR feedback: restore original package.json and create custom …
codegen-sh[bot] Apr 16, 2025
30d609f
yarn lock update
jaruesink Apr 16, 2025
835242a
Replace nuqs with React Router 7 features in all components
codegen-sh[bot] Apr 16, 2025
f2b051b
Fix build error and improve data table component
codegen-sh[bot] Apr 16, 2025
d21afd9
Remove action function from data table story and rely solely on loade…
codegen-sh[bot] Apr 17, 2025
08a4244
Fix data table storybook story to show mock data
Apr 17, 2025
176e787
Merge pull request #52 from lambda-curry/codegen-bot/3a34e1bf
jaruesink Apr 17, 2025
09d5381
Fix data table storybook story to show mock data
codegen-sh[bot] Apr 17, 2025
76b4578
Merge pull request #54 from lambda-curry/codegen-bot/fix-data-table-s…
jaruesink Apr 17, 2025
0bf634e
Refactor data table components to improve loading state and paginatio…
jaruesink Apr 17, 2025
222fc4b
Enhance DropdownMenuSelect component with context for item selection …
jaruesink Apr 17, 2025
b256fa8
Merge pull request #51 from lambda-curry/codegen-replace-nuqs-with-re…
jaruesink Apr 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,6 @@ jobs:
- name: Install Playwright Chromium
run: npx playwright install chromium

- name: Build Storybook
run: yarn build-storybook

- name: Start Storybook server
run: yarn workspace @lambdacurry/forms-docs storybook --ci --port 6006 &
env:
NODE_OPTIONS: --max-old-space-size=4096

- name: Wait for Storybook to be ready
run: npx wait-on http://localhost:6006

- name: Run tests
run: yarn test

Expand Down
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"hookform",
"isbot",
"lucide",
"Nuqs",
"shadcn",
"sonner"
],
Expand All @@ -20,5 +21,6 @@
"editor.codeActionsOnSave": {
"source.fixAll.biome": "explicit",
"source.organizeImports.biome": "explicit"
}
},
"tailwindCSS.classAttributes": ["class", "className", "ngClass", "class:list", "wrapperClassName"]
}
9 changes: 7 additions & 2 deletions apps/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
"build": "storybook build",
"build-storybook": "storybook build",
"storybook": "storybook dev -p 6006",
"test": "test-storybook"
"serve": "http-server ./storybook-static -p 6006 -s",
"test": "start-server-and-test serve http://127.0.0.1:6006 'test-storybook --url http://127.0.0.1:6006'",
"test:local": "test-storybook"
},
"dependencies": {
"@lambdacurry/forms": "*",
Expand All @@ -28,12 +30,15 @@
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"autoprefixer": "^10.4.20",
"http-server": "^14.1.1",
"react": "^19.0.0",
"react-router": "^7.0.0",
"react-router-dom": "^7.0.0",
"start-server-and-test": "^2.0.11",
"tailwindcss": "^4.0.0",
"typescript": "^5.7.2",
"vite": "^6.2.2",
"vite-tsconfig-paths": "^5.1.4"
"vite-tsconfig-paths": "^5.1.4",
"wait-on": "^8.0.3"
}
}
79 changes: 79 additions & 0 deletions apps/docs/src/examples/middleware-example.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Example of using the new middleware feature in remix-hook-form v7.0.0
import { Form } from 'react-router';
import { RemixFormProvider, useRemixForm } from 'remix-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as zod from 'zod';
import { TextField } from '@lambdacurry/forms/remix-hook-form';
import { getValidatedFormData } from 'remix-hook-form/middleware';
import type { ActionFunctionArgs } from 'react-router';

// Define schema and types
const schema = zod.object({
name: zod.string().min(1, "Name is required"),
email: zod.string().email("Invalid email format").min(1, "Email is required"),
});

type FormData = zod.infer<typeof schema>;
const resolver = zodResolver(schema);

// Action function using the new middleware
export const action = async ({ context }: ActionFunctionArgs) => {
// Use the middleware to extract and validate form data
const { errors, data, receivedValues } = await getValidatedFormData<FormData>(
context,
resolver
);

if (errors) {
return { errors, defaultValues: receivedValues };
}

// Process the validated data
console.log('Processing data:', data);

return { success: true, data };
};

// Component
export default function MiddlewareExample() {
const {
handleSubmit,
formState: { errors },
register,
} = useRemixForm<FormData>({
mode: "onSubmit",
resolver,
});

return (
<div className="p-4">
<h1 className="text-2xl font-bold mb-4">Remix Hook Form v7 Middleware Example</h1>

<RemixFormProvider>
<Form method="POST" onSubmit={handleSubmit}>
<div className="space-y-4">
<TextField
label="Name"
{...register("name")}
error={errors.name?.message}
/>

<TextField
label="Email"
type="email"
{...register("email")}
error={errors.email?.message}
/>

<button
type="submit"
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
Submit
</button>
</div>
</Form>
</RemixFormProvider>
</div>
);
}
23 changes: 23 additions & 0 deletions apps/docs/src/examples/root-example.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Example of setting up the middleware in root.tsx
import { unstable_extractFormDataMiddleware } from 'remix-hook-form/middleware';
import { Outlet } from 'react-router-dom';

// Export the middleware for React Router 7
export const unstable_middleware = [unstable_extractFormDataMiddleware()];

export default function Root() {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Remix Hook Form v7 Example</title>
</head>
<body>
<div className="container mx-auto">
<Outlet />
</div>
</body>
</html>
);
}
47 changes: 38 additions & 9 deletions apps/docs/src/lib/storybook/react-router-stub.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
type LoaderFunction,
type MetaFunction,
type NonIndexRouteObject,
createRoutesStub,
RouterProvider,
createMemoryRouter,
} from 'react-router-dom';

export type StubRouteObject = StubIndexRouteObject | StubNonIndexRouteObject;
Expand Down Expand Up @@ -36,21 +37,49 @@ interface StubIndexRouteObject

interface RemixStubOptions {
routes: StubRouteObject[];
initialPath?: string;
}

export const withReactRouterStubDecorator = (options: RemixStubOptions): Decorator => {
const { routes } = options;
return (Story) => {
// Map routes to include Story component as fallback if no Component provided
const { routes, initialPath = '/' } = options;
// This outer function runs once when Storybook loads the story meta

return (Story, context) => {
// This inner function runs when the story component actually renders
const mappedRoutes = routes.map((route) => ({
...route,
Component: route.Component ?? (() => <Story />),
Component: route.Component ?? (() => <Story {...context.args} />),
}));

// Use more specific type assertion to fix the incompatibility
// @ts-ignore - Types from createRoutesStub are incompatible but the code works at runtime
const RemixStub = createRoutesStub(mappedRoutes);
// Get the base path (without existing query params from options)
const basePath = initialPath.split('?')[0];

// Get the current search string from the actual browser window, if available
// If not available, use a default search string with parameters needed for the data table
const currentWindowSearch = typeof window !== 'undefined'
? window.location.search
: '?page=0&pageSize=10';

// Combine them for the initial entry
const actualInitialPath = `${basePath}${currentWindowSearch}`;

// Create a memory router, initializing it with the path derived from the window's search params
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
const router = createMemoryRouter(mappedRoutes as any, {
initialEntries: [actualInitialPath], // Use the path combined with window.location.search
});

return <RemixStub initialEntries={['/']} />;
return <RouterProvider router={router} />;
};
};

/**
* A decorator that provides URL state management for stories
* Use this when you need URL query parameters in your stories
*/
export const withURLState = (initialPath = '/'): Decorator => {
return withReactRouterStubDecorator({
routes: [{ path: '/' }],
initialPath,
});
};
Loading