Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
90db72d
feat: medusa-forms
dwene May 30, 2025
f218347
wip: storybook
dwene May 30, 2025
4a054aa
chore: update dependencies and improve medusa-forms structure
jaruesink May 30, 2025
d05ea74
chore: update dependencies and refactor Storybook configuration
jaruesink May 30, 2025
3c4b798
chore: remove unused dependencies and clean up configuration
jaruesink May 30, 2025
b837ce5
chore: update dependencies and improve styles
jaruesink May 31, 2025
a5c0926
Merge pull request #78 from lambda-curry/medusa-forms-upgrades
jaruesink May 31, 2025
d6819ba
Add form input components with field wrapper for Medusa UI
cursoragent May 31, 2025
2f4a280
Resolve merge conflicts: keep React 19 patterns without forwardRef
codegen-sh[bot] May 31, 2025
9d8b33c
Merge pull request #80 from lambda-curry/cursor/update-components-to-…
jaruesink May 31, 2025
c2e0790
refactor: update imports to use type-only imports for React in form c…
jaruesink May 31, 2025
b446142
Initial scaffolding for controlled components stories
codegen-sh[bot] May 31, 2025
4cb0966
feat: Add comprehensive ControlledTextArea Storybook stories
codegen-sh[bot] May 31, 2025
bd9ecb9
feat: add comprehensive form integration examples
codegen-sh[bot] May 31, 2025
d23ebab
feat: Add ControlledTextArea stories (merged from LC-245)
codegen-sh[bot] May 31, 2025
25359e0
feat: Add ControlledCurrencyInput and ControlledDatePicker stories
codegen-sh[bot] May 31, 2025
248f6ae
feat: Add ControlledCheckbox stories (merged from LC-243)
codegen-sh[bot] May 31, 2025
871b81d
feat: Add comprehensive ControlledSelect Storybook stories
codegen-sh[bot] May 31, 2025
eb24bde
fix: Resolve identifier conflicts and import issues in Storybook stories
codegen-sh[bot] May 31, 2025
7f02d10
Merge pull request #83 from lambda-curry/codegen/lc-246-create-comple…
jaruesink May 31, 2025
a00c074
Merge pull request #82 from lambda-curry/codegen/lc-245-create-contro…
jaruesink May 31, 2025
a790cdc
Merge pull request #86 from lambda-curry/codegen/lc-241-create-contro…
jaruesink May 31, 2025
67fe131
Merge pull request #85 from lambda-curry/codegen/lc-240-medusa-forms-…
jaruesink May 31, 2025
0d48c37
feat: Enhance ControlledCurrencyInput and FormIntegrationExamples sto…
jaruesink May 31, 2025
bf5c98b
feat: Refactor form components in FormIntegrationExamples to use Butt…
jaruesink May 31, 2025
cb26e17
Add cursor rules for Medusa Forms components and stories
codegen-sh[bot] May 31, 2025
8fa205b
Update existing cursor rules for Medusa Forms compatibility
codegen-sh[bot] May 31, 2025
2bc8ec3
Merge pull request #87 from lambda-curry/codegen-bot/medusa-cursor-ru…
jaruesink May 31, 2025
47379bc
refactor: Update form stories to enhance usability and consistency
jaruesink Jun 2, 2025
b5505b2
Merge pull request #81 from lambda-curry/medusa-forms-upgrades
jaruesink Jun 2, 2025
d003476
fix: Correct import path for Button component in ControlledSelect story
jaruesink Jun 2, 2025
706a6ef
refactor: Update controlled components to use consistent prop types
jaruesink Jun 2, 2025
b132ab4
chore: Update dependencies in yarn.lock and increment medusa-forms ve…
jaruesink Jun 2, 2025
848207a
chore: Remove outdated CHANGELOG.md from medusa-forms package
jaruesink Jun 2, 2025
0cb684f
chore: Update dependencies and refactor form components for improved …
jaruesink Jun 2, 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
7 changes: 4 additions & 3 deletions .cursor/rules/form-component-patterns.mdc
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
---
type: Always
description: Rules for form component integration patterns in the lambda-curry/forms repository
description: Rules for form component integration patterns in the lambda-curry/forms repository (applies to Remix Hook Form components)
---

You are an expert in React Hook Form, Remix Hook Form, Zod validation, and form component architecture for the lambda-curry/forms monorepo.

# Form Component Integration Patterns
# Form Component Integration Patterns (Remix Hook Form)

**Note: These patterns apply to Remix Hook Form components in `packages/components/`. For Medusa Forms components using react-hook-form + @medusajs/ui, see the medusa-forms-patterns.mdc rules.**

## Core Principles
- All form components must integrate seamlessly with Remix Hook Form
Expand Down Expand Up @@ -215,4 +217,3 @@ export type { ComponentNameProps };
- Test component composition and customization

Remember: Form components are the core of this library. Every form component should be intuitive, accessible, and integrate seamlessly with the Remix Hook Form + Zod validation pattern.

252 changes: 252 additions & 0 deletions .cursor/rules/medusa-forms-patterns.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
---
type: Auto Attached
description: Rules for Medusa Forms component development patterns using @medusajs/ui and react-hook-form
globs: ["packages/medusa-forms/**/*.{ts,tsx}", "apps/docs/src/medusa-forms/**/*.{ts,tsx}"]
---

You are an expert in React Hook Form, @medusajs/ui components, and Medusa design system integration for the lambda-curry/forms repository.

# Medusa Forms Component Patterns

## Core Architecture Principles
- Medusa Forms use **react-hook-form** directly (not remix-hook-form)
- All UI components are built on **@medusajs/ui** as the base design system
- Follow the **controlled/** and **ui/** directory separation pattern
- Use the **Controller** pattern for form integration
- Maintain **FieldWrapper** consistency for all form fields

## Required Imports for Medusa Forms

### For Controlled Components
```typescript
import {
Controller,
type ControllerProps,
type FieldValues,
type Path,
type RegisterOptions,
useFormContext,
} from 'react-hook-form';
import { ComponentName, type Props as ComponentNameProps } from '../ui/ComponentName';
```

### For UI Components
```typescript
import { ComponentName as MedusaComponentName } from '@medusajs/ui';
import type * as React from 'react';
import { FieldWrapper } from './FieldWrapper';
import type { BasicFieldProps, MedusaComponentNameProps } from './types';
```

## Directory Structure Convention
```
packages/medusa-forms/src/
├── controlled/ # Form-aware wrapper components using Controller
│ ├── ControlledInput.tsx
│ ├── ControlledCheckbox.tsx
│ ├── ControlledSelect.tsx
│ └── index.ts
└── ui/ # Base UI components using @medusajs/ui
├── Input.tsx
├── FieldCheckbox.tsx
├── Select.tsx
├── FieldWrapper.tsx
└── types.d.ts
```

## Controlled Component Pattern
All controlled components must follow this exact pattern:

```typescript
type Props<T extends FieldValues> = ComponentNameProps &
Omit<ControllerProps, 'render'> & {
name: Path<T>;
rules?: Omit<RegisterOptions<T, Path<T>>, 'disabled' | 'valueAsNumber' | 'valueAsDate' | 'setValueAs'>;
};

Comment on lines +61 to +66

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add missing generics to ControllerProps for full type-safety

ControllerProps is generic (ControllerProps<TFieldValues, TName>).
Omitting the generics forces any inside the library and silently drops a lot of compile-time guarantees that the rest of the pattern is trying to enforce.

-  Omit<ControllerProps, 'render'> & {
+  Omit<ControllerProps<T, Path<T>>, 'render'> & {

This keeps the strong correlation between the form’s FieldValues and the controlled field name.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
type Props<T extends FieldValues> = ComponentNameProps &
Omit<ControllerProps, 'render'> & {
name: Path<T>;
rules?: Omit<RegisterOptions<T, Path<T>>, 'disabled' | 'valueAsNumber' | 'valueAsDate' | 'setValueAs'>;
};
type Props<T extends FieldValues> = ComponentNameProps &
Omit<ControllerProps<T, Path<T>>, 'render'> & {
name: Path<T>;
rules?: Omit<
RegisterOptions<T, Path<T>>,
'disabled' | 'valueAsNumber' | 'valueAsDate' | 'setValueAs'
>;
};
🤖 Prompt for AI Agents
In .cursor/rules/medusa-forms-patterns.mdc around lines 61 to 66, the type Props
uses ControllerProps without specifying its generic parameters, which causes
loss of type safety. Fix this by adding the appropriate generics to
ControllerProps, matching the generic T and the Path<T> used elsewhere, to
maintain full type correlation and compile-time guarantees.

export const ControlledComponentName = <T extends FieldValues>({
name,
rules,
onChange,
...props
}: Props<T>) => {
const {
control,
formState: { errors },
} = useFormContext<T>();

return (
<Controller
control={control}
name={name}
rules={rules as Omit<RegisterOptions<T, Path<T>>, 'disabled' | 'valueAsNumber' | 'valueAsDate' | 'setValueAs'>}
render={({ field }) => (
<ComponentName
{...field}
{...props}
formErrors={errors}
onChange={(value) => {
if (onChange) onChange(value);
field.onChange(value);
}}
/>
)}
/>
);
};
```

## UI Component Pattern
All UI components must use FieldWrapper and @medusajs/ui:

```typescript
export type Props = MedusaComponentNameProps &
BasicFieldProps & {
ref?: React.Ref<HTMLInputElement>; // Adjust ref type based on component
};

const Wrapper = FieldWrapper<Props>;

export const ComponentName: React.FC<Props> = ({ ref, ...props }) => (
Comment on lines +103 to +110

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Prefer forwardRef over exposing ref in the props object

Because React reserves ref for the special forwarding mechanism, accepting it as a plain prop:

export type Props = MedusaComponentNameProps & BasicFieldProps & { ref?: React.Ref<HTMLInputElement> }

will not work if the consumer writes
<Input ref={someRef} /> – React strips the attribute before it reaches your component.

Refactor the UI-component pattern to use React.forwardRef:

- export const ComponentName: React.FC<Props> = ({ ref, ...props }) => (
+ export const ComponentName = React.forwardRef<HTMLInputElement, Props>(({ ...props }, ref) => (
     <Wrapper {...props}>
       {(inputProps) => <MedusaComponentName {...inputProps} ref={ref} />}
     </Wrapper>
- );
+ });

This keeps authoring ergonomics intact and aligns with React conventions.

🤖 Prompt for AI Agents
In .cursor/rules/medusa-forms-patterns.mdc around lines 103 to 110, the
component incorrectly accepts `ref` as a prop, which React ignores when using
JSX ref forwarding. Refactor the component to use `React.forwardRef` by removing
`ref` from the props type and wrapping the component definition with
`forwardRef`, passing the ref as the second argument and forwarding it to the
underlying input element. This aligns with React's ref forwarding conventions
and ensures the ref is properly received.

<Wrapper {...props}>
{(inputProps) => <MedusaComponentName {...inputProps} ref={ref} />}
</Wrapper>
);
```

## FieldWrapper Integration
- **Always** use FieldWrapper for consistent label, error, and styling patterns
- Pass `formErrors` prop to enable automatic error display
- Use `labelClassName`, `wrapperClassName`, `errorClassName` for styling customization

```typescript
<FieldWrapper<ComponentProps>
wrapperClassName={wrapperClassName}
errorClassName={errorClassName}
formErrors={formErrors}
{...props}
>
{(fieldProps) => (
<MedusaComponent {...fieldProps} ref={ref} />
)}
</FieldWrapper>
```

## @medusajs/ui Component Integration

### Input Components
```typescript
import { Input as MedusaInput } from '@medusajs/ui';
// Use with FieldWrapper pattern
```

### Checkbox Components
```typescript
import { Checkbox as MedusaCheckbox } from '@medusajs/ui';
// Special handling for checked state and onCheckedChange
```

### Select Components
```typescript
import { Select as MedusaSelect } from '@medusajs/ui';
// Compound component pattern with Trigger, Content, Item
```

### Currency Input Components
```typescript
import { CurrencyInput as MedusaCurrencyInput } from '@medusajs/ui';
// Special props: symbol, code, currency
```

### Date Picker Components
```typescript
import { DatePicker } from '@medusajs/ui';
// Special props: dateFormat, minDate, maxDate, filterDate
```

## Type Safety Requirements
- Use generic types `<T extends FieldValues>` for all controlled components
- Properly type `Path<T>` for name props
- Extend `BasicFieldProps` for all UI components
- Use proper ref types based on underlying HTML element

## Error Handling Pattern
```typescript
// In controlled components
const {
control,
formState: { errors },
} = useFormContext<T>();

// Pass errors to UI component
<ComponentName
{...field}
{...props}
formErrors={errors}
/>
```

## Validation Integration
- Use `rules` prop for react-hook-form validation
- Support both built-in and custom validation rules
- Ensure error messages are user-friendly and specific

```typescript
<ControlledInput
name="email"
label="Email Address"
rules={{
required: 'Email is required',
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: 'Invalid email address'
}
}}
/>
```

## Accessibility Requirements
- All form fields must have proper labels via FieldWrapper
- Use ARIA attributes provided by @medusajs/ui components
- Ensure keyboard navigation works correctly
- Provide clear error announcements for screen readers

## Component Naming Conventions
- Controlled components: `ControlledComponentName` (e.g., `ControlledInput`, `ControlledCheckbox`)
- UI components: `ComponentName` (e.g., `Input`, `FieldCheckbox`)
- Props interfaces: `ComponentNameProps`
- File names: PascalCase matching component name

## Export Requirements
Always export both the component and its props type:
```typescript
export { ControlledComponentName };
export type { Props as ControlledComponentNameProps };
```

## Performance Considerations
- Use React.memo for expensive form components when needed
- Avoid unnecessary re-renders by properly structuring form state
- Consider field-level subscriptions for large forms

## Testing Integration
- Components should work with existing Storybook patterns
- Test both valid and invalid form states
- Verify @medusajs/ui component integration
- Test component composition and customization

## Common Patterns to Avoid
- **Don't** use remix-hook-form patterns (use react-hook-form directly)
- **Don't** create custom UI components when @medusajs/ui equivalents exist
- **Don't** bypass FieldWrapper for form fields
- **Don't** mix controlled and uncontrolled patterns
- **Don't** forget to handle both onChange and field.onChange in controlled components

## Medusa Design System Compliance
- Follow Medusa UI spacing and sizing conventions
- Use Medusa color tokens and design patterns
- Ensure components work with Medusa themes
- Maintain consistency with Medusa component APIs

Remember: Medusa Forms are specifically designed to integrate with the Medusa ecosystem. Always prioritize @medusajs/ui component usage and follow Medusa design system principles while maintaining the react-hook-form integration patterns.

Loading