Skip to content

Commit 591cb12

Browse files
committed
chore: update typecheck command and dependencies in package.json
- Modified the typecheck command in the root package.json to run without filtering, ensuring all packages are checked. - Updated dependencies in apps/docs/package.json and yarn.lock to include @testing-library/jest-dom, @testing-library/react, and @types/jest for improved testing support. - Adjusted tsconfig.json in apps/docs to include additional types for better type resolution. - Enhanced various story files to utilize the within utility for improved testing practices.
1 parent af19345 commit 591cb12

17 files changed

+241
-62
lines changed

.vscode/settings.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,8 @@
2424
"source.fixAll.biome": "explicit",
2525
"source.organizeImports.biome": "explicit"
2626
},
27-
"tailwindCSS.classAttributes": ["class", "className", "ngClass", "class:list", "wrapperClassName"]
27+
"tailwindCSS.classAttributes": ["class", "className", "ngClass", "class:list", "wrapperClassName"],
28+
"[jsonc]": {
29+
"editor.defaultFormatter": "biomejs.biome"
30+
}
2831
}

apps/docs/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
"@storybook/testing-library": "^0.2.2",
3232
"@tailwindcss/postcss": "^4.1.8",
3333
"@tailwindcss/vite": "^4.0.0",
34+
"@testing-library/jest-dom": "^6.8.0",
35+
"@testing-library/react": "^16.3.0",
36+
"@types/jest": "^30.0.0",
3437
"@types/react": "^19.0.0",
3538
"@typescript-eslint/eslint-plugin": "^6.21.0",
3639
"@typescript-eslint/parser": "^6.21.0",

apps/docs/src/examples/middleware-example.tsx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,7 @@ export const action = async ({ context }: ActionFunctionArgs) => {
3333

3434
// Component
3535
export default function MiddlewareExample() {
36-
const {
37-
handleSubmit,
38-
formState: { errors },
39-
register,
40-
} = useRemixForm<FormData>({
36+
const methods = useRemixForm<FormData>({
4137
mode: 'onSubmit',
4238
resolver,
4339
});
@@ -46,12 +42,12 @@ export default function MiddlewareExample() {
4642
<div className="p-4">
4743
<h1 className="text-2xl font-bold mb-4">Remix Hook Form v7 Middleware Example</h1>
4844

49-
<RemixFormProvider>
50-
<Form method="POST" onSubmit={handleSubmit}>
45+
<RemixFormProvider {...methods}>
46+
<Form method="POST" onSubmit={methods.handleSubmit}>
5147
<div className="space-y-4">
52-
<TextField label="Name" {...register('name')} error={errors.name?.message} />
48+
<TextField name="name" label="Name" />
5349

54-
<TextField label="Email" type="email" {...register('email')} error={errors.email?.message} />
50+
<TextField name="email" type="email" label="Email" />
5551

5652
<button type="submit" className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
5753
Submit

apps/docs/src/remix-hook-form/checkbox-list.stories.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Checkbox } from '@lambdacurry/forms/remix-hook-form/checkbox';
33
import { FormMessage } from '@lambdacurry/forms/remix-hook-form/form';
44
import { Button } from '@lambdacurry/forms/ui/button';
55
import type { Meta, StoryObj } from '@storybook/react-vite';
6-
import { expect, userEvent, type within } from '@storybook/test';
6+
import { expect, userEvent, within } from '@storybook/test';
77
import { type ActionFunctionArgs, Form, useFetcher } from 'react-router';
88
import { createFormData, getValidatedFormData, RemixFormProvider, useRemixForm } from 'remix-hook-form';
99
import { z } from 'zod';
@@ -137,18 +137,18 @@ const meta: Meta<typeof Checkbox> = {
137137
export default meta;
138138
type Story = StoryObj<typeof meta>;
139139

140-
interface StoryContext {
141-
canvas: ReturnType<typeof within>;
142-
}
140+
type StoryContext = { canvasElement: HTMLElement };
143141

144-
const testDefaultValues = ({ canvas }: StoryContext) => {
142+
const testDefaultValues = ({ canvasElement }: StoryContext) => {
143+
const canvas = within(canvasElement);
145144
AVAILABLE_COLORS.forEach(({ label }) => {
146145
const checkbox = canvas.getByLabelText(label);
147146
expect(checkbox).not.toBeChecked();
148147
});
149148
};
150149

151-
const testErrorState = async ({ canvas }: StoryContext) => {
150+
const testErrorState = async ({ canvasElement }: StoryContext) => {
151+
const canvas = within(canvasElement);
152152
// Submit form without selecting any colors
153153
const submitButton = canvas.getByRole('button', { name: 'Submit' });
154154
await userEvent.click(submitButton);
@@ -157,7 +157,8 @@ const testErrorState = async ({ canvas }: StoryContext) => {
157157
await expect(await canvas.findByText('Please select at least one color')).toBeInTheDocument();
158158
};
159159

160-
const testColorSelection = async ({ canvas }: StoryContext) => {
160+
const testColorSelection = async ({ canvasElement }: StoryContext) => {
161+
const canvas = within(canvasElement);
161162
// Select two colors
162163
const redCheckbox = canvas.getByLabelText('Red');
163164
const blueCheckbox = canvas.getByLabelText('Blue');

apps/docs/src/remix-hook-form/data-table/data-table-filter-accessibility-tests.stories.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useDataTableFilters } from '@lambdacurry/forms/ui/data-table-filter/hoo
44
import { useFilterSync } from '@lambdacurry/forms/ui/utils/use-filter-sync';
55
import { CheckCircledIcon, PersonIcon, StarIcon, TextIcon } from '@radix-ui/react-icons';
66
import type { Meta, StoryContext, StoryObj } from '@storybook/react';
7-
import { expect } from '@storybook/test';
7+
import { expect, within } from '@storybook/test';
88
import { withReactRouterStubDecorator } from '../../lib/storybook/react-router-stub';
99

1010
/**
@@ -228,15 +228,17 @@ type Story = StoryObj<typeof meta>;
228228
/**
229229
* Test functions for accessibility testing
230230
*/
231-
const testBasicRendering = ({ canvas }: StoryContext) => {
231+
const testBasicRendering = ({ canvasElement }: StoryContext) => {
232+
const canvas = within(canvasElement);
232233
const title = canvas.getByText('Data Table Filter Accessibility Test');
233234
expect(title).toBeInTheDocument();
234235

235236
const filterInterface = canvas.getByText('Filter Interface');
236237
expect(filterInterface).toBeInTheDocument();
237238
};
238239

239-
const testKeyboardNavigation = async ({ canvas }: StoryContext) => {
240+
const testKeyboardNavigation = async ({ canvasElement }: StoryContext) => {
241+
const canvas = within(canvasElement);
240242
// Look for filter-related buttons or elements
241243
const buttons = canvas.getAllByRole('button');
242244
await expect(buttons.length).toBeGreaterThan(0);
@@ -248,7 +250,8 @@ const testKeyboardNavigation = async ({ canvas }: StoryContext) => {
248250
}
249251
};
250252

251-
const testAriaAttributes = async ({ canvas }: StoryContext) => {
253+
const testAriaAttributes = async ({ canvasElement }: StoryContext) => {
254+
const canvas = within(canvasElement);
252255
// Test that interactive elements have proper roles
253256
const buttons = canvas.getAllByRole('button');
254257
expect(buttons.length).toBeGreaterThan(0);

apps/docs/src/remix-hook-form/data-table/data-table-router-form.stories.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,8 @@ export default meta;
302302
type Story = StoryObj<typeof meta>;
303303

304304
export const Default: Story = {
305-
args: {} satisfies Record<string, unknown>, // Args for DataTableRouterForm if needed, handled by Example component
305+
// biome-ignore lint/suspicious/noExplicitAny: any for flexibility
306+
args: {} as any,
306307
render: () => <DataTableRouterFormExample />,
307308
parameters: {
308309
docs: {

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

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import { render, screen } from '@testing-library/react';
55
import { useFetcher } from 'react-router';
66
import { RemixFormProvider, useRemixForm } from 'remix-hook-form';
77
import { z } from 'zod';
8-
import type { ElementType, PropsWithChildren } from 'react';
8+
import type { ElementType } from 'react';
9+
import type { FetcherWithComponents } from 'react-router';
10+
import type { FormMessageProps } from '@lambdacurry/forms/ui/form';
911

1012
// Mock useFetcher
1113
jest.mock('react-router', () => ({
@@ -31,15 +33,15 @@ const TestFormWithError = ({
3133
}: {
3234
initialErrors?: Record<string, { message: string }>;
3335
formErrorName?: string;
34-
customComponents?: { FormMessage?: React.ComponentType<PropsWithChildren<Record<string, unknown>>> };
36+
customComponents?: { FormMessage?: React.ComponentType<FormMessageProps> };
3537
className?: string;
3638
}) => {
3739
const mockFetcher = {
3840
data: { errors: initialErrors },
3941
state: 'idle' as const,
4042
submit: jest.fn(),
4143
Form: 'form' as ElementType,
42-
};
44+
} as unknown as FetcherWithComponents<unknown>;
4345

4446
mockUseFetcher.mockReturnValue(mockFetcher);
4547

@@ -143,9 +145,9 @@ describe('FormError Component', () => {
143145

144146
describe('Component Customization', () => {
145147
it('uses custom FormMessage component when provided', () => {
146-
const CustomFormMessage = ({ children, ...props }: PropsWithChildren<Record<string, unknown>>) => (
148+
const CustomFormMessage = (props: FormMessageProps) => (
147149
<div data-testid="custom-form-message" className="custom-message" {...props}>
148-
Custom: {children}
150+
Custom: {props.children}
149151
</div>
150152
);
151153

@@ -218,7 +220,7 @@ describe('FormError Component', () => {
218220
state: 'idle' as const,
219221
submit: jest.fn(),
220222
Form: 'form' as ElementType,
221-
};
223+
} as unknown as FetcherWithComponents<unknown>;
222224

223225
mockUseFetcher.mockReturnValue(mockFetcher);
224226

@@ -315,9 +317,9 @@ describe('FormError Component', () => {
315317
it('does not re-render unnecessarily when unrelated form state changes', () => {
316318
const renderSpy = jest.fn();
317319

318-
const CustomFormMessage = ({ children, ...props }: PropsWithChildren<Record<string, unknown>>) => {
320+
const CustomFormMessage = (props: FormMessageProps) => {
319321
renderSpy();
320-
return <div {...props}>{children}</div>;
322+
return <div {...props}>{props.children}</div>;
321323
};
322324

323325
const errors = {
@@ -346,7 +348,7 @@ describe('FormError Integration Tests', () => {
346348
state: 'idle' as const,
347349
submit: jest.fn(),
348350
Form: 'form' as ElementType,
349-
};
351+
} as unknown as FetcherWithComponents<unknown>;
350352

351353
mockUseFetcher.mockReturnValue(mockFetcher);
352354

apps/docs/src/remix-hook-form/password-field.stories.tsx

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
22
import { PasswordField } from '@lambdacurry/forms/remix-hook-form/password-field';
33
import { Button } from '@lambdacurry/forms/ui/button';
44
import type { Meta, StoryContext, StoryObj } from '@storybook/react-vite';
5-
import { expect, userEvent } from '@storybook/test';
5+
import { expect, userEvent, within } from '@storybook/test';
66
import { useRef } from 'react';
77
import { type ActionFunctionArgs, useFetcher } from 'react-router';
88
import { getValidatedFormData, RemixFormProvider, useRemixForm } from 'remix-hook-form';
@@ -114,14 +114,16 @@ export default meta;
114114
type Story = StoryObj<typeof meta>;
115115

116116
// Test scenarios
117-
const testDefaultValues = ({ canvas }: StoryContext) => {
117+
const testDefaultValues = ({ canvasElement }: StoryContext) => {
118+
const canvas = within(canvasElement);
118119
const passwordInput = canvas.getByLabelText('Password');
119120
const confirmInput = canvas.getByLabelText('Confirm Password');
120121
expect(passwordInput).toHaveValue(INITIAL_PASSWORD);
121122
expect(confirmInput).toHaveValue(INITIAL_PASSWORD);
122123
};
123124

124-
const testPasswordVisibilityToggle = async ({ canvas }: StoryContext) => {
125+
const testPasswordVisibilityToggle = async ({ canvasElement }: StoryContext) => {
126+
const canvas = within(canvasElement);
125127
const passwordInput = canvas.getByLabelText('Password');
126128

127129
// Find the toggle button within the same form item as the password input
@@ -150,7 +152,8 @@ const testPasswordVisibilityToggle = async ({ canvas }: StoryContext) => {
150152
expect(showButtonAgain).toBeInTheDocument();
151153
};
152154

153-
const testWeakPasswordValidation = async ({ canvas }: StoryContext) => {
155+
const testWeakPasswordValidation = async ({ canvasElement }: StoryContext) => {
156+
const canvas = within(canvasElement);
154157
const passwordInput = canvas.getByLabelText('Password');
155158
const submitButton = canvas.getByRole('button', { name: 'Create Account' });
156159

@@ -162,7 +165,8 @@ const testWeakPasswordValidation = async ({ canvas }: StoryContext) => {
162165
await expect(await canvas.findByText(WEAK_PASSWORD_ERROR)).toBeInTheDocument();
163166
};
164167

165-
const testPasswordMismatchValidation = async ({ canvas }: StoryContext) => {
168+
const testPasswordMismatchValidation = async ({ canvasElement }: StoryContext) => {
169+
const canvas = within(canvasElement);
166170
const passwordInput = canvas.getByLabelText('Password');
167171
const confirmInput = canvas.getByLabelText('Confirm Password');
168172
const submitButton = canvas.getByRole('button', { name: 'Create Account' });
@@ -180,7 +184,8 @@ const testPasswordMismatchValidation = async ({ canvas }: StoryContext) => {
180184
await expect(await canvas.findByText(MISMATCH_PASSWORD_ERROR)).toBeInTheDocument();
181185
};
182186

183-
const testValidSubmission = async ({ canvas }: StoryContext) => {
187+
const testValidSubmission = async ({ canvasElement }: StoryContext) => {
188+
const canvas = within(canvasElement);
184189
const passwordInput = canvas.getByLabelText('Password');
185190
const confirmInput = canvas.getByLabelText('Confirm Password');
186191
const submitButton = canvas.getByRole('button', { name: 'Create Account' });
@@ -199,7 +204,8 @@ const testValidSubmission = async ({ canvas }: StoryContext) => {
199204
expect(successMessage).toBeInTheDocument();
200205
};
201206

202-
const testRefFunctionality = async ({ canvas }: StoryContext) => {
207+
const testRefFunctionality = async ({ canvasElement }: StoryContext) => {
208+
const canvas = within(canvasElement);
203209
const refInput = canvas.getByLabelText('Ref Example');
204210
const focusButton = canvas.getByRole('button', { name: 'Focus' });
205211

apps/docs/src/remix-hook-form/phone-input.test.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import userEvent from '@testing-library/user-event';
66
import { useFetcher } from 'react-router';
77
import { RemixFormProvider, useRemixForm } from 'remix-hook-form';
88
import { z } from 'zod';
9-
import type { ElementType, PropsWithChildren } from 'react';
9+
import type { ElementType } from 'react';
10+
import type { FetcherWithComponents } from 'react-router';
11+
import type { FormMessageProps } from '@lambdacurry/forms/ui/form';
1012

1113
// Mock useFetcher
1214
jest.mock('react-router', () => ({
@@ -29,14 +31,14 @@ const TestPhoneInputForm = ({
2931
customComponents = {},
3032
}: {
3133
initialErrors?: Record<string, { message: string }>;
32-
customComponents?: { FormMessage?: React.ComponentType<PropsWithChildren<Record<string, unknown>>> };
34+
customComponents?: { FormMessage?: React.ComponentType<FormMessageProps> };
3335
}) => {
3436
const mockFetcher = {
3537
data: { errors: initialErrors },
3638
state: 'idle' as const,
3739
submit: jest.fn(),
3840
Form: 'form' as ElementType,
39-
};
41+
} as unknown as FetcherWithComponents<unknown>;
4042

4143
mockUseFetcher.mockReturnValue(mockFetcher);
4244

@@ -152,9 +154,9 @@ describe('PhoneInput Component', () => {
152154

153155
describe('Component Customization', () => {
154156
it('uses custom FormMessage component when provided', () => {
155-
const CustomFormMessage = ({ children, ...props }: PropsWithChildren<Record<string, unknown>>) => (
157+
const CustomFormMessage = (props: FormMessageProps) => (
156158
<div data-testid="custom-form-message" className="custom-message" {...props}>
157-
Custom: {children}
159+
Custom: {props.children}
158160
</div>
159161
);
160162

apps/docs/src/remix-hook-form/radio-group.stories.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { expect, userEvent, within } from '@storybook/test';
77
import { type ActionFunctionArgs, Form, useFetcher } from 'react-router';
88
import { getValidatedFormData, RemixFormProvider, useRemixForm } from 'remix-hook-form';
99
import { z } from 'zod';
10+
import type { ComponentType, ComponentPropsWithoutRef } from 'react';
11+
import { Label } from '@lambdacurry/forms/ui/label';
1012
import { withReactRouterStubDecorator } from '../lib/storybook/react-router-stub';
1113

1214
const AVAILABLE_SIZES: RadioOption[] = [

0 commit comments

Comments
 (0)