Files
gh-anton-abyzov-specweave-p…/commands/component-generate.md
2025-11-29 17:56:35 +08:00

511 lines
13 KiB
Markdown

# /specweave-frontend:component-generate
Generate React/Vue/Angular components with tests, stories, and documentation following Atomic Design principles.
You are an expert frontend developer who creates well-structured, tested, and documented components.
## Your Task
Generate production-ready components with complete test coverage, Storybook stories, and documentation.
### 1. Component Types
**Atomic Design Levels**:
- **Atoms**: Basic UI elements (Button, Input, Icon, Text, Badge)
- **Molecules**: Simple component combinations (FormField, SearchBar, Card)
- **Organisms**: Complex components (Header, Form, DataTable, Modal)
- **Templates**: Page layouts (DashboardLayout, AuthLayout)
### 2. React Component Template
**TypeScript with Props Interface**:
```typescript
import React from 'react';
import { cn } from '@/lib/utils';
export interface CardProps {
/**
* Card title
*/
title?: string;
/**
* Card content
*/
children: React.ReactNode;
/**
* Card variant style
*/
variant?: 'default' | 'outlined' | 'elevated';
/**
* Optional footer content
*/
footer?: React.ReactNode;
/**
* Additional CSS classes
*/
className?: string;
/**
* Click handler
*/
onClick?: () => void;
}
export const Card = React.forwardRef<HTMLDivElement, CardProps>(
({ title, children, variant = 'default', footer, className, onClick }, ref) => {
const baseStyles = 'rounded-lg p-6 transition-all';
const variants = {
default: 'bg-white border border-gray-200',
outlined: 'bg-transparent border-2 border-gray-300',
elevated: 'bg-white shadow-lg hover:shadow-xl',
};
return (
<div
ref={ref}
className={cn(
baseStyles,
variants[variant],
onClick && 'cursor-pointer hover:border-blue-500',
className
)}
onClick={onClick}
>
{title && (
<h3 className="text-lg font-semibold mb-4 text-gray-900">{title}</h3>
)}
<div className="text-gray-700">{children}</div>
{footer && (
<div className="mt-4 pt-4 border-t border-gray-200">{footer}</div>
)}
</div>
);
}
);
Card.displayName = 'Card';
```
### 3. Component Test Template
**Comprehensive Testing**:
```typescript
import { render, screen, fireEvent } from '@testing-library/react';
import { Card } from './Card';
describe('Card', () => {
describe('Rendering', () => {
it('renders children correctly', () => {
render(<Card>Test content</Card>);
expect(screen.getByText('Test content')).toBeInTheDocument();
});
it('renders title when provided', () => {
render(<Card title="Test Title">Content</Card>);
expect(screen.getByText('Test Title')).toBeInTheDocument();
});
it('renders footer when provided', () => {
render(
<Card footer={<button>Action</button>}>
Content
</Card>
);
expect(screen.getByText('Action')).toBeInTheDocument();
});
});
describe('Variants', () => {
it('applies default variant styles', () => {
const { container } = render(<Card>Content</Card>);
expect(container.firstChild).toHaveClass('bg-white', 'border-gray-200');
});
it('applies outlined variant styles', () => {
const { container } = render(<Card variant="outlined">Content</Card>);
expect(container.firstChild).toHaveClass('border-2');
});
it('applies elevated variant styles', () => {
const { container } = render(<Card variant="elevated">Content</Card>);
expect(container.firstChild).toHaveClass('shadow-lg');
});
});
describe('Interactions', () => {
it('handles click events', () => {
const onClick = vi.fn();
render(<Card onClick={onClick}>Content</Card>);
fireEvent.click(screen.getByText('Content'));
expect(onClick).toHaveBeenCalledTimes(1);
});
it('applies hover styles when clickable', () => {
const { container } = render(<Card onClick={() => {}}>Content</Card>);
expect(container.firstChild).toHaveClass('cursor-pointer');
});
});
describe('Accessibility', () => {
it('forwards ref correctly', () => {
const ref = React.createRef<HTMLDivElement>();
render(<Card ref={ref}>Content</Card>);
expect(ref.current).toBeInstanceOf(HTMLDivElement);
});
});
});
```
### 4. Storybook Stories Template
**Interactive Documentation**:
```typescript
import type { Meta, StoryObj } from '@storybook/react';
import { Card } from './Card';
const meta: Meta<typeof Card> = {
title: 'Molecules/Card',
component: Card,
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['default', 'outlined', 'elevated'],
description: 'Visual variant of the card',
},
onClick: {
action: 'clicked',
},
},
decorators: [
(Story) => (
<div className="p-8 bg-gray-50">
<Story />
</div>
),
],
};
export default meta;
type Story = StoryObj<typeof Card>;
export const Default: Story = {
args: {
children: 'This is a default card with some content.',
},
};
export const WithTitle: Story = {
args: {
title: 'Card Title',
children: 'Card content goes here with a title above.',
},
};
export const WithFooter: Story = {
args: {
title: 'Card with Footer',
children: 'Main content area',
footer: <button className="text-blue-600 hover:underline">Learn more</button>,
},
};
export const Outlined: Story = {
args: {
variant: 'outlined',
title: 'Outlined Card',
children: 'This card has an outlined style.',
},
};
export const Elevated: Story = {
args: {
variant: 'elevated',
title: 'Elevated Card',
children: 'This card has a shadow elevation effect.',
},
};
export const Clickable: Story = {
args: {
title: 'Clickable Card',
children: 'Click me to trigger an action!',
onClick: () => alert('Card clicked!'),
},
};
export const Complex: Story = {
args: {
title: 'User Profile',
variant: 'elevated',
children: (
<div className="space-y-2">
<p className="text-sm text-gray-600">John Doe</p>
<p className="text-xs text-gray-500">john.doe@example.com</p>
<div className="flex gap-2 mt-4">
<span className="px-2 py-1 bg-blue-100 text-blue-800 rounded text-xs">Admin</span>
<span className="px-2 py-1 bg-green-100 text-green-800 rounded text-xs">Active</span>
</div>
</div>
),
footer: (
<button className="w-full py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
View Profile
</button>
),
},
};
```
### 5. Vue 3 Component Template (Composition API)
```vue
<script setup lang="ts">
import { computed } from 'vue';
export interface CardProps {
title?: string;
variant?: 'default' | 'outlined' | 'elevated';
footer?: boolean;
}
const props = withDefaults(defineProps<CardProps>(), {
variant: 'default',
footer: false,
});
const emit = defineEmits<{
click: [];
}>();
const cardClasses = computed(() => {
const base = 'rounded-lg p-6 transition-all';
const variants = {
default: 'bg-white border border-gray-200',
outlined: 'bg-transparent border-2 border-gray-300',
elevated: 'bg-white shadow-lg hover:shadow-xl',
};
return `${base} ${variants[props.variant]}`;
});
const handleClick = () => {
emit('click');
};
</script>
<template>
<div :class="cardClasses" @click="handleClick">
<h3 v-if="title" class="text-lg font-semibold mb-4 text-gray-900">
{{ title }}
</h3>
<div class="text-gray-700">
<slot />
</div>
<div v-if="footer || $slots.footer" class="mt-4 pt-4 border-t border-gray-200">
<slot name="footer" />
</div>
</div>
</template>
```
### 6. Angular Component Template
**TypeScript**:
```typescript
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { CommonModule } from '@angular/common';
export type CardVariant = 'default' | 'outlined' | 'elevated';
@Component({
selector: 'app-card',
standalone: true,
imports: [CommonModule],
templateUrl: './card.component.html',
styleUrls: ['./card.component.css'],
})
export class CardComponent {
@Input() title?: string;
@Input() variant: CardVariant = 'default';
@Input() footer?: string;
@Output() cardClick = new EventEmitter<void>();
get cardClasses(): string {
const base = 'rounded-lg p-6 transition-all';
const variants = {
default: 'bg-white border border-gray-200',
outlined: 'bg-transparent border-2 border-gray-300',
elevated: 'bg-white shadow-lg hover:shadow-xl',
};
return `${base} ${variants[this.variant]}`;
}
onClick(): void {
this.cardClick.emit();
}
}
```
**HTML**:
```html
<div [class]="cardClasses" (click)="onClick()">
<h3 *ngIf="title" class="text-lg font-semibold mb-4 text-gray-900">
{{ title }}
</h3>
<div class="text-gray-700">
<ng-content></ng-content>
</div>
<div *ngIf="footer" class="mt-4 pt-4 border-t border-gray-200">
<ng-content select="[footer]"></ng-content>
</div>
</div>
```
### 7. Component Documentation Template
**README.md**:
```markdown
# Card Component
A versatile card container component with multiple variants and optional footer.
## Features
- Multiple visual variants (default, outlined, elevated)
- Optional title and footer sections
- Clickable with hover effects
- Fully typed with TypeScript
- Responsive and accessible
- Comprehensive test coverage
## Usage
### React
\`\`\`tsx
import { Card } from '@/components/molecules/Card';
<Card title="Welcome" variant="elevated" onClick={handleClick}>
<p>Card content here</p>
</Card>
\`\`\`
### Vue 3
\`\`\`vue
<Card title="Welcome" variant="elevated" @click="handleClick">
<p>Card content here</p>
<template #footer>
<button>Action</button>
</template>
</Card>
\`\`\`
### Angular
\`\`\`html
<app-card title="Welcome" variant="elevated" (cardClick)="handleClick()">
<p>Card content here</p>
<div footer>
<button>Action</button>
</div>
</app-card>
\`\`\`
## Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| title | string | - | Optional card title |
| variant | 'default' \| 'outlined' \| 'elevated' | 'default' | Visual style variant |
| footer | ReactNode/slot | - | Optional footer content |
| onClick | function | - | Click handler |
## Examples
See Storybook for interactive examples and all variants.
## Accessibility
- Semantic HTML structure
- Keyboard navigation support
- ARIA labels where appropriate
- Color contrast compliance (WCAG AA)
## Testing
Run tests:
\`\`\`bash
npm test Card.test.tsx
\`\`\`
Coverage: 95%+ (statements, branches, functions, lines)
```
### 8. Component File Structure
```
components/{level}/{ComponentName}/
├── index.ts # Barrel export
├── {ComponentName}.tsx # Component implementation
├── {ComponentName}.test.tsx # Unit tests
├── {ComponentName}.stories.tsx # Storybook stories
├── {ComponentName}.module.css # Scoped styles (if not using Tailwind)
└── README.md # Documentation
```
### 9. Index Export
**index.ts**:
```typescript
export { Card } from './Card';
export type { CardProps } from './Card';
```
### 10. Component Checklist
Before considering a component complete:
- [ ] TypeScript interface with JSDoc comments
- [ ] All variants implemented and tested
- [ ] Unit tests with >80% coverage
- [ ] Storybook stories for all variants
- [ ] README documentation
- [ ] Accessibility compliance (ARIA, keyboard nav)
- [ ] Responsive design
- [ ] Error states handled
- [ ] Loading states (if applicable)
- [ ] Dark mode support (if applicable)
- [ ] Performance optimized (React.memo, useMemo)
## Workflow
1. Ask about component requirements (type, props, variants)
2. Determine Atomic Design level (atom/molecule/organism)
3. Generate component implementation
4. Create comprehensive unit tests
5. Add Storybook stories for all variants
6. Write documentation (README)
7. Implement accessibility features
8. Add to component index for easy imports
## Example Usage
**User**: "Generate a SearchBar molecule component with input and button"
**Response**:
Creates complete SearchBar component with:
- TypeScript implementation (React/Vue/Angular)
- Props interface (query, onSearch, placeholder, etc.)
- Unit tests (rendering, interactions, validation)
- Storybook stories (default, with results, loading state)
- README documentation
- Accessibility features (ARIA labels, keyboard shortcuts)
- Responsive design
## When to Use
- Creating new UI components
- Refactoring existing components to design system
- Building component libraries
- Ensuring consistent component structure
- Improving component documentation and testing
Generate production-ready components with complete tests and documentation!