diff --git a/.cursor/rules/form-component-patterns.mdc b/.cursor/rules/form-component-patterns.mdc index 75c41cb5..5e61c546 100644 --- a/.cursor/rules/form-component-patterns.mdc +++ b/.cursor/rules/form-component-patterns.mdc @@ -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 @@ -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. - diff --git a/.cursor/rules/medusa-forms-patterns.mdc b/.cursor/rules/medusa-forms-patterns.mdc new file mode 100644 index 00000000..98549311 --- /dev/null +++ b/.cursor/rules/medusa-forms-patterns.mdc @@ -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 = ComponentNameProps & + Omit & { + name: Path; + rules?: Omit>, 'disabled' | 'valueAsNumber' | 'valueAsDate' | 'setValueAs'>; + }; + +export const ControlledComponentName = ({ + name, + rules, + onChange, + ...props +}: Props) => { + const { + control, + formState: { errors }, + } = useFormContext(); + + return ( + >, 'disabled' | 'valueAsNumber' | 'valueAsDate' | 'setValueAs'>} + render={({ field }) => ( + { + 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; // Adjust ref type based on component + }; + +const Wrapper = FieldWrapper; + +export const ComponentName: React.FC = ({ ref, ...props }) => ( + + {(inputProps) => } + +); +``` + +## 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 + + wrapperClassName={wrapperClassName} + errorClassName={errorClassName} + formErrors={formErrors} + {...props} +> + {(fieldProps) => ( + + )} + +``` + +## @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 `` for all controlled components +- Properly type `Path` 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(); + +// Pass errors to UI component + +``` + +## 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 + +``` + +## 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. + diff --git a/.cursor/rules/medusa-stories-patterns.mdc b/.cursor/rules/medusa-stories-patterns.mdc new file mode 100644 index 00000000..1ec58236 --- /dev/null +++ b/.cursor/rules/medusa-stories-patterns.mdc @@ -0,0 +1,349 @@ +--- +type: Auto Attached +description: Rules for creating Storybook stories for Medusa Forms components using react-hook-form patterns +globs: ["apps/docs/src/medusa-forms/**/*.stories.{ts,tsx}"] +--- + +You are an expert in Storybook, React Hook Form, and @medusajs/ui component documentation for the lambda-curry/forms repository. + +# Medusa Forms Storybook Patterns + +## Core Story Principles +- Use **react-hook-form** directly with **FormProvider** and **useForm** +- Create comprehensive examples showcasing **@medusajs/ui** integration +- Follow **Medusa design system** patterns and conventions +- Provide **realistic use cases** and **validation examples** +- Ensure stories work independently without external dependencies + +## Required Imports for Medusa Stories +```typescript +import { ControlledComponentName } from '@lambdacurry/medusa-forms/controlled/ControlledComponentName'; +import type { Meta, StoryObj } from '@storybook/react-vite'; +import React from 'react'; +import { FormProvider, useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; // When using validation +import { z } from 'zod'; // When using validation +``` + +## Story Meta Configuration +```typescript +const meta = { + title: 'Medusa Forms/Controlled ComponentName', + component: ControlledComponentName, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta; + +export default meta; +type Story = StoryObj; +``` + +## Basic Story Pattern +Every component should have a basic usage story: + +```typescript +const BasicComponentForm = () => { + const form = useForm({ + defaultValues: { + fieldName: '', // Provide appropriate default + }, + }); + + return ( + +
+ +
+ Current value: {JSON.stringify(form.watch('fieldName'))} +
+
+
+ ); +}; + +export const BasicUsage: Story = { + render: () => , +}; +``` + +## Validation Story Pattern +Include validation examples with clear error states: + +```typescript +const validationSchema = z.object({ + fieldName: z.string().min(1, 'Field is required'), +}); + +const ValidationForm = () => { + const form = useForm({ + resolver: zodResolver(validationSchema), + defaultValues: { + fieldName: '', + }, + mode: 'onChange', // Show validation on change + }); + + const onSubmit = (data: any) => { + console.log('Form submitted:', data); + }; + + return ( + +
+ +
+ Form valid: {form.formState.isValid ? 'Yes' : 'No'} +
+ + +
+ ); +}; + +export const RequiredValidation: Story = { + render: () => , +}; +``` + +## Component-Specific Story Categories + +### Input Components +Required stories: +- **BasicUsage**: Simple input with value display +- **RequiredValidation**: Required field validation +- **CustomValidation**: Pattern/custom validation +- **ErrorState**: Manually triggered error state +- **DisabledState**: Disabled input demonstration + +### Checkbox Components +Required stories: +- **BasicUsage**: Simple checkbox +- **DefaultChecked**: Pre-checked state +- **DefaultUnchecked**: Unchecked state +- **RequiredValidation**: Required checkbox validation +- **MultipleCheckboxes**: Group management with select all + +### Select Components +Required stories: +- **BasicSingleSelect**: Simple dropdown +- **WithDefaultValue**: Pre-selected option +- **RequiredValidation**: Required selection +- **MultipleSelect**: Multi-selection mode +- **SearchableSelect**: With search functionality + +### Currency Input Components +Required stories: +- **USDCurrency**: Dollar symbol and formatting +- **EURCurrency**: Euro symbol and formatting +- **MinimumValueValidation**: Min value constraints +- **MaximumValueValidation**: Max value constraints +- **RangeValidation**: Min/max range validation + +### Date Picker Components +Required stories: +- **BasicDateSelection**: Simple date picker +- **RequiredFieldValidation**: Required date validation +- **DateFormatVariations**: Different format displays +- **DisabledDates**: Past/future date restrictions +- **MinMaxDateConstraints**: Date range limitations + +## Form Integration Examples +Always include a complete form integration story: + +```typescript +const CompleteFormExample = () => { + const form = useForm({ + defaultValues: { + // Include multiple field types + name: '', + email: '', + acceptTerms: false, + category: '', + }, + mode: 'onChange', + }); + + const onSubmit = (data: any) => { + alert(`Form submitted: ${JSON.stringify(data, null, 2)}`); + }; + + return ( + +
+ {/* Multiple component types */} + + + + + +
+ + +
+ +
+ ); +}; + +export const CompleteFormExample: Story = { + render: () => , +}; +``` + +## Story Data Patterns + +### Sample Options for Select Components +```typescript +const countryOptions = [ + { label: 'United States', value: 'us' }, + { label: 'Canada', value: 'ca' }, + { label: 'United Kingdom', value: 'uk' }, + // ... more options +]; + +const categoryOptions = [ + { label: 'Electronics', value: 'electronics' }, + { label: 'Clothing', value: 'clothing' }, + // ... more options +]; +``` + +### Validation Schemas +```typescript +// Simple validation +const basicSchema = z.object({ + field: z.string().min(1, 'Field is required'), +}); + +// Complex validation +const complexSchema = z.object({ + email: z.string().email('Invalid email address'), + password: z.string().min(8, 'Password must be at least 8 characters'), + confirmPassword: z.string(), +}).refine((data) => data.password === data.confirmPassword, { + message: "Passwords don't match", + path: ["confirmPassword"], +}); +``` + +## Error State Demonstrations +Show how errors appear and behave: + +```typescript +const ErrorStateForm = () => { + const form = useForm({ + defaultValues: { field: '' }, + }); + + // Manually trigger error for demonstration + React.useEffect(() => { + form.setError('field', { + type: 'manual', + message: 'This is an example error message' + }); + }, [form]); + + return ( + +
+ +
+
+ ); +}; +``` + +## Interactive State Display +Always show current form state for debugging: + +```typescript +
+ Current value: {JSON.stringify(form.watch('fieldName'))} +
+ +
+ Form valid: {form.formState.isValid ? 'Yes' : 'No'} +
+ +
+ Has errors: {Object.keys(form.formState.errors).length > 0 ? 'Yes' : 'No'} +
+``` + +## Styling Conventions +- Use consistent container widths: `w-[400px]` +- Apply consistent spacing: `space-y-4` +- Use Tailwind classes for quick styling +- Maintain Medusa design system color palette +- Ensure responsive design considerations + +## Story Naming Conventions +- Use PascalCase for story exports +- Be descriptive and specific +- Group related functionality +- Follow the pattern: `ComponentAction` (e.g., `BasicUsage`, `RequiredValidation`) + +## Args and Controls +For components with configurable props: + +```typescript +export const Configurable: Story = { + args: { + label: 'Configurable Field', + placeholder: 'Enter value...', + disabled: false, + }, + render: (args) => ( + + ), +}; +``` + +## Documentation Integration +- Use `tags: ['autodocs']` for automatic documentation +- Include JSDoc comments for complex examples +- Provide clear descriptions of what each story demonstrates +- Link to related Medusa UI documentation when relevant + +## Performance Considerations +- Keep story components lightweight +- Avoid unnecessary re-renders in story components +- Use React.memo for expensive story components +- Consider story loading performance for complex examples + +## Common Anti-Patterns to Avoid +- **Don't** use remix-hook-form patterns in stories +- **Don't** create stories without FormProvider wrapper +- **Don't** forget to show current form state +- **Don't** create stories that depend on external APIs +- **Don't** mix different form libraries in the same story +- **Don't** create overly complex stories that obscure the component's purpose + +## Testing Integration +- Stories should work with Storybook interaction testing +- Include play functions for automated testing when appropriate +- Ensure stories cover edge cases and error states +- Verify accessibility in story examples + +Remember: Medusa Forms stories should showcase the seamless integration between react-hook-form and @medusajs/ui components while providing practical, real-world examples that developers can reference and adapt. + diff --git a/.cursor/rules/monorepo-organization.mdc b/.cursor/rules/monorepo-organization.mdc index 299846f5..c444d744 100644 --- a/.cursor/rules/monorepo-organization.mdc +++ b/.cursor/rules/monorepo-organization.mdc @@ -1,6 +1,6 @@ --- description: Monorepo structure and import conventions for the lambda-curry/forms repository -globs: +globs: ["packages/**/*.{ts,tsx}", "apps/**/*.{ts,tsx}", "*.{ts,tsx,json}", "turbo.json", "package.json"] alwaysApply: false --- @@ -21,14 +21,21 @@ lambda-curry/forms/ ├── apps/ │ └── docs/ # Storybook documentation app │ ├── src/ -│ │ ├── remix-hook-form/ # Story files +│ │ ├── remix-hook-form/ # Remix Hook Form story files +│ │ ├── medusa-forms/ # Medusa Forms story files │ │ └── lib/ # Storybook utilities │ └── package.json ├── packages/ -│ └── components/ # Main component library +│ ├── components/ # Main component library (Remix Hook Form) +│ │ ├── src/ +│ │ │ ├── remix-hook-form/ # Form-aware components +│ │ │ ├── ui/ # Base UI components +│ │ │ └── index.ts # Main export file +│ │ └── package.json +│ └── medusa-forms/ # Medusa Forms component library │ ├── src/ -│ │ ├── remix-hook-form/ # Form-aware components -│ │ ├── ui/ # Base UI components +│ │ ├── controlled/ # Controlled components using react-hook-form +│ │ ├── ui/ # Base UI components using @medusajs/ui │ │ └── index.ts # Main export file │ └── package.json ├── package.json # Root package.json @@ -43,9 +50,13 @@ lambda-curry/forms/ import { TextField } from '@lambdacurry/forms/remix-hook-form'; import { Button } from '@lambdacurry/forms/ui'; +// ✅ Import from Medusa Forms package +import { ControlledInput } from '@lambdacurry/medusa-forms/controlled'; +import { Input } from '@lambdacurry/medusa-forms/ui'; + // ✅ Import from specific entry points import { TextField } from '@lambdacurry/forms/remix-hook-form/text-field'; -import { Button } from '@lambdacurry/forms/ui/button'; +import { ControlledInput } from '@lambdacurry/medusa-forms/controlled/ControlledInput'; ``` ### Internal Package Imports @@ -361,4 +372,3 @@ docs/ - Maintain backward compatibility when possible Remember: A well-organized monorepo makes development faster and more predictable. Every import should be intentional and follow the established patterns to maintain consistency across the codebase. - diff --git a/.cursor/rules/storybook-testing.mdc b/.cursor/rules/storybook-testing.mdc index bd4b8039..ac109880 100644 --- a/.cursor/rules/storybook-testing.mdc +++ b/.cursor/rules/storybook-testing.mdc @@ -3,7 +3,7 @@ type: Always description: Rules for writing Storybook Playwright tests in the lambda-curry/forms repository --- -You are an expert in Storybook, Playwright testing, React, TypeScript, Remix Hook Form, Zod validation, and the lambda-curry/forms monorepo architecture. +You are an expert in Storybook, Playwright testing, React, TypeScript, Remix Hook Form, react-hook-form, @medusajs/ui, Zod validation, and the lambda-curry/forms monorepo architecture. # Project Context This is a monorepo containing form components with comprehensive Storybook Playwright testing. The testing setup combines Storybook's component isolation with Playwright's browser automation to create real-world testing scenarios. @@ -13,7 +13,8 @@ This is a monorepo containing form components with comprehensive Storybook Playw - @storybook/test-runner for Playwright automation - @storybook/test for testing utilities (userEvent, expect, canvas) - React Router stub decorator for form handling -- Remix Hook Form + Zod for validation testing +- Remix Hook Form + Zod for validation testing (main components) +- react-hook-form + @medusajs/ui for Medusa Forms components - Yarn 4.7.0 with corepack - TypeScript throughout @@ -22,13 +23,18 @@ This is a monorepo containing form components with comprehensive Storybook Playw lambda-curry/forms/ ├── apps/docs/ # Storybook app │ ├── .storybook/ # Storybook configuration -│ ├── src/remix-hook-form/ # Story files with tests +│ ├── src/remix-hook-form/ # Remix Hook Form story files with tests +│ ├── src/medusa-forms/ # Medusa Forms story files with tests │ ├── simple-server.js # Custom static server for testing │ └── package.json # Test scripts -├── packages/components/ # Component library +├── packages/components/ # Main component library (Remix Hook Form) │ └── src/ │ ├── remix-hook-form/ # Form components │ └── ui/ # UI components +├── packages/medusa-forms/ # Medusa Forms component library +│ └── src/ +│ ├── controlled/ # Controlled components using react-hook-form +│ └── ui/ # UI components using @medusajs/ui └── .cursor/rules/ # Cursor rules directory ```