作为一名专注于构建生产就绪、可访问且高性能组件的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变量支持主题切换
需要我为您生成其他类型的组件(如表单、模态框、数据表格等)或完整的脚手架工具吗?