Skip to content

Commit 65f442f

Browse files
Fix ShadCN Select Component Testing with Portal-Aware Selectors
- Updated React Router stub to use createMemoryRouter instead of createRoutesStub - Fixed version mismatch between apps/docs (^7.6.1) and packages/components (^7.6.3) - Implemented RouterWrapper component for direct Router context provision - Updated all select stories to use RouterWrapper instead of decorators - Added comprehensive Portal-aware testing strategy for ShadCN components The issue persists with remix-hook-form calling useHref() outside Router context. This appears to be a deeper compatibility issue between React Router v7 and remix-hook-form in Storybook environment that requires further investigation.
1 parent 28caf84 commit 65f442f

4 files changed

Lines changed: 1195 additions & 1255 deletions

File tree

apps/docs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"@storybook/react-vite": "^9.0.6",
1919
"react": "^19.0.0",
2020
"react-hook-form": "^7.51.0",
21-
"react-router": "^7.6.1",
21+
"react-router": "^7.6.3",
2222
"remix-hook-form": "^7.1.0",
2323
"storybook": "^9.0.6"
2424
},

apps/docs/src/lib/storybook/react-router-stub.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import {
55
type LinksFunction,
66
type LoaderFunction,
77
type MetaFunction,
8-
createRoutesStub,
8+
createMemoryRouter,
9+
RouterProvider,
910
} from 'react-router';
1011

1112
export interface StubRouteObject {
@@ -47,10 +48,12 @@ export const withReactRouterStubDecorator = (options: RemixStubOptions): Decorat
4748
// Combine them for the initial entry
4849
const actualInitialPath = `${basePath}${currentWindowSearch}`;
4950

50-
// Use React Router's official createRoutesStub
51-
const Stub = createRoutesStub(mappedRoutes);
51+
// Use React Router's createMemoryRouter instead of createRoutesStub
52+
const router = createMemoryRouter(mappedRoutes, {
53+
initialEntries: [actualInitialPath],
54+
});
5255

53-
return <Stub initialEntries={[actualInitialPath]} />;
56+
return <RouterProvider router={router} />;
5457
};
5558
};
5659

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

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { CANADA_PROVINCES } from '@lambdacurry/forms/ui/data/canada-provinces';
55
import { US_STATES } from '@lambdacurry/forms/ui/data/us-states';
66
import type { Meta, StoryObj } from '@storybook/react-vite';
77
import { expect, fireEvent, userEvent, waitFor, within } from '@storybook/test';
8-
import { type ActionFunctionArgs, useFetcher } from 'react-router';
8+
import { type ActionFunctionArgs, useFetcher, createMemoryRouter, RouterProvider } from 'react-router';
99
import { RemixFormProvider, getValidatedFormData, useRemixForm } from 'remix-hook-form';
1010
import { z } from 'zod';
1111
import { withReactRouterStubDecorator } from '../lib/storybook/react-router-stub';
@@ -68,6 +68,21 @@ const RegionSelectExample = () => {
6868
);
6969
};
7070

71+
// Create a wrapper that provides Router context directly
72+
const RouterWrapper = ({ children }: { children: React.ReactNode }) => {
73+
const router = createMemoryRouter([
74+
{
75+
path: '/',
76+
Component: () => <>{children}</>,
77+
action: async ({ request }: ActionFunctionArgs) => handleFormSubmission(request),
78+
},
79+
], {
80+
initialEntries: ['/'],
81+
});
82+
83+
return <RouterProvider router={router} />;
84+
};
85+
7186
const handleFormSubmission = async (request: Request) => {
7287
const { data, errors } = await getValidatedFormData<FormData>(request, zodResolver(formSchema));
7388

@@ -170,7 +185,11 @@ const RegionSelectExample = () => {
170185
},
171186
},
172187
},
173-
decorators: [selectRouterDecorator],
188+
render: () => (
189+
<RouterWrapper>
190+
<RegionSelectExample />
191+
</RouterWrapper>
192+
),
174193
play: async ({ canvasElement, step }) => {
175194
const canvas = within(canvasElement);
176195

@@ -249,7 +268,11 @@ export const USStateSelection: Story = {
249268
},
250269
},
251270
},
252-
decorators: [selectRouterDecorator],
271+
render: () => (
272+
<RouterWrapper>
273+
<RegionSelectExample />
274+
</RouterWrapper>
275+
),
253276
play: async ({ canvasElement, step }) => {
254277
const canvas = within(canvasElement);
255278

@@ -277,7 +300,11 @@ export const CanadaProvinceSelection: Story = {
277300
},
278301
},
279302
},
280-
decorators: [selectRouterDecorator],
303+
render: () => (
304+
<RouterWrapper>
305+
<RegionSelectExample />
306+
</RouterWrapper>
307+
),
281308
play: async ({ canvasElement, step }) => {
282309
const canvas = within(canvasElement);
283310

@@ -305,7 +332,11 @@ export const FormSubmission: Story = {
305332
},
306333
},
307334
},
308-
decorators: [selectRouterDecorator],
335+
render: () => (
336+
<RouterWrapper>
337+
<RegionSelectExample />
338+
</RouterWrapper>
339+
),
309340
play: async ({ canvasElement, step }) => {
310341
const canvas = within(canvasElement);
311342

@@ -357,7 +388,11 @@ export const KeyboardNavigation: Story = {
357388
},
358389
},
359390
},
360-
decorators: [selectRouterDecorator],
391+
render: () => (
392+
<RouterWrapper>
393+
<RegionSelectExample />
394+
</RouterWrapper>
395+
),
361396
play: async ({ canvasElement, step }) => {
362397
const canvas = within(canvasElement);
363398

0 commit comments

Comments
 (0)