Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 17:51:59 +08:00
commit 38e80921c8
89 changed files with 20480 additions and 0 deletions

View File

@@ -0,0 +1,492 @@
---
name: frontend-component
description: Create React/Vue component with TypeScript, tests, and styles. Auto-invoke when user says "create component", "add component", "new component", or "build component".
allowed-tools: Read, Write, Edit, Grep, Glob, Bash
version: 1.0.0
---
# Frontend Component Generator
Generate production-ready React/Vue components with TypeScript, tests, and styles following modern best practices.
## When to Invoke
Auto-invoke when user mentions:
- "Create a component"
- "Add a component"
- "New component"
- "Build a component"
- "Generate component for [feature]"
## What This Does
1. Generates component file with TypeScript and props interface
2. Creates test file with React Testing Library
3. Generates CSS module for styling
4. Creates barrel export (index.ts)
5. Validates naming conventions
6. Follows project patterns
## Execution Steps
### Step 1: Gather Component Requirements
**Ask user for component details**:
```
Component name: [PascalCase name, e.g., UserProfile]
Component type:
- simple (basic functional component)
- with-hooks (useState, useEffect, etc.)
- container (data fetching component)
Styling approach:
- css-modules (default)
- styled-components
- tailwind
Props needed: [Optional: describe expected props]
```
**Validate component name**:
- Use predefined function: `functions/name_validator.py`
- Ensure PascalCase format
- No reserved words
- Descriptive and specific
### Step 2: Generate Props Interface
**Based on component type and requirements**:
Use predefined function: `functions/props_interface_generator.py`
```python
# Generates TypeScript interface based on component requirements
python3 functions/props_interface_generator.py \
--name "UserProfile" \
--props "userId:string,onUpdate:function,isActive:boolean"
```
**Output**:
```typescript
interface UserProfileProps {
userId: string;
onUpdate?: () => void;
isActive?: boolean;
children?: React.ReactNode;
className?: string;
}
```
### Step 3: Generate Component File
**Use appropriate template based on type**:
**Simple component**:
```
Use template: templates/component-simple-template.tsx
```
**Component with hooks**:
```
Use template: templates/component-with-hooks-template.tsx
```
**Container component**:
```
Use template: templates/component-container-template.tsx
```
**Use predefined function**: `functions/component_generator.py`
```bash
python3 functions/component_generator.py \
--name "UserProfile" \
--type "simple" \
--props-interface "UserProfileProps" \
--template "templates/component-simple-template.tsx" \
--output "src/components/UserProfile/UserProfile.tsx"
```
**Template substitutions**:
- `${COMPONENT_NAME}` → Component name (PascalCase)
- `${PROPS_INTERFACE}` → Generated props interface
- `${STYLE_IMPORT}` → CSS module import
- `${DESCRIPTION}` → Brief component description
### Step 4: Generate Test File
**Use predefined function**: `functions/test_generator.py`
```bash
python3 functions/test_generator.py \
--component-name "UserProfile" \
--component-path "src/components/UserProfile/UserProfile.tsx" \
--template "templates/test-template.test.tsx" \
--output "src/components/UserProfile/UserProfile.test.tsx"
```
**Test template includes**:
- Basic rendering test
- Props validation test
- Event handler tests (if applicable)
- Accessibility tests
**Template substitutions**:
- `${COMPONENT_NAME}` → Component name
- `${IMPORT_PATH}` → Relative import path
- `${TEST_CASES}` → Generated test cases based on props
### Step 5: Generate Style File
**Use predefined function**: `functions/style_generator.py`
```bash
python3 functions/style_generator.py \
--name "UserProfile" \
--approach "css-modules" \
--template "templates/style-template.module.css" \
--output "src/components/UserProfile/UserProfile.module.css"
```
**CSS Modules template**:
```css
.container {
/* Component wrapper styles */
}
.title {
/* Title styles */
}
/* Add more classes as needed */
```
**Styled Components alternative**:
```typescript
// Generated if --approach "styled-components"
import styled from 'styled-components';
export const Container = styled.div`
/* Component wrapper styles */
`;
export const Title = styled.h2`
/* Title styles */
`;
```
### Step 6: Generate Barrel Export
**Create index.ts for clean imports**:
```bash
Write(
file_path: "src/components/UserProfile/index.ts",
content: "export { UserProfile } from './UserProfile';\nexport type { UserProfileProps } from './UserProfile';\n"
)
```
**Allows usage**:
```typescript
import { UserProfile } from '@/components/UserProfile';
```
### Step 7: Show Component Summary
**Display generated files and usage**:
```
✅ Component Created: UserProfile
Structure:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📁 src/components/UserProfile/
├── UserProfile.tsx (Component)
├── UserProfile.test.tsx (Tests)
├── UserProfile.module.css (Styles)
└── index.ts (Exports)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Props Interface:
interface UserProfileProps {
userId: string;
onUpdate?: () => void;
isActive?: boolean;
}
Usage:
import { UserProfile } from '@/components/UserProfile';
<UserProfile
userId="123"
onUpdate={() => console.log('Updated')}
isActive={true}
/>
Next Steps:
1. Customize component implementation
2. Run tests: npm test UserProfile
3. Import and use in your feature
```
---
## Predefined Functions
### 1. name_validator.py
Validates component naming conventions.
**Usage**:
```bash
python3 functions/name_validator.py --name "UserProfile"
```
**Checks**:
- PascalCase format
- Not a reserved word (e.g., Component, Element, etc.)
- Descriptive (length > 2 chars)
- No special characters
**Returns**: Valid name or error message
---
### 2. props_interface_generator.py
Generates TypeScript props interface from user input.
**Usage**:
```bash
python3 functions/props_interface_generator.py \
--name "UserProfile" \
--props "userId:string,onUpdate:function,isActive:boolean"
```
**Supported types**:
- `string`, `number`, `boolean`
- `function` (becomes `() => void`)
- `array` (becomes `any[]`)
- `object` (becomes `Record<string, any>`)
- `react-node` (becomes `React.ReactNode`)
**Returns**: TypeScript interface string
---
### 3. component_generator.py
Generates component file from template with substitutions.
**Usage**:
```bash
python3 functions/component_generator.py \
--name "UserProfile" \
--type "simple" \
--props-interface "UserProfileProps" \
--template "templates/component-simple-template.tsx" \
--output "src/components/UserProfile/UserProfile.tsx"
```
**Parameters**:
- `--name`: Component name (PascalCase)
- `--type`: Component type (simple/with-hooks/container)
- `--props-interface`: Props interface name
- `--template`: Template file path
- `--output`: Output file path
**Returns**: Generated component code
---
### 4. test_generator.py
Generates test file with React Testing Library.
**Usage**:
```bash
python3 functions/test_generator.py \
--component-name "UserProfile" \
--component-path "src/components/UserProfile/UserProfile.tsx" \
--template "templates/test-template.test.tsx" \
--output "src/components/UserProfile/UserProfile.test.tsx"
```
**Generates tests for**:
- Component rendering
- Props validation
- Event handlers
- Accessibility attributes
**Returns**: Generated test code
---
### 5. style_generator.py
Generates style file (CSS Modules or Styled Components).
**Usage**:
```bash
python3 functions/style_generator.py \
--name "UserProfile" \
--approach "css-modules" \
--template "templates/style-template.module.css" \
--output "src/components/UserProfile/UserProfile.module.css"
```
**Supported approaches**:
- `css-modules` (default)
- `styled-components`
- `tailwind` (generates className utilities)
**Returns**: Generated style code
---
## Templates
### component-simple-template.tsx
Basic functional component template.
**Placeholders**:
- `${COMPONENT_NAME}` - Component name
- `${PROPS_INTERFACE}` - Props interface definition
- `${STYLE_IMPORT}` - CSS import statement
- `${DESCRIPTION}` - Component description
### component-with-hooks-template.tsx
Component template with useState, useEffect examples.
**Additional placeholders**:
- `${HOOKS}` - Hook declarations
- `${HANDLERS}` - Event handler functions
### component-container-template.tsx
Container component template with data fetching.
**Additional placeholders**:
- `${API_IMPORT}` - API function import
- `${DATA_TYPE}` - Data type definition
- `${FETCH_LOGIC}` - Data fetching implementation
### test-template.test.tsx
React Testing Library test template.
**Placeholders**:
- `${COMPONENT_NAME}` - Component name
- `${IMPORT_PATH}` - Import path
- `${TEST_CASES}` - Generated test cases
### style-template.module.css
CSS Modules template.
**Placeholders**:
- `${COMPONENT_NAME_KEBAB}` - Component name in kebab-case
- `${BASE_STYLES}` - Base container styles
---
## Examples
See `examples/` directory for reference implementations:
1. **Button.tsx** - Simple component with variants
2. **SearchBar.tsx** - Component with hooks (useState, useEffect)
3. **UserProfile.tsx** - Container component with data fetching
Each example includes:
- Component implementation
- Test file
- Style file
- Usage documentation
---
## Best Practices
### Component Design
- Keep components **small and focused** (single responsibility)
- **Compose** complex UIs from simple components
- **Lift state up** only when necessary
- Use **descriptive names** (UserProfile, not UP)
### TypeScript
- **Define prop interfaces** explicitly
- **Avoid `any`** type (use `unknown` if needed)
- **Export types** for consumers
- **Use strict mode**
### Testing
- **Test user behavior**, not implementation
- **Query by role/text**, not test IDs
- **Test accessible attributes**
- **Mock external dependencies**
### Styling
- **CSS Modules** for scoped styles
- **BEM or descriptive class names**
- **Mobile-first** responsive design
- **Use CSS custom properties** for theming
### Accessibility
- **Semantic HTML** (button, nav, main, etc.)
- **ARIA labels** when needed
- **Keyboard navigation** support
- **Focus management** in modals/dropdowns
---
## Troubleshooting
### Component Not Rendering
**Problem**: Generated component throws errors
**Solutions**:
1. Check TypeScript compilation errors
2. Verify all imports are correct
3. Check props interface matches usage
4. Validate JSX syntax
### Tests Failing
**Problem**: Generated tests don't pass
**Solutions**:
1. Ensure React Testing Library is installed
2. Check test queries match component output
3. Verify mocks are set up correctly
4. Run tests with `--verbose` flag
### Styles Not Applying
**Problem**: CSS modules not loading
**Solutions**:
1. Check CSS module import syntax
2. Verify webpack/vite config supports CSS modules
3. Check className is applied to element
4. Inspect browser devtools for loaded styles
---
## Success Criteria
**This skill succeeds when**:
- [ ] Component file generated with valid TypeScript
- [ ] Test file created with passing tests
- [ ] Style file generated with scoped styles
- [ ] Barrel export allows clean imports
- [ ] Props interface matches requirements
- [ ] Code follows React best practices
- [ ] Accessibility attributes included
---
**Auto-invoke this skill when creating React components to ensure consistency and save time** ⚛️

View File

@@ -0,0 +1,40 @@
/**
* Button - Simple button component with variants
*
* @example
* <Button variant="primary" onClick={() => console.log('clicked')}>
* Click me
* </Button>
*/
import React from 'react';
import styles from './Button.module.css';
interface ButtonProps {
children: React.ReactNode;
onClick?: () => void;
variant?: 'primary' | 'secondary' | 'danger';
disabled?: boolean;
type?: 'button' | 'submit' | 'reset';
className?: string;
}
export const Button: React.FC<ButtonProps> = ({
children,
onClick,
variant = 'primary',
disabled = false,
type = 'button',
className,
}) => {
return (
<button
type={type}
className={`${styles.button} ${styles[variant]} ${className || ''}`}
onClick={onClick}
disabled={disabled}
>
{children}
</button>
);
};

View File

@@ -0,0 +1,52 @@
/**
* SearchBar - Search input with debounced onChange
*
* @example
* <SearchBar
* onSearch={(query) => console.log('Search:', query)}
* placeholder="Search users..."
* />
*/
import React, { useState, useEffect, useCallback } from 'react';
import styles from './SearchBar.module.css';
interface SearchBarProps {
onSearch: (query: string) => void;
placeholder?: string;
debounceMs?: number;
className?: string;
}
export const SearchBar: React.FC<SearchBarProps> = ({
onSearch,
placeholder = 'Search...',
debounceMs = 300,
className,
}) => {
const [query, setQuery] = useState('');
const handleSearch = useCallback(() => {
if (query.trim()) {
onSearch(query);
}
}, [query, onSearch]);
useEffect(() => {
const timer = setTimeout(handleSearch, debounceMs);
return () => clearTimeout(timer);
}, [query, debounceMs, handleSearch]);
return (
<div className={`${styles.container} ${className || ''}`}>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder={placeholder}
className={styles.input}
aria-label="Search"
/>
</div>
);
};

View File

@@ -0,0 +1,89 @@
#!/usr/bin/env python3
"""
Generate React component file from template with substitutions.
Replaces placeholders in template with component-specific values.
"""
import sys
import argparse
import os
def read_template(template_path: str) -> str:
"""Read template file content."""
try:
with open(template_path, 'r') as f:
return f.read()
except FileNotFoundError:
raise FileNotFoundError(f"Template file not found: {template_path}")
def generate_component(name: str, props_interface: str, template_content: str, description: str = None) -> str:
"""
Generate component code by substituting placeholders in template.
Args:
name: Component name (PascalCase)
props_interface: Props interface name
template_content: Template file content
description: Brief component description
Returns:
str: Generated component code
"""
# Convert PascalCase to kebab-case for file names
kebab_name = ''.join(['-' + c.lower() if c.isupper() else c for c in name]).lstrip('-')
# Perform substitutions
substitutions = {
'${COMPONENT_NAME}': name,
'${PROPS_INTERFACE}': props_interface,
'${STYLE_IMPORT}': f"import styles from './{name}.module.css';",
'${DESCRIPTION}': description or f"{name} component",
'${COMPONENT_NAME_KEBAB}': kebab_name,
}
result = template_content
for placeholder, value in substitutions.items():
result = result.replace(placeholder, value)
return result
def main():
parser = argparse.ArgumentParser(description='Generate React component from template')
parser.add_argument('--name', required=True, help='Component name (PascalCase)')
parser.add_argument('--type', default='simple', choices=['simple', 'with-hooks', 'container'], help='Component type')
parser.add_argument('--props-interface', required=True, help='Props interface name')
parser.add_argument('--template', required=True, help='Template file path')
parser.add_argument('--output', help='Output file path (optional, prints to stdout if not provided)')
parser.add_argument('--description', help='Component description')
args = parser.parse_args()
try:
# Read template
template_content = read_template(args.template)
# Generate component
component_code = generate_component(
args.name,
args.props_interface,
template_content,
args.description
)
# Output
if args.output:
os.makedirs(os.path.dirname(args.output), exist_ok=True)
with open(args.output, 'w') as f:
f.write(component_code)
print(f"✅ Component generated: {args.output}")
else:
print(component_code)
sys.exit(0)
except Exception as e:
print(f"❌ Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,115 @@
#!/usr/bin/env python3
"""
Validate component naming conventions.
Ensures component names follow PascalCase, are descriptive, and avoid reserved words.
"""
import sys
import re
import argparse
# Reserved component names that should be avoided
RESERVED_WORDS = {
'Component', 'Element', 'Node', 'React', 'ReactNode', 'Fragment',
'Props', 'State', 'Context', 'Provider', 'Consumer', 'Children',
'Ref', 'Key', 'Type', 'Class', 'Function', 'Object', 'Array',
'String', 'Number', 'Boolean', 'Symbol', 'Null', 'Undefined'
}
def is_pascal_case(name):
"""
Check if name is in PascalCase format.
Args:
name: String to validate
Returns:
bool: True if PascalCase, False otherwise
"""
# PascalCase: starts with uppercase, contains only alphanumeric
pattern = r'^[A-Z][a-zA-Z0-9]*$'
return bool(re.match(pattern, name))
def validate_component_name(name):
"""
Validate component name against conventions.
Args:
name: Component name to validate
Returns:
tuple: (is_valid: bool, error_message: str or None)
"""
# Check length
if len(name) < 2:
return False, "Component name must be at least 2 characters long"
# Check for special characters
if not name.replace('_', '').isalnum():
return False, "Component name should only contain alphanumeric characters"
# Check PascalCase
if not is_pascal_case(name):
return False, f"Component name '{name}' must be in PascalCase (e.g., UserProfile, TodoList)"
# Check reserved words
if name in RESERVED_WORDS:
return False, f"'{name}' is a reserved word. Choose a more descriptive name."
# Check descriptiveness (not too generic)
if len(name) < 4:
return False, f"Component name '{name}' is too short. Use a more descriptive name (e.g., UserCard, not UC)"
# Check doesn't start with common anti-patterns
anti_patterns = ['My', 'The', 'New', 'Test']
if any(name.startswith(pattern) for pattern in anti_patterns):
return False, f"Avoid starting component names with '{name[:3]}...'. Be more specific about what it does."
return True, None
def suggest_valid_name(name):
"""
Suggest a valid component name if the provided one is invalid.
Args:
name: Invalid component name
Returns:
str: Suggested valid name
"""
# Convert to PascalCase
suggested = ''.join(word.capitalize() for word in re.split(r'[-_\s]+', name))
# Remove special characters
suggested = re.sub(r'[^a-zA-Z0-9]', '', suggested)
# Ensure starts with uppercase
if suggested and not suggested[0].isupper():
suggested = suggested.capitalize()
return suggested if suggested else "MyComponent"
def main():
parser = argparse.ArgumentParser(description='Validate React component naming conventions')
parser.add_argument('--name', required=True, help='Component name to validate')
parser.add_argument('--suggest', action='store_true', help='Suggest a valid name if invalid')
args = parser.parse_args()
is_valid, error = validate_component_name(args.name)
if is_valid:
print(f"'{args.name}' is a valid component name")
sys.exit(0)
else:
print(f"❌ Invalid component name: {error}", file=sys.stderr)
if args.suggest:
suggested = suggest_valid_name(args.name)
print(f"💡 Suggested name: {suggested}", file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,125 @@
#!/usr/bin/env python3
"""
Generate TypeScript props interface from user input.
Converts simple prop specifications into proper TypeScript interface definitions.
"""
import sys
import argparse
from typing import List, Tuple
# Type mapping from simple names to TypeScript types
TYPE_MAPPING = {
'string': 'string',
'str': 'string',
'number': 'number',
'num': 'number',
'int': 'number',
'boolean': 'boolean',
'bool': 'boolean',
'function': '() => void',
'func': '() => void',
'callback': '() => void',
'array': 'any[]',
'arr': 'any[]',
'object': 'Record<string, any>',
'obj': 'Record<string, any>',
'react-node': 'React.ReactNode',
'node': 'React.ReactNode',
'children': 'React.ReactNode',
'element': 'React.ReactElement',
'style': 'React.CSSProperties',
'class': 'string',
'classname': 'string',
}
def parse_prop_spec(prop_spec: str) -> Tuple[str, str, bool]:
"""
Parse a single prop specification.
Format: "propName:type" or "propName:type:optional"
Args:
prop_spec: Prop specification string
Returns:
tuple: (prop_name, ts_type, is_optional)
"""
parts = prop_spec.strip().split(':')
if len(parts) < 2:
raise ValueError(f"Invalid prop specification: '{prop_spec}'. Expected format: 'propName:type' or 'propName:type:optional'")
prop_name = parts[0].strip()
type_name = parts[1].strip().lower()
is_optional = len(parts) > 2 and parts[2].strip().lower() in ('optional', 'opt', '?', 'true')
# Map to TypeScript type
ts_type = TYPE_MAPPING.get(type_name, type_name)
return prop_name, ts_type, is_optional
def generate_props_interface(name: str, props: List[str], include_common: bool = True) -> str:
"""
Generate TypeScript props interface.
Args:
name: Component name (will become {name}Props)
props: List of prop specifications
include_common: Whether to include common props (children, className, etc.)
Returns:
str: TypeScript interface definition
"""
interface_name = f"{name}Props"
lines = [f"interface {interface_name} {{"]
# Add custom props
for prop_spec in props:
if not prop_spec.strip():
continue
prop_name, ts_type, is_optional = parse_prop_spec(prop_spec)
optional_marker = '?' if is_optional else ''
lines.append(f" {prop_name}{optional_marker}: {ts_type};")
# Add common props if requested
if include_common:
# Only add children if not already specified
if not any('children' in prop for prop in props):
lines.append(" children?: React.ReactNode;")
# Only add className if not already specified
if not any('className' in prop or 'class' in prop.lower() for prop in props):
lines.append(" className?: string;")
lines.append("}")
return '\n'.join(lines)
def main():
parser = argparse.ArgumentParser(description='Generate TypeScript props interface')
parser.add_argument('--name', required=True, help='Component name')
parser.add_argument('--props', required=True, help='Comma-separated prop specifications (e.g., "userId:string,onUpdate:function,isActive:boolean:optional")')
parser.add_argument('--no-common', action='store_true', help='Do not include common props (children, className)')
args = parser.parse_args()
# Parse prop specifications
prop_specs = [p.strip() for p in args.props.split(',') if p.strip()]
try:
interface = generate_props_interface(
args.name,
prop_specs,
include_common=not args.no_common
)
print(interface)
sys.exit(0)
except ValueError as e:
print(f"❌ Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,85 @@
#!/usr/bin/env python3
"""
Generate style file (CSS Modules or Styled Components).
Creates scoped styles for React components.
"""
import sys
import argparse
import os
def read_template(template_path: str) -> str:
"""Read template file content."""
try:
with open(template_path, 'r') as f:
return f.read()
except FileNotFoundError:
raise FileNotFoundError(f"Template file not found: {template_path}")
def generate_style(name: str, approach: str, template_content: str) -> str:
"""
Generate style code by substituting placeholders in template.
Args:
name: Component name (PascalCase)
approach: Styling approach (css-modules, styled-components, tailwind)
template_content: Template file content
Returns:
str: Generated style code
"""
# Convert PascalCase to kebab-case
kebab_name = ''.join(['-' + c.lower() if c.isupper() else c for c in name]).lstrip('-')
# Perform substitutions
substitutions = {
'${COMPONENT_NAME}': name,
'${COMPONENT_NAME_KEBAB}': kebab_name,
'${BASE_STYLES}': """ display: flex;
flex-direction: column;
gap: 1rem;""",
}
result = template_content
for placeholder, value in substitutions.items():
result = result.replace(placeholder, value)
return result
def main():
parser = argparse.ArgumentParser(description='Generate React component style file')
parser.add_argument('--name', required=True, help='Component name (PascalCase)')
parser.add_argument('--approach', default='css-modules', choices=['css-modules', 'styled-components', 'tailwind'], help='Styling approach')
parser.add_argument('--template', required=True, help='Style template file path')
parser.add_argument('--output', help='Output file path (optional, prints to stdout if not provided)')
args = parser.parse_args()
try:
# Read template
template_content = read_template(args.template)
# Generate style
style_code = generate_style(
args.name,
args.approach,
template_content
)
# Output
if args.output:
os.makedirs(os.path.dirname(args.output), exist_ok=True)
with open(args.output, 'w') as f:
f.write(style_code)
print(f"✅ Style file generated: {args.output}")
else:
print(style_code)
sys.exit(0)
except Exception as e:
print(f"❌ Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,100 @@
#!/usr/bin/env python3
"""
Generate test file with React Testing Library.
Creates comprehensive test suite for React components.
"""
import sys
import argparse
import os
def read_template(template_path: str) -> str:
"""Read template file content."""
try:
with open(template_path, 'r') as f:
return f.read()
except FileNotFoundError:
raise FileNotFoundError(f"Template file not found: {template_path}")
def generate_test(component_name: str, component_path: str, template_content: str) -> str:
"""
Generate test code by substituting placeholders in template.
Args:
component_name: Component name (PascalCase)
component_path: Path to component file
template_content: Template file content
Returns:
str: Generated test code
"""
# Calculate relative import path
import_path = f'./{component_name}'
# Basic test cases (can be expanded based on props analysis)
test_cases = f"""
it('renders without crashing', () => {{
render(<{component_name} />);
}});
it('renders children correctly', () => {{
render(<{component_name}>Test Content</{component_name}>);
expect(screen.getByText('Test Content')).toBeInTheDocument();
}});
it('applies custom className', () => {{
const {{ container }} = render(<{component_name} className="custom-class" />);
expect(container.firstChild).toHaveClass('custom-class');
}});
""".strip()
# Perform substitutions
substitutions = {
'${COMPONENT_NAME}': component_name,
'${IMPORT_PATH}': import_path,
'${TEST_CASES}': test_cases,
}
result = template_content
for placeholder, value in substitutions.items():
result = result.replace(placeholder, value)
return result
def main():
parser = argparse.ArgumentParser(description='Generate React component test file')
parser.add_argument('--component-name', required=True, help='Component name (PascalCase)')
parser.add_argument('--component-path', required=True, help='Path to component file')
parser.add_argument('--template', required=True, help='Test template file path')
parser.add_argument('--output', help='Output file path (optional, prints to stdout if not provided)')
args = parser.parse_args()
try:
# Read template
template_content = read_template(args.template)
# Generate test
test_code = generate_test(
args.component_name,
args.component_path,
template_content
)
# Output
if args.output:
os.makedirs(os.path.dirname(args.output), exist_ok=True)
with open(args.output, 'w') as f:
f.write(test_code)
print(f"✅ Test file generated: {args.output}")
else:
print(test_code)
sys.exit(0)
except Exception as e:
print(f"❌ Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,20 @@
/**
* ${COMPONENT_NAME} - ${DESCRIPTION}
*/
import React from 'react';
${STYLE_IMPORT}
${PROPS_INTERFACE}
export const ${COMPONENT_NAME}: React.FC<${PROPS_INTERFACE}> = ({
children,
className,
...props
}) => {
return (
<div className={`${styles.container} ${className || ''}`}>
{children}
</div>
);
};

View File

@@ -0,0 +1,15 @@
/* ${COMPONENT_NAME} Styles */
.container {
${BASE_STYLES}
}
.title {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
.content {
flex: 1;
}

View File

@@ -0,0 +1,7 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { ${COMPONENT_NAME} } from '${IMPORT_PATH}';
describe('${COMPONENT_NAME}', () => {
${TEST_CASES}
});