Initial commit
This commit is contained in:
510
commands/component-generate.md
Normal file
510
commands/component-generate.md
Normal file
@@ -0,0 +1,510 @@
|
||||
# /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!
|
||||
Reference in New Issue
Block a user