feat(marketplace): command consolidation + 8 new plugins (v8.1.0 → v9.0.0) [BREAKING]
Phase 1b: Rename all ~94 commands across 12 plugins to /<noun> <action> sub-command pattern. Git-flow consolidated from 8→5 commands (commit variants absorbed into --push/--merge/--sync flags). Dispatch files, name: frontmatter, and cross-reference updates for all plugins. Phase 2: Design documents for 8 new plugins in docs/designs/. Phase 3: Scaffold 8 new plugins — saas-api-platform, saas-db-migrate, saas-react-platform, saas-test-pilot, data-seed, ops-release-manager, ops-deploy-pipeline, debug-mcp. Each with plugin.json, commands, agents, skills, README, and claude-md-integration. Marketplace grows from 12→20. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
134
plugins/saas-react-platform/skills/component-patterns.md
Normal file
134
plugins/saas-react-platform/skills/component-patterns.md
Normal file
@@ -0,0 +1,134 @@
|
||||
---
|
||||
name: component-patterns
|
||||
description: Component structure conventions including functional components, prop typing, exports, and co-located tests
|
||||
---
|
||||
|
||||
# Component Patterns
|
||||
|
||||
## Purpose
|
||||
|
||||
Define standard patterns for React component scaffolding. This skill ensures all generated components follow consistent structure, typing, export conventions, and test co-location.
|
||||
|
||||
---
|
||||
|
||||
## Component File Structure
|
||||
|
||||
Every component file follows this order:
|
||||
|
||||
```typescript
|
||||
// 1. Imports (external first, then internal, then styles)
|
||||
import { type FC } from 'react';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import styles from './ComponentName.module.css';
|
||||
|
||||
// 2. Types (inline for simple, separate file for complex)
|
||||
interface ComponentNameProps {
|
||||
title: string;
|
||||
onAction: () => void;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
// 3. Component definition
|
||||
/**
|
||||
* Brief description of what this component does.
|
||||
*
|
||||
* @component
|
||||
* @example
|
||||
* <ComponentName title="Hello" onAction={() => console.log('clicked')} />
|
||||
*/
|
||||
const ComponentName: FC<ComponentNameProps> = ({ title, onAction, children }) => {
|
||||
return (
|
||||
<div>
|
||||
<h2>{title}</h2>
|
||||
{children}
|
||||
<button onClick={onAction}>Action</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 4. Display name (for DevTools)
|
||||
ComponentName.displayName = 'ComponentName';
|
||||
|
||||
// 5. Export
|
||||
export default ComponentName;
|
||||
```
|
||||
|
||||
## Component Type Templates
|
||||
|
||||
### UI Component (presentational)
|
||||
- Props in, JSX out — no side effects, no data fetching
|
||||
- Pure function: same props always produce same output
|
||||
- Accept `className` prop for style override flexibility
|
||||
- Accept `children` if component is a container/wrapper
|
||||
|
||||
### Page Component
|
||||
- Includes data fetching (server component in App Router, `useEffect` in client)
|
||||
- Loading state with skeleton placeholder
|
||||
- Error state with retry action
|
||||
- `'use client'` directive only if client interactivity required (App Router)
|
||||
|
||||
### Layout Component
|
||||
- Accepts `children: React.ReactNode` as required prop
|
||||
- Optional slot props for sidebar, header, footer
|
||||
- Handles responsive behavior
|
||||
- Wraps with error boundary
|
||||
|
||||
### Form Component
|
||||
- Controlled inputs with `useState` or form library (`react-hook-form`)
|
||||
- Typed form values interface
|
||||
- Validation schema (Zod recommended)
|
||||
- Submit handler with loading state
|
||||
- Error display per field and form-level
|
||||
|
||||
## Test Co-location Patterns
|
||||
|
||||
Test file sits next to component file:
|
||||
|
||||
```
|
||||
src/components/Button/
|
||||
Button.tsx
|
||||
Button.test.tsx
|
||||
Button.module.css (if CSS Modules)
|
||||
index.ts (barrel file)
|
||||
```
|
||||
|
||||
### Minimum Test Coverage
|
||||
|
||||
```typescript
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import ComponentName from './ComponentName';
|
||||
|
||||
describe('ComponentName', () => {
|
||||
it('renders without crashing', () => {
|
||||
render(<ComponentName title="Test" onAction={() => {}} />);
|
||||
expect(screen.getByText('Test')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls onAction when button clicked', async () => {
|
||||
const onAction = vi.fn();
|
||||
render(<ComponentName title="Test" onAction={onAction} />);
|
||||
await userEvent.click(screen.getByRole('button'));
|
||||
expect(onAction).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Barrel File Convention
|
||||
|
||||
Each component directory exports through `index.ts`:
|
||||
|
||||
```typescript
|
||||
export { default as ComponentName } from './ComponentName';
|
||||
export type { ComponentNameProps } from './ComponentName';
|
||||
```
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
| Pattern | Why | Alternative |
|
||||
|---------|-----|-------------|
|
||||
| Class components | Legacy API, verbose | Functional components + hooks |
|
||||
| `React.FC` with children | Children always optional, incorrect type narrowing | Explicit `children` prop in interface |
|
||||
| Prop spreading `{...props}` | Obscures expected interface | Explicitly destructure needed props |
|
||||
| `useEffect` for derived state | Unnecessary render cycle | Compute during render or `useMemo` |
|
||||
| `forwardRef` without `displayName` | Unnamed in DevTools | Always set `displayName` |
|
||||
Reference in New Issue
Block a user