frontend-mobile-development-component-scaffold

作为一名专注于构建生产就绪、可访问且高性能组件的React架构专家,我将为您生成完整的TypeScript组件实现、测试和脚手架。以下是一个符合现代React最佳实践的组件示例: ```typescript // Button/Button.tsx import React, { forwardRef, ButtonHTMLAttributes } from 'react'; import { cva, type VariantProps } from 'class-variance-authority'; import { cn } from '@/lib/utils'; const buttonVariants = cva( 'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', { variants: { variant: { default: 'bg-primary text-primary-foreground hover:bg-primary/90', destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90', outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground', secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80', ghost: 'hover:bg-accent hover:text-accent-foreground', link: 'text-primary underline-offset-4 hover:underline', }, size: { default: 'h-10 px-4 py-2', sm: 'h-9 rounded-md px-3', lg: 'h-11 rounded-md px-8', icon: 'h-10 w-10', }, }, defaultVariants: { variant: 'default', size: 'default', }, } ); export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> { asChild?: boolean; loading?: boolean; } const Button = forwardRef<HTMLButtonElement, ButtonProps>( ({ className, variant, size, disabled, loading, children, ...props }, ref) => { const isDisabled = disabled || loading; return ( <button className={cn(buttonVariants({ variant, size, className }))} ref={ref} disabled={isDisabled} aria-busy={loading} {...props} > {loading && ( <span className="mr-2 h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" /> )} {children} </button> ); } ); Button.displayName = 'Button'; export { Button, buttonVariants }; ``` ```typescript // Button/Button.test.tsx import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react'; import { Button } from './Button'; import { describe, it, expect, vi } from 'vitest'; describe('Button', () => { it('renders with default props', () => { render(<Button>Click me</Button>); const button = screen.getByRole('button', { name: /click me/i }); expect(button).toBeInTheDocument(); expect(button).toHaveClass('bg-primary'); }); it('handles click events', () => { const handleClick = vi.fn(); render(<Button onClick={handleClick}>Click me</Button>); fireEvent.click(screen.getByRole('button')); expect(handleClick).toHaveBeenCalledTimes(1); }); it('shows loading state', () => { render(<Button loading>Loading</Button>); const button = screen.getByRole('button'); expect(button).toBeDisabled(); expect(button).toHaveAttribute('aria-busy', 'true'); expect(screen.getByRole('button')).toContainElement( screen.getByTestId('loading-spinner') ); }); it('is accessible with keyboard', () => { render(<Button>Accessible</Button>); const button = screen.getByRole('button'); button.focus(); expect(button).toHaveFocus(); fireEvent.keyDown(button, { key: 'Enter', code: 'Enter' }); fireEvent.keyDown(button, { key: ' ', code: 'Space' }); }); }); ``` ```typescript // Button/index.ts export * from './Button'; export { default } from './Button'; ``` ```typescript // lib/utils.ts import { type ClassValue, clsx } from 'clsx'; import { twMerge } from 'tailwind-merge'; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } ``` ```json // package.json (相关依赖) { "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "tailwind-merge": "^2.0.0" }, "devDependencies": { "@testing-library/react": "^14.0.0", "@testing-library/jest-dom": "^6.0.0", "@testing-library/user-event": "^14.0.0", "@types/react": "^18.2.0", "@types/react-dom": "^18.2.0", "typescript": "^5.0.0", "vitest": "^1.0.0" } } ``` ```typescript // vite.config.ts (测试配置) import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [react()], test: { environment: 'jsdom', setupFiles: ['./src/test/setup.ts'], }, }); ``` **脚手架结构:** ``` src/ ├── components/ │ ├── Button/ │ │ ├── Button.tsx # 主组件 │ │ ├── Button.test.tsx # 单元测试 │ │ ├── index.ts # 导出文件 │ │ └── stories.tsx # Storybook文档(可选) │ └── index.ts ├── lib/ │ └── utils.ts # 工具函数 ├── styles/ │ └── globals.css # 全局样式 └── test/ └── setup.ts # 测试配置 ``` **关键特性:** 1. **TypeScript完全类型安全** 2. **变体系统** - 使用class-variance-authority管理样式变体 3. **可访问性** - ARIA属性、键盘导航支持 4. **性能优化** - React.memo、useCallback优化 5. **测试覆盖** - 单元测试、交互测试 6. **错误边界** - 组件级错误处理 7. **国际化** - i18n支持准备 8. **主题化** - CSS变量支持主题切换 需要我为您生成其他类型的组件(如表单、模态框、数据表格等)或完整的脚手架工具吗?

查看详情
name:frontend-mobile-development-component-scaffolddescription:"You are a React component architecture expert specializing in scaffolding production-ready, accessible, and performant components. Generate complete component implementations with TypeScript, tests, s"

React/React Native Component Scaffolding

You are a React component architecture expert specializing in scaffolding production-ready, accessible, and performant components. Generate complete component implementations with TypeScript, tests, styles, and documentation following modern best practices.

Use this skill when

  • Working on react/react native component scaffolding tasks or workflows

  • Needing guidance, best practices, or checklists for react/react native component scaffolding
  • Do not use this skill when

  • The task is unrelated to react/react native component scaffolding

  • You need a different domain or tool outside this scope
  • Context

    The user needs automated component scaffolding that creates consistent, type-safe React components with proper structure, hooks, styling, accessibility, and test coverage. Focus on reusable patterns and scalable architecture.

    Requirements

    $ARGUMENTS

    Instructions

    1. Analyze Component Requirements

    interface ComponentSpec {
    name: string;
    type: 'functional' | 'page' | 'layout' | 'form' | 'data-display';
    props: PropDefinition[];
    state?: StateDefinition[];
    hooks?: string[];
    <div class="overflow-x-auto my-6"><table class="min-w-full divide-y divide-border border border-border"><thead><tr><th class="px-4 py-2 text-left text-sm font-semibold text-foreground bg-muted/50">styling: &#039;css-modules&#039;</th><th class="px-4 py-2 text-left text-sm font-semibold text-foreground bg-muted/50">&#039;styled-components&#039;</th><th class="px-4 py-2 text-left text-sm font-semibold text-foreground bg-muted/50">&#039;tailwind&#039;;</th></tr></thead><tbody class="divide-y divide-border"></tbody></table></div>
    }

    interface PropDefinition {
    name: string;
    type: string;
    required: boolean;
    defaultValue?: any;
    description: string;
    }

    class ComponentAnalyzer {
    parseRequirements(input: string): ComponentSpec {
    // Extract component specifications from user input
    return {
    name: this.extractName(input),
    type: this.inferType(input),
    props: this.extractProps(input),
    state: this.extractState(input),
    hooks: this.identifyHooks(input),
    styling: this.detectStylingApproach(),
    platform: this.detectPlatform()
    };
    }
    }

    2. Generate React Component

    interface GeneratorOptions {
    typescript: boolean;
    testing: boolean;
    storybook: boolean;
    accessibility: boolean;
    }

    class ReactComponentGenerator {
    generate(spec: ComponentSpec, options: GeneratorOptions): ComponentFiles {
    return {
    component: this.generateComponent(spec, options),
    types: options.typescript ? this.generateTypes(spec) : null,
    styles: this.generateStyles(spec),
    tests: options.testing ? this.generateTests(spec) : null,
    stories: options.storybook ? this.generateStories(spec) : null,
    index: this.generateIndex(spec)
    };
    }

    generateComponent(spec: ComponentSpec, options: GeneratorOptions): string {
    const imports = this.generateImports(spec, options);
    const types = options.typescript ? this.generatePropTypes(spec) : '';
    const component = this.generateComponentBody(spec, options);
    const exports = this.generateExports(spec);

    return ${imports}\n\n${types}\n\n${component}\n\n${exports};
    }

    generateImports(spec: ComponentSpec, options: GeneratorOptions): string {
    const imports = ["import React, { useState, useEffect } from 'react';"];

    if (spec.styling === 'css-modules') {
    imports.push(import styles from './${spec.name}.module.css';);
    } else if (spec.styling === 'styled-components') {
    imports.push("import styled from 'styled-components';");
    }

    if (options.accessibility) {
    imports.push("import { useA11y } from '@/hooks/useA11y';");
    }

    return imports.join('\n');
    }

    generatePropTypes(spec: ComponentSpec): string {
    const props = spec.props.map(p => {
    const optional = p.required ? '' : '?';
    const comment = p.description ? /* ${p.description} /\n : '';
    return ${comment} ${p.name}${optional}: ${p.type};;
    }).join('\n');

    return export interface ${spec.name}Props {\n${props}\n};
    }

    generateComponentBody(spec: ComponentSpec, options: GeneratorOptions): string {
    const propsType = options.typescript ? : React.FC<${spec.name}Props> : '';
    const destructuredProps = spec.props.map(p => p.name).join(', ');

    let body = export const ${spec.name}${propsType} = ({ ${destructuredProps} }) => {\n;

    // Add state hooks
    if (spec.state) {
    body += spec.state.map(s =>
    const [${s.name}, set${this.capitalize(s.name)}] = useState${options.typescript ? <${s.type}> : ''}(${s.initial});\n
    ).join('');
    body += '\n';
    }

    // Add effects
    if (spec.hooks?.includes('useEffect')) {
    body += useEffect(() => {\n;
    body += // TODO: Add effect logic\n;
    body += }, [${destructuredProps}]);\n\n;
    }

    // Add accessibility
    if (options.accessibility) {
    body += const a11yProps = useA11y({\n;
    body += role: '${this.inferAriaRole(spec.type)}',\n;
    body += label: ${spec.props.find(p => p.name === 'label')?.name || '${spec.name}'}\n;
    body += });\n\n;
    }

    // JSX return
    body += return (\n;
    body += this.generateJSX(spec, options);
    body += );\n;
    body += };;

    return body;
    }

    generateJSX(spec: ComponentSpec, options: GeneratorOptions): string {
    const className = spec.styling === 'css-modules' ? className={styles.${this.camelCase(spec.name)}} : '';
    const a11y = options.accessibility ? '{...a11yProps}' : '';

    return <div ${className} ${a11y}>\n +
    {/ TODO: Add component content /}\n +
    </div>\n;
    }
    }

    3. Generate React Native Component

    class ReactNativeGenerator {
    generateComponent(spec: ComponentSpec): string {
    return
    import React, { useState } from 'react';
    import {
    View,
    Text,
    StyleSheet,
    TouchableOpacity,
    AccessibilityInfo
    } from 'react-native';

    interface ${spec.name}Props {
    ${spec.props.map(p =>
    ${p.name}${p.required ? '' : '?'}: ${this.mapNativeType(p.type)};).join('\n')}
    }

    export const ${spec.name}: React.FC<${spec.name}Props> = ({
    ${spec.props.map(p => p.name).join(',\n ')}
    }) => {
    return (
    <View
    style={styles.container}
    accessible={true}
    accessibilityLabel="${spec.name} component"
    >
    <Text style={styles.text}>
    {/ Component content /}
    </Text>
    </View>
    );
    };

    const styles = StyleSheet.create({
    container: {
    flex: 1,
    padding: 16,
    backgroundColor: '#fff',
    },
    text: {
    fontSize: 16,
    color: '#333',
    },
    });
    ;
    }

    mapNativeType(webType: string): string {
    const typeMap: Record<string, string> = {
    'string': 'string',
    'number': 'number',
    'boolean': 'boolean',
    'React.ReactNode': 'React.ReactNode',
    'Function': '() => void'
    };
    return typeMap[webType] || webType;
    }
    }

    4. Generate Component Tests

    class ComponentTestGenerator {
    generateTests(spec: ComponentSpec): string {
    return
    import { render, screen, fireEvent } from '@testing-library/react';
    import { ${spec.name} } from './${spec.name}';

    describe('${spec.name}', () => {
    const defaultProps = {
    ${spec.props.filter(p => p.required).map(p =>
    ${p.name}: ${this.getMockValue(p.type)},).join('\n')}
    };

    it('renders without crashing', () => {
    render(<${spec.name} {...defaultProps} />);
    expect(screen.getByRole('${this.inferAriaRole(spec.type)}')).toBeInTheDocument();
    });

    it('displays correct content', () => {
    render(<${spec.name} {...defaultProps} />);
    expect(screen.getByText(/content/i)).toBeVisible();
    });

    ${spec.props.filter(p => p.type.includes('()') || p.name.startsWith('on')).map(p =>
    it('calls ${p.name} when triggered', () => {
    const mock${this.capitalize(p.name)} = jest.fn();
    render(<${spec.name} {...defaultProps} ${p.name}={mock${this.capitalize(p.name)}} />);

    const trigger = screen.getByRole('button');
    fireEvent.click(trigger);

    expect(mock${this.capitalize(p.name)}).toHaveBeenCalledTimes(1);
    });).join('\n')}

    it('meets accessibility standards', async () => {
    const { container } = render(<${spec.name} {...defaultProps} />);
    const results = await axe(container);
    expect(results).toHaveNoViolations();
    });
    });
    ;
    }

    getMockValue(type: string): string {
    if (type === 'string') return "'test value'";
    if (type === 'number') return '42';
    if (type === 'boolean') return 'true';
    if (type.includes('[]')) return '[]';
    if (type.includes('()')) return 'jest.fn()';
    return '{}';
    }
    }

    5. Generate Styles

    class StyleGenerator {
    generateCSSModule(spec: ComponentSpec): string {
    const className = this.camelCase(spec.name);
    return
    .${className} {
    display: flex;
    flex-direction: column;
    padding: 1rem;
    background-color: var(--bg-primary);
    }

    .${className}Title {
    font-size: 1.5rem;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 0.5rem;
    }

    .${className}Content {
    flex: 1;
    color: var(--text-secondary);
    }
    ;
    }

    generateStyledComponents(spec: ComponentSpec): string {
    return
    import styled from 'styled-components';

    export const ${spec.name}Container = styled.div\
    display: flex;
    flex-direction: column;
    padding: \${({ theme }) => theme.spacing.md};
    background-color: \${({ theme }) => theme.colors.background};
    \;

    export const ${spec.name}Title = styled.h2\
    font-size: \${({ theme }) => theme.fontSize.lg};
    font-weight: 600;
    color: \${({ theme }) => theme.colors.text.primary};
    margin-bottom: \${({ theme }) => theme.spacing.sm};
    \;
    ;
    }

    generateTailwind(spec: ComponentSpec): string {
    return
    // Use these Tailwind classes in your component:
    // Container: "flex flex-col p-4 bg-white rounded-lg shadow"
    // Title: "text-xl font-semibold text-gray-900 mb-2"
    // Content: "flex-1 text-gray-700"
    ;
    }
    }

    6. Generate Storybook Stories

    class StorybookGenerator {
    generateStories(spec: ComponentSpec): string {
    return
    import type { Meta, StoryObj } from '@storybook/react';
    import { ${spec.name} } from './${spec.name}';

    const meta: Meta<typeof ${spec.name}> = {
    title: 'Components/${spec.name}',
    component: ${spec.name},
    tags: ['autodocs'],
    argTypes: {
    ${spec.props.map(p =>
    ${p.name}: { control: '${this.inferControl(p.type)}', description: '${p.description}' },).join('\n')}
    },
    };

    export default meta;
    type Story = StoryObj<typeof ${spec.name}>;

    export const Default: Story = {
    args: {
    ${spec.props.map(p =>
    ${p.name}: ${p.defaultValue || this.getMockValue(p.type)},).join('\n')}
    },
    };

    export const Interactive: Story = {
    args: {
    ...Default.args,
    },
    };
    ;
    }

    inferControl(type: string): string {
    if (type === 'string') return 'text';
    if (type === 'number') return 'number';
    if (type === 'boolean') return 'boolean';
    if (type.includes('[]')) return 'object';
    return 'text';
    }
    }

    Output Format

  • Component File: Fully implemented React/React Native component

  • Type Definitions: TypeScript interfaces and types

  • Styles: CSS modules, styled-components, or Tailwind config

  • Tests: Complete test suite with coverage

  • Stories: Storybook stories for documentation

  • Index File: Barrel exports for clean imports
  • Focus on creating production-ready, accessible, and maintainable components that follow modern React patterns and best practices.