specweave 0.23.16 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +93 -38
- package/CLAUDE.md +159 -11
- package/dist/plugins/specweave-github/lib/github-spec-content-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-spec-content-sync.js +57 -0
- package/dist/plugins/specweave-github/lib/github-spec-content-sync.js.map +1 -1
- package/dist/src/cli/commands/sync-spec-content.js +3 -0
- package/dist/src/cli/commands/sync-spec-content.js.map +1 -1
- package/dist/src/cli/helpers/ado-area-path-mapper.d.ts +89 -0
- package/dist/src/cli/helpers/ado-area-path-mapper.d.ts.map +1 -0
- package/dist/src/cli/helpers/ado-area-path-mapper.js +213 -0
- package/dist/src/cli/helpers/ado-area-path-mapper.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts +29 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js +109 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado.d.ts +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado.js +2 -0
- package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
- package/dist/src/cli/helpers/smart-filter.d.ts +83 -0
- package/dist/src/cli/helpers/smart-filter.d.ts.map +1 -0
- package/dist/src/cli/helpers/smart-filter.js +265 -0
- package/dist/src/cli/helpers/smart-filter.js.map +1 -0
- package/dist/src/core/progress/progress-tracker.d.ts +4 -1
- package/dist/src/core/progress/progress-tracker.d.ts.map +1 -1
- package/dist/src/core/progress/progress-tracker.js +33 -4
- package/dist/src/core/progress/progress-tracker.js.map +1 -1
- package/dist/src/core/qa/quality-gate-decider.d.ts +1 -1
- package/dist/src/core/qa/quality-gate-decider.js +2 -2
- package/dist/src/core/qa/quality-gate-decider.js.map +1 -1
- package/dist/src/core/qa/risk-calculator.d.ts +2 -2
- package/dist/src/core/qa/risk-calculator.js +2 -2
- package/dist/src/core/spec-content-sync.d.ts +1 -1
- package/dist/src/core/spec-content-sync.d.ts.map +1 -1
- package/dist/src/core/validators/ac-presence-validator.d.ts +56 -0
- package/dist/src/core/validators/ac-presence-validator.d.ts.map +1 -0
- package/dist/src/core/validators/ac-presence-validator.js +149 -0
- package/dist/src/core/validators/ac-presence-validator.js.map +1 -0
- package/dist/src/integrations/ado/ado-dependency-loader.d.ts +1 -1
- package/dist/src/integrations/ado/ado-dependency-loader.d.ts.map +1 -1
- package/dist/src/integrations/ado/ado-dependency-loader.js +39 -7
- package/dist/src/integrations/ado/ado-dependency-loader.js.map +1 -1
- package/dist/src/integrations/ado/area-path-mapper.d.ts +137 -0
- package/dist/src/integrations/ado/area-path-mapper.d.ts.map +1 -0
- package/dist/src/integrations/ado/area-path-mapper.js +267 -0
- package/dist/src/integrations/ado/area-path-mapper.js.map +1 -0
- package/dist/src/integrations/jira/filter-processor.d.ts +126 -0
- package/dist/src/integrations/jira/filter-processor.d.ts.map +1 -0
- package/dist/src/integrations/jira/filter-processor.js +207 -0
- package/dist/src/integrations/jira/filter-processor.js.map +1 -0
- package/dist/src/integrations/jira/jira-client.d.ts +13 -0
- package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
- package/dist/src/integrations/jira/jira-client.js +33 -0
- package/dist/src/integrations/jira/jira-client.js.map +1 -1
- package/dist/src/utils/ac-embedder.d.ts +63 -0
- package/dist/src/utils/ac-embedder.d.ts.map +1 -0
- package/dist/src/utils/ac-embedder.js +217 -0
- package/dist/src/utils/ac-embedder.js.map +1 -0
- package/dist/src/utils/env-manager.d.ts +86 -0
- package/dist/src/utils/env-manager.d.ts.map +1 -0
- package/dist/src/utils/env-manager.js +188 -0
- package/dist/src/utils/env-manager.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave/agents/AGENTS-INDEX.md +1 -1
- package/plugins/specweave/agents/increment-quality-judge-v2/AGENT.md +9 -9
- package/plugins/specweave/commands/specweave-do.md +37 -0
- package/plugins/specweave/commands/specweave-done.md +159 -0
- package/plugins/specweave/commands/specweave-embed-acs.md +446 -0
- package/plugins/specweave/commands/specweave-next.md +148 -3
- package/plugins/specweave/commands/specweave-qa.md +2 -2
- package/plugins/specweave/hooks/lib/migrate-increment-work.sh +1 -1
- package/plugins/specweave/hooks/lib/migrate-increment-work.sh.bak +245 -0
- package/plugins/specweave/hooks/lib/sync-spec-content.sh +2 -2
- package/plugins/specweave/hooks/lib/sync-spec-content.sh.bak +149 -0
- package/plugins/specweave/hooks/lib/update-status-line.sh +34 -4
- package/plugins/specweave/hooks/lib/validate-spec-status.sh +1 -1
- package/plugins/specweave/hooks/lib/validate-spec-status.sh.bak +163 -0
- package/plugins/specweave/hooks/post-first-increment.sh +1 -1
- package/plugins/specweave/hooks/post-first-increment.sh.bak +61 -0
- package/plugins/specweave/hooks/post-spec-update.sh +1 -1
- package/plugins/specweave/hooks/post-spec-update.sh.bak +158 -0
- package/plugins/specweave/hooks/post-user-story-complete.sh +1 -1
- package/plugins/specweave/hooks/post-user-story-complete.sh.bak +179 -0
- package/plugins/specweave/hooks/pre-command-deduplication.sh +1 -1
- package/plugins/specweave/hooks/pre-command-deduplication.sh.bak +83 -0
- package/plugins/specweave/hooks/pre-increment-start.sh +168 -0
- package/plugins/specweave/hooks/user-prompt-submit.sh +1 -1
- package/plugins/specweave/hooks/user-prompt-submit.sh.bak +386 -0
- package/plugins/specweave/skills/SKILLS-INDEX.md +1 -1
- package/plugins/specweave/skills/specweave-framework/SKILL.md +1 -1
- package/plugins/specweave-ado/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-ado/agents/ado-manager/AGENT.md +23 -0
- package/plugins/specweave-ado/agents/ado-multi-project-mapper/AGENT.md +23 -0
- package/plugins/specweave-ado/agents/ado-sync-judge/AGENT.md +23 -0
- package/plugins/specweave-ado/commands/specweave-ado-import-projects.md +331 -0
- package/plugins/specweave-alternatives/.claude-plugin/plugin.json +10 -0
- package/plugins/specweave-alternatives/commands/alternatives-analyze.md +336 -0
- package/plugins/specweave-alternatives/skills/architecture-alternatives/SKILL.md +651 -0
- package/plugins/specweave-alternatives/skills/bmad-method/SKILL.md +420 -0
- package/plugins/specweave-alternatives/skills/spec-kit-expert/SKILL.md +487 -0
- package/plugins/specweave-backend/agents/database-optimizer/AGENT.md +23 -0
- package/plugins/specweave-backend/commands/api-scaffold.md +80 -0
- package/plugins/specweave-backend/commands/crud-generate.md +109 -0
- package/plugins/specweave-backend/commands/migration-generate.md +139 -0
- package/plugins/specweave-confluent/agents/confluent-architect/AGENT.md +23 -0
- package/plugins/specweave-confluent/commands/connector-deploy.md +154 -0
- package/plugins/specweave-confluent/commands/ksqldb-query.md +179 -0
- package/plugins/specweave-confluent/commands/schema-register.md +123 -0
- package/plugins/specweave-core/.claude-plugin/plugin.json +21 -0
- package/plugins/specweave-core/commands/architecture-review.md +288 -0
- package/plugins/specweave-core/commands/code-review.md +213 -0
- package/plugins/specweave-core/commands/refactor-plan.md +249 -0
- package/plugins/specweave-core/skills/code-quality/SKILL.md +157 -0
- package/plugins/specweave-core/skills/design-patterns/SKILL.md +244 -0
- package/plugins/specweave-core/skills/software-architecture/SKILL.md +83 -0
- package/plugins/specweave-cost-optimizer/.claude-plugin/plugin.json +22 -0
- package/plugins/specweave-cost-optimizer/commands/cost-analyze.md +360 -0
- package/plugins/specweave-cost-optimizer/commands/cost-optimize.md +480 -0
- package/plugins/specweave-cost-optimizer/skills/aws-cost-expert/SKILL.md +416 -0
- package/plugins/specweave-cost-optimizer/skills/cloud-pricing/SKILL.md +325 -0
- package/plugins/specweave-cost-optimizer/skills/cost-optimization/SKILL.md +337 -0
- package/plugins/specweave-diagrams/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-diagrams/agents/diagrams-architect/AGENT.md +23 -0
- package/plugins/specweave-diagrams/commands/diagrams-generate.md +168 -0
- package/plugins/specweave-docs/.claude-plugin/plugin.json +10 -0
- package/plugins/specweave-docs/commands/docs-generate.md +441 -0
- package/plugins/specweave-docs/commands/docs-init.md +334 -0
- package/plugins/specweave-docs/skills/docusaurus/SKILL.md +581 -0
- package/plugins/specweave-docs/skills/spec-driven-brainstorming/SKILL.md +689 -0
- package/plugins/specweave-docs/skills/technical-writing/SKILL.md +1039 -0
- package/plugins/specweave-docs-preview/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-figma/.claude-plugin/plugin.json +23 -0
- package/plugins/specweave-figma/commands/figma-import.md +690 -0
- package/plugins/specweave-figma/commands/figma-to-react.md +834 -0
- package/plugins/specweave-figma/commands/figma-tokens.md +815 -0
- package/plugins/specweave-frontend/.claude-plugin/plugin.json +21 -0
- package/plugins/specweave-frontend/agents/frontend-architect/AGENT.md +387 -0
- package/plugins/specweave-frontend/agents/frontend-architect/README.md +385 -0
- package/plugins/specweave-frontend/agents/frontend-architect/examples.md +590 -0
- package/plugins/specweave-frontend/agents/frontend-architect/templates/component-template.tsx +152 -0
- package/plugins/specweave-frontend/agents/frontend-architect/templates/hook-template.ts +311 -0
- package/plugins/specweave-frontend/agents/frontend-architect/templates/page-template.tsx +228 -0
- package/plugins/specweave-frontend/commands/component-generate.md +510 -0
- package/plugins/specweave-frontend/commands/design-system-init.md +494 -0
- package/plugins/specweave-frontend/commands/frontend-scaffold.md +207 -0
- package/plugins/specweave-frontend/commands/nextjs-setup.md +396 -0
- package/plugins/specweave-frontend/skills/design-system-architect/SKILL.md +278 -0
- package/plugins/specweave-frontend/skills/frontend/SKILL.md +420 -0
- package/plugins/specweave-frontend/skills/nextjs/SKILL.md +546 -0
- package/plugins/specweave-github/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-github/agents/github-manager/AGENT.md +23 -0
- package/plugins/specweave-github/agents/github-task-splitter/AGENT.md +25 -0
- package/plugins/specweave-github/agents/user-story-updater/AGENT.md +25 -0
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +194 -0
- package/plugins/specweave-github/lib/github-spec-content-sync.js +49 -0
- package/plugins/specweave-github/lib/github-spec-content-sync.ts +67 -0
- package/plugins/specweave-infrastructure/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-infrastructure/agents/devops/AGENT.md +26 -0
- package/plugins/specweave-infrastructure/agents/network-engineer/AGENT.md +26 -0
- package/plugins/specweave-infrastructure/agents/observability-engineer/AGENT.md +26 -0
- package/plugins/specweave-infrastructure/agents/performance-engineer/AGENT.md +26 -0
- package/plugins/specweave-infrastructure/agents/sre/AGENT.md +26 -0
- package/plugins/specweave-jira/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-jira/agents/jira-manager/AGENT.md +26 -0
- package/plugins/specweave-jira/commands/import-projects.js +183 -0
- package/plugins/specweave-jira/commands/import-projects.md +97 -0
- package/plugins/specweave-jira/commands/import-projects.ts +288 -0
- package/plugins/specweave-jira/commands/specweave-jira-import-projects.md +298 -0
- package/plugins/specweave-kafka/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-kafka/agents/kafka-architect/AGENT.md +26 -0
- package/plugins/specweave-kafka/agents/kafka-devops/AGENT.md +26 -0
- package/plugins/specweave-kafka/agents/kafka-observability/AGENT.md +26 -0
- package/plugins/specweave-kafka-streams/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-kubernetes/agents/kubernetes-architect/AGENT.md +26 -0
- package/plugins/specweave-kubernetes/commands/cluster-setup.md +262 -0
- package/plugins/specweave-kubernetes/commands/deployment-generate.md +242 -0
- package/plugins/specweave-kubernetes/commands/helm-scaffold.md +333 -0
- package/plugins/specweave-ml/.claude-plugin/plugin.json +3 -3
- package/plugins/specweave-ml/agents/data-scientist/AGENT.md +26 -0
- package/plugins/specweave-ml/agents/ml-engineer/AGENT.md +26 -0
- package/plugins/specweave-ml/agents/mlops-engineer/AGENT.md +26 -0
- package/plugins/specweave-mobile/agents/mobile-architect/AGENT.md +26 -0
- package/plugins/specweave-mobile/commands/app-scaffold.md +233 -0
- package/plugins/specweave-mobile/commands/build-config.md +256 -0
- package/plugins/specweave-mobile/commands/screen-generate.md +289 -0
- package/plugins/specweave-n8n/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-payments/agents/payment-integration/AGENT.md +26 -0
- package/plugins/specweave-plugin-dev/.claude-plugin/plugin.json +20 -0
- package/plugins/specweave-plugin-dev/commands/plugin-create.md +333 -0
- package/plugins/specweave-plugin-dev/commands/plugin-publish.md +339 -0
- package/plugins/specweave-plugin-dev/commands/plugin-test.md +293 -0
- package/plugins/specweave-plugin-dev/skills/claude-sdk/SKILL.md +162 -0
- package/plugins/specweave-plugin-dev/skills/marketplace-publishing/SKILL.md +263 -0
- package/plugins/specweave-plugin-dev/skills/plugin-development/SKILL.md +316 -0
- package/plugins/specweave-release/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-release/agents/release-manager/AGENT.md +27 -0
- package/plugins/specweave-release/commands/specweave-release-npm.md +110 -0
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +168 -0
- package/plugins/specweave-testing/.claude-plugin/plugin.json +21 -0
- package/plugins/specweave-testing/agents/qa-engineer/AGENT.md +797 -0
- package/plugins/specweave-testing/agents/qa-engineer/README.md +443 -0
- package/plugins/specweave-testing/agents/qa-engineer/templates/playwright-e2e-test.ts +470 -0
- package/plugins/specweave-testing/agents/qa-engineer/templates/test-data-factory.ts +507 -0
- package/plugins/specweave-testing/agents/qa-engineer/templates/vitest-unit-test.ts +400 -0
- package/plugins/specweave-testing/agents/qa-engineer/test-strategies.md +726 -0
- package/plugins/specweave-testing/commands/e2e-setup.md +1081 -0
- package/plugins/specweave-testing/commands/test-coverage.md +979 -0
- package/plugins/specweave-testing/commands/test-generate.md +1156 -0
- package/plugins/specweave-testing/commands/test-init.md +409 -0
- package/plugins/specweave-testing/skills/e2e-playwright/SKILL.md +769 -0
- package/plugins/specweave-testing/skills/tdd-expert/SKILL.md +934 -0
- package/plugins/specweave-testing/skills/unit-testing-expert/SKILL.md +1011 -0
- package/plugins/specweave-tooling/.claude-plugin/plugin.json +22 -0
- package/plugins/specweave-tooling/commands/specweave-tooling-skill-create.md +691 -0
- package/plugins/specweave-tooling/commands/specweave-tooling-skill-package.md +751 -0
- package/plugins/specweave-tooling/commands/specweave-tooling-skill-validate.md +858 -0
- package/plugins/specweave-ui/.claude-plugin/plugin.json +10 -0
- package/plugins/specweave-ui/commands/ui-automate.md +199 -0
- package/plugins/specweave-ui/commands/ui-inspect.md +70 -0
- package/plugins/specweave-ui/skills/browser-automation/SKILL.md +314 -0
- package/plugins/specweave-ui/skills/ui-testing/SKILL.md +716 -0
- package/plugins/specweave-ui/skills/visual-regression/SKILL.md +728 -0
- package/plugins/specweave/commands/check-hooks.md +0 -257
- package/plugins/specweave/commands/specweave-archive-increments.md +0 -82
- package/plugins/specweave/skills/plugin-expert/SKILL.md +0 -340
- /package/plugins/specweave/{agents/code-reviewer.md → skills/code-reviewer/SKILL.md} +0 -0
|
@@ -0,0 +1,834 @@
|
|
|
1
|
+
# /specweave-figma:to-react
|
|
2
|
+
|
|
3
|
+
Convert Figma components to production-ready React components with TypeScript, styled-components, and responsive design.
|
|
4
|
+
|
|
5
|
+
You are a Figma-to-React conversion expert who generates pixel-perfect, type-safe React components from Figma designs.
|
|
6
|
+
|
|
7
|
+
## Your Task
|
|
8
|
+
|
|
9
|
+
Transform Figma components into React components with proper TypeScript types, styling, accessibility, and responsive behavior.
|
|
10
|
+
|
|
11
|
+
### 1. Conversion Architecture
|
|
12
|
+
|
|
13
|
+
**Design-to-Code Pipeline**:
|
|
14
|
+
```
|
|
15
|
+
Figma Component → Analyze Structure → Extract Props → Generate TSX → Apply Styles → Add Interactivity
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Supported Patterns**:
|
|
19
|
+
- Functional components with hooks
|
|
20
|
+
- TypeScript interfaces for props
|
|
21
|
+
- Styled-components or CSS modules
|
|
22
|
+
- Responsive design (mobile-first)
|
|
23
|
+
- Accessibility attributes (ARIA)
|
|
24
|
+
- Component variants (Figma properties)
|
|
25
|
+
- Auto-layout → Flexbox/Grid
|
|
26
|
+
|
|
27
|
+
### 2. Component Analysis
|
|
28
|
+
|
|
29
|
+
**Figma Node Structure**:
|
|
30
|
+
```typescript
|
|
31
|
+
interface FigmaNode {
|
|
32
|
+
id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
type: 'COMPONENT' | 'FRAME' | 'TEXT' | 'RECTANGLE' | 'VECTOR';
|
|
35
|
+
children?: FigmaNode[];
|
|
36
|
+
|
|
37
|
+
// Layout
|
|
38
|
+
absoluteBoundingBox: { x: number; y: number; width: number; height: number };
|
|
39
|
+
layoutMode?: 'HORIZONTAL' | 'VERTICAL' | 'NONE';
|
|
40
|
+
layoutAlign?: 'MIN' | 'CENTER' | 'MAX' | 'STRETCH';
|
|
41
|
+
primaryAxisSizingMode?: 'FIXED' | 'AUTO';
|
|
42
|
+
counterAxisSizingMode?: 'FIXED' | 'AUTO';
|
|
43
|
+
paddingLeft?: number;
|
|
44
|
+
paddingRight?: number;
|
|
45
|
+
paddingTop?: number;
|
|
46
|
+
paddingBottom?: number;
|
|
47
|
+
itemSpacing?: number;
|
|
48
|
+
|
|
49
|
+
// Styling
|
|
50
|
+
fills?: Fill[];
|
|
51
|
+
strokes?: Stroke[];
|
|
52
|
+
effects?: Effect[];
|
|
53
|
+
cornerRadius?: number;
|
|
54
|
+
|
|
55
|
+
// Text
|
|
56
|
+
characters?: string;
|
|
57
|
+
style?: TextStyle;
|
|
58
|
+
|
|
59
|
+
// Component properties (variants)
|
|
60
|
+
componentPropertyDefinitions?: Record<string, ComponentProperty>;
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Extract Component Metadata**:
|
|
65
|
+
```typescript
|
|
66
|
+
function analyzeComponent(node: FigmaNode) {
|
|
67
|
+
return {
|
|
68
|
+
name: node.name,
|
|
69
|
+
type: inferComponentType(node),
|
|
70
|
+
props: extractProps(node),
|
|
71
|
+
children: node.children?.map(analyzeComponent) || [],
|
|
72
|
+
layout: extractLayout(node),
|
|
73
|
+
styling: extractStyling(node),
|
|
74
|
+
text: node.type === 'TEXT' ? node.characters : null,
|
|
75
|
+
variants: node.componentPropertyDefinitions || {},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function inferComponentType(node: FigmaNode): string {
|
|
80
|
+
// Button detection
|
|
81
|
+
if (node.name.toLowerCase().includes('button')) return 'Button';
|
|
82
|
+
|
|
83
|
+
// Input detection
|
|
84
|
+
if (node.name.toLowerCase().includes('input')) return 'Input';
|
|
85
|
+
|
|
86
|
+
// Card detection
|
|
87
|
+
if (node.name.toLowerCase().includes('card')) return 'Card';
|
|
88
|
+
|
|
89
|
+
// Icon detection
|
|
90
|
+
if (node.type === 'VECTOR') return 'Icon';
|
|
91
|
+
|
|
92
|
+
// Text detection
|
|
93
|
+
if (node.type === 'TEXT') return 'Text';
|
|
94
|
+
|
|
95
|
+
// Generic container
|
|
96
|
+
return 'Container';
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 3. React Component Generation
|
|
101
|
+
|
|
102
|
+
**TypeScript Component Template**:
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { FC } from 'react';
|
|
106
|
+
import styled from 'styled-components';
|
|
107
|
+
|
|
108
|
+
// Generated interfaces from Figma component properties
|
|
109
|
+
interface ${ComponentName}Props {
|
|
110
|
+
variant?: 'primary' | 'secondary' | 'tertiary';
|
|
111
|
+
size?: 'small' | 'medium' | 'large';
|
|
112
|
+
disabled?: boolean;
|
|
113
|
+
onClick?: () => void;
|
|
114
|
+
children?: React.ReactNode;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const ${ComponentName}: FC<${ComponentName}Props> = ({
|
|
118
|
+
variant = 'primary',
|
|
119
|
+
size = 'medium',
|
|
120
|
+
disabled = false,
|
|
121
|
+
onClick,
|
|
122
|
+
children,
|
|
123
|
+
}) => {
|
|
124
|
+
return (
|
|
125
|
+
<StyledContainer
|
|
126
|
+
variant={variant}
|
|
127
|
+
size={size}
|
|
128
|
+
disabled={disabled}
|
|
129
|
+
onClick={onClick}
|
|
130
|
+
>
|
|
131
|
+
{children}
|
|
132
|
+
</StyledContainer>
|
|
133
|
+
);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const StyledContainer = styled.div<${ComponentName}Props>`
|
|
137
|
+
/* Generated styles from Figma */
|
|
138
|
+
`;
|
|
139
|
+
|
|
140
|
+
export default ${ComponentName};
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Complete Example - Button Component**:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { FC, ButtonHTMLAttributes } from 'react';
|
|
147
|
+
import styled from 'styled-components';
|
|
148
|
+
|
|
149
|
+
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
150
|
+
variant?: 'primary' | 'secondary' | 'outline';
|
|
151
|
+
size?: 'sm' | 'md' | 'lg';
|
|
152
|
+
fullWidth?: boolean;
|
|
153
|
+
leftIcon?: React.ReactNode;
|
|
154
|
+
rightIcon?: React.ReactNode;
|
|
155
|
+
loading?: boolean;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const Button: FC<ButtonProps> = ({
|
|
159
|
+
variant = 'primary',
|
|
160
|
+
size = 'md',
|
|
161
|
+
fullWidth = false,
|
|
162
|
+
leftIcon,
|
|
163
|
+
rightIcon,
|
|
164
|
+
loading = false,
|
|
165
|
+
disabled,
|
|
166
|
+
children,
|
|
167
|
+
...props
|
|
168
|
+
}) => {
|
|
169
|
+
return (
|
|
170
|
+
<StyledButton
|
|
171
|
+
variant={variant}
|
|
172
|
+
size={size}
|
|
173
|
+
fullWidth={fullWidth}
|
|
174
|
+
disabled={disabled || loading}
|
|
175
|
+
{...props}
|
|
176
|
+
>
|
|
177
|
+
{leftIcon && <IconWrapper>{leftIcon}</IconWrapper>}
|
|
178
|
+
{loading ? <Spinner /> : children}
|
|
179
|
+
{rightIcon && <IconWrapper>{rightIcon}</IconWrapper>}
|
|
180
|
+
</StyledButton>
|
|
181
|
+
);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const StyledButton = styled.button<ButtonProps>`
|
|
185
|
+
/* Base styles */
|
|
186
|
+
display: inline-flex;
|
|
187
|
+
align-items: center;
|
|
188
|
+
justify-content: center;
|
|
189
|
+
gap: 8px;
|
|
190
|
+
font-family: 'Inter', sans-serif;
|
|
191
|
+
font-weight: 500;
|
|
192
|
+
border: none;
|
|
193
|
+
border-radius: 8px;
|
|
194
|
+
cursor: pointer;
|
|
195
|
+
transition: all 0.2s ease;
|
|
196
|
+
|
|
197
|
+
/* Full width */
|
|
198
|
+
width: ${({ fullWidth }) => fullWidth ? '100%' : 'auto'};
|
|
199
|
+
|
|
200
|
+
/* Size variants */
|
|
201
|
+
${({ size }) => {
|
|
202
|
+
switch (size) {
|
|
203
|
+
case 'sm':
|
|
204
|
+
return `
|
|
205
|
+
padding: 8px 16px;
|
|
206
|
+
font-size: 14px;
|
|
207
|
+
line-height: 20px;
|
|
208
|
+
`;
|
|
209
|
+
case 'lg':
|
|
210
|
+
return `
|
|
211
|
+
padding: 16px 32px;
|
|
212
|
+
font-size: 18px;
|
|
213
|
+
line-height: 24px;
|
|
214
|
+
`;
|
|
215
|
+
default: // md
|
|
216
|
+
return `
|
|
217
|
+
padding: 12px 24px;
|
|
218
|
+
font-size: 16px;
|
|
219
|
+
line-height: 24px;
|
|
220
|
+
`;
|
|
221
|
+
}
|
|
222
|
+
}}
|
|
223
|
+
|
|
224
|
+
/* Color variants */
|
|
225
|
+
${({ variant }) => {
|
|
226
|
+
switch (variant) {
|
|
227
|
+
case 'primary':
|
|
228
|
+
return `
|
|
229
|
+
background: #0066FF;
|
|
230
|
+
color: #FFFFFF;
|
|
231
|
+
|
|
232
|
+
&:hover:not(:disabled) {
|
|
233
|
+
background: #0052CC;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
&:active:not(:disabled) {
|
|
237
|
+
background: #003D99;
|
|
238
|
+
}
|
|
239
|
+
`;
|
|
240
|
+
case 'secondary':
|
|
241
|
+
return `
|
|
242
|
+
background: #F0F0F0;
|
|
243
|
+
color: #333333;
|
|
244
|
+
|
|
245
|
+
&:hover:not(:disabled) {
|
|
246
|
+
background: #E0E0E0;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
&:active:not(:disabled) {
|
|
250
|
+
background: #D0D0D0;
|
|
251
|
+
}
|
|
252
|
+
`;
|
|
253
|
+
case 'outline':
|
|
254
|
+
return `
|
|
255
|
+
background: transparent;
|
|
256
|
+
color: #0066FF;
|
|
257
|
+
border: 2px solid #0066FF;
|
|
258
|
+
|
|
259
|
+
&:hover:not(:disabled) {
|
|
260
|
+
background: rgba(0, 102, 255, 0.1);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
&:active:not(:disabled) {
|
|
264
|
+
background: rgba(0, 102, 255, 0.2);
|
|
265
|
+
}
|
|
266
|
+
`;
|
|
267
|
+
}
|
|
268
|
+
}}
|
|
269
|
+
|
|
270
|
+
/* Disabled state */
|
|
271
|
+
&:disabled {
|
|
272
|
+
opacity: 0.5;
|
|
273
|
+
cursor: not-allowed;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* Focus state (accessibility) */
|
|
277
|
+
&:focus-visible {
|
|
278
|
+
outline: 2px solid #0066FF;
|
|
279
|
+
outline-offset: 2px;
|
|
280
|
+
}
|
|
281
|
+
`;
|
|
282
|
+
|
|
283
|
+
const IconWrapper = styled.span`
|
|
284
|
+
display: inline-flex;
|
|
285
|
+
align-items: center;
|
|
286
|
+
`;
|
|
287
|
+
|
|
288
|
+
const Spinner = styled.div`
|
|
289
|
+
width: 16px;
|
|
290
|
+
height: 16px;
|
|
291
|
+
border: 2px solid currentColor;
|
|
292
|
+
border-right-color: transparent;
|
|
293
|
+
border-radius: 50%;
|
|
294
|
+
animation: spin 0.6s linear infinite;
|
|
295
|
+
|
|
296
|
+
@keyframes spin {
|
|
297
|
+
to { transform: rotate(360deg); }
|
|
298
|
+
}
|
|
299
|
+
`;
|
|
300
|
+
|
|
301
|
+
export default Button;
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### 4. Style Conversion
|
|
305
|
+
|
|
306
|
+
**Figma → CSS Mapping**:
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
function convertFigmaStylesToCSS(node: FigmaNode): string {
|
|
310
|
+
const styles: string[] = [];
|
|
311
|
+
|
|
312
|
+
// Layout (Auto Layout → Flexbox)
|
|
313
|
+
if (node.layoutMode) {
|
|
314
|
+
styles.push('display: flex;');
|
|
315
|
+
styles.push(`flex-direction: ${node.layoutMode === 'HORIZONTAL' ? 'row' : 'column'};`);
|
|
316
|
+
|
|
317
|
+
if (node.layoutAlign) {
|
|
318
|
+
const alignMap = {
|
|
319
|
+
MIN: 'flex-start',
|
|
320
|
+
CENTER: 'center',
|
|
321
|
+
MAX: 'flex-end',
|
|
322
|
+
STRETCH: 'stretch',
|
|
323
|
+
};
|
|
324
|
+
styles.push(`align-items: ${alignMap[node.layoutAlign]};`);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (node.itemSpacing) {
|
|
328
|
+
styles.push(`gap: ${node.itemSpacing}px;`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Padding
|
|
333
|
+
if (node.paddingLeft || node.paddingRight || node.paddingTop || node.paddingBottom) {
|
|
334
|
+
const padding = [
|
|
335
|
+
node.paddingTop || 0,
|
|
336
|
+
node.paddingRight || 0,
|
|
337
|
+
node.paddingBottom || 0,
|
|
338
|
+
node.paddingLeft || 0,
|
|
339
|
+
].join('px ');
|
|
340
|
+
styles.push(`padding: ${padding}px;`);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Size
|
|
344
|
+
const box = node.absoluteBoundingBox;
|
|
345
|
+
if (node.primaryAxisSizingMode === 'FIXED') {
|
|
346
|
+
const dim = node.layoutMode === 'HORIZONTAL' ? 'width' : 'height';
|
|
347
|
+
styles.push(`${dim}: ${box.width}px;`);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Background (fills)
|
|
351
|
+
if (node.fills && node.fills.length > 0) {
|
|
352
|
+
const fill = node.fills[0];
|
|
353
|
+
if (fill.type === 'SOLID') {
|
|
354
|
+
const color = rgbaToCSS(fill.color, fill.opacity);
|
|
355
|
+
styles.push(`background: ${color};`);
|
|
356
|
+
} else if (fill.type === 'GRADIENT_LINEAR') {
|
|
357
|
+
const gradient = convertGradient(fill);
|
|
358
|
+
styles.push(`background: ${gradient};`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Border (strokes)
|
|
363
|
+
if (node.strokes && node.strokes.length > 0) {
|
|
364
|
+
const stroke = node.strokes[0];
|
|
365
|
+
const color = rgbaToCSS(stroke.color, stroke.opacity);
|
|
366
|
+
const width = node.strokeWeight || 1;
|
|
367
|
+
styles.push(`border: ${width}px solid ${color};`);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Border radius
|
|
371
|
+
if (node.cornerRadius) {
|
|
372
|
+
styles.push(`border-radius: ${node.cornerRadius}px;`);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Shadows (effects)
|
|
376
|
+
if (node.effects && node.effects.length > 0) {
|
|
377
|
+
const shadows = node.effects
|
|
378
|
+
.filter((e: any) => e.type === 'DROP_SHADOW')
|
|
379
|
+
.map((e: any) => {
|
|
380
|
+
const color = rgbaToCSS(e.color, e.color.a);
|
|
381
|
+
return `${e.offset.x}px ${e.offset.y}px ${e.radius}px ${color}`;
|
|
382
|
+
})
|
|
383
|
+
.join(', ');
|
|
384
|
+
|
|
385
|
+
if (shadows) {
|
|
386
|
+
styles.push(`box-shadow: ${shadows};`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Typography (text styles)
|
|
391
|
+
if (node.style) {
|
|
392
|
+
styles.push(`font-family: '${node.style.fontFamily}', sans-serif;`);
|
|
393
|
+
styles.push(`font-size: ${node.style.fontSize}px;`);
|
|
394
|
+
styles.push(`font-weight: ${node.style.fontWeight};`);
|
|
395
|
+
styles.push(`line-height: ${node.style.lineHeightPx}px;`);
|
|
396
|
+
|
|
397
|
+
if (node.style.letterSpacing) {
|
|
398
|
+
styles.push(`letter-spacing: ${node.style.letterSpacing}px;`);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (node.style.textAlignHorizontal) {
|
|
402
|
+
const alignMap = { LEFT: 'left', CENTER: 'center', RIGHT: 'right', JUSTIFIED: 'justify' };
|
|
403
|
+
styles.push(`text-align: ${alignMap[node.style.textAlignHorizontal]};`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return styles.join('\n ');
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function rgbaToCSS(color: { r: number; g: number; b: number }, opacity = 1): string {
|
|
411
|
+
const r = Math.round(color.r * 255);
|
|
412
|
+
const g = Math.round(color.g * 255);
|
|
413
|
+
const b = Math.round(color.b * 255);
|
|
414
|
+
|
|
415
|
+
return opacity === 1
|
|
416
|
+
? `rgb(${r}, ${g}, ${b})`
|
|
417
|
+
: `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function convertGradient(fill: any): string {
|
|
421
|
+
const stops = fill.gradientStops
|
|
422
|
+
.map((stop: any) => {
|
|
423
|
+
const color = rgbaToCSS(stop.color, stop.color.a);
|
|
424
|
+
return `${color} ${Math.round(stop.position * 100)}%`;
|
|
425
|
+
})
|
|
426
|
+
.join(', ');
|
|
427
|
+
|
|
428
|
+
const angle = Math.atan2(
|
|
429
|
+
fill.gradientHandlePositions[1].y - fill.gradientHandlePositions[0].y,
|
|
430
|
+
fill.gradientHandlePositions[1].x - fill.gradientHandlePositions[0].x
|
|
431
|
+
) * (180 / Math.PI);
|
|
432
|
+
|
|
433
|
+
return `linear-gradient(${angle}deg, ${stops})`;
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### 5. Responsive Design
|
|
438
|
+
|
|
439
|
+
**Generate Media Queries from Figma Breakpoints**:
|
|
440
|
+
|
|
441
|
+
```typescript
|
|
442
|
+
const breakpoints = {
|
|
443
|
+
mobile: 375,
|
|
444
|
+
tablet: 768,
|
|
445
|
+
desktop: 1440,
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
const StyledCard = styled.div`
|
|
449
|
+
/* Mobile-first base styles */
|
|
450
|
+
padding: 16px;
|
|
451
|
+
font-size: 14px;
|
|
452
|
+
|
|
453
|
+
/* Tablet */
|
|
454
|
+
@media (min-width: ${breakpoints.tablet}px) {
|
|
455
|
+
padding: 24px;
|
|
456
|
+
font-size: 16px;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/* Desktop */
|
|
460
|
+
@media (min-width: ${breakpoints.desktop}px) {
|
|
461
|
+
padding: 32px;
|
|
462
|
+
font-size: 18px;
|
|
463
|
+
}
|
|
464
|
+
`;
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
**Responsive Container**:
|
|
468
|
+
```typescript
|
|
469
|
+
const Container = styled.div`
|
|
470
|
+
width: 100%;
|
|
471
|
+
max-width: 1200px;
|
|
472
|
+
margin: 0 auto;
|
|
473
|
+
padding: 0 16px;
|
|
474
|
+
|
|
475
|
+
@media (min-width: 768px) {
|
|
476
|
+
padding: 0 32px;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
@media (min-width: 1440px) {
|
|
480
|
+
padding: 0 64px;
|
|
481
|
+
}
|
|
482
|
+
`;
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### 6. Component Variants (Figma Properties)
|
|
486
|
+
|
|
487
|
+
**Figma Component Property → React Props**:
|
|
488
|
+
|
|
489
|
+
```typescript
|
|
490
|
+
// Figma component with variants:
|
|
491
|
+
// - State: Default, Hover, Active, Disabled
|
|
492
|
+
// - Size: Small, Medium, Large
|
|
493
|
+
// - Icon: None, Left, Right
|
|
494
|
+
|
|
495
|
+
interface ChipProps {
|
|
496
|
+
state?: 'default' | 'hover' | 'active' | 'disabled';
|
|
497
|
+
size?: 'sm' | 'md' | 'lg';
|
|
498
|
+
iconPosition?: 'none' | 'left' | 'right';
|
|
499
|
+
label: string;
|
|
500
|
+
icon?: React.ReactNode;
|
|
501
|
+
onClick?: () => void;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const Chip: FC<ChipProps> = ({
|
|
505
|
+
state = 'default',
|
|
506
|
+
size = 'md',
|
|
507
|
+
iconPosition = 'none',
|
|
508
|
+
label,
|
|
509
|
+
icon,
|
|
510
|
+
onClick,
|
|
511
|
+
}) => {
|
|
512
|
+
return (
|
|
513
|
+
<StyledChip
|
|
514
|
+
state={state}
|
|
515
|
+
size={size}
|
|
516
|
+
onClick={state !== 'disabled' ? onClick : undefined}
|
|
517
|
+
>
|
|
518
|
+
{iconPosition === 'left' && icon && <IconWrapper>{icon}</IconWrapper>}
|
|
519
|
+
<Label>{label}</Label>
|
|
520
|
+
{iconPosition === 'right' && icon && <IconWrapper>{icon}</IconWrapper>}
|
|
521
|
+
</StyledChip>
|
|
522
|
+
);
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
const StyledChip = styled.div<ChipProps>`
|
|
526
|
+
display: inline-flex;
|
|
527
|
+
align-items: center;
|
|
528
|
+
gap: 8px;
|
|
529
|
+
border-radius: 100px;
|
|
530
|
+
font-weight: 500;
|
|
531
|
+
cursor: ${({ state }) => state === 'disabled' ? 'not-allowed' : 'pointer'};
|
|
532
|
+
transition: all 0.2s;
|
|
533
|
+
|
|
534
|
+
/* Size variants */
|
|
535
|
+
${({ size }) => {
|
|
536
|
+
switch (size) {
|
|
537
|
+
case 'sm': return `padding: 4px 12px; font-size: 12px;`;
|
|
538
|
+
case 'lg': return `padding: 12px 20px; font-size: 16px;`;
|
|
539
|
+
default: return `padding: 8px 16px; font-size: 14px;`;
|
|
540
|
+
}
|
|
541
|
+
}}
|
|
542
|
+
|
|
543
|
+
/* State variants */
|
|
544
|
+
${({ state }) => {
|
|
545
|
+
switch (state) {
|
|
546
|
+
case 'hover':
|
|
547
|
+
return `background: #E0F2FF; color: #0066FF;`;
|
|
548
|
+
case 'active':
|
|
549
|
+
return `background: #0066FF; color: #FFFFFF;`;
|
|
550
|
+
case 'disabled':
|
|
551
|
+
return `background: #F0F0F0; color: #A0A0A0; opacity: 0.6;`;
|
|
552
|
+
default:
|
|
553
|
+
return `background: #F0F0F0; color: #333333;`;
|
|
554
|
+
}
|
|
555
|
+
}}
|
|
556
|
+
`;
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
### 7. Accessibility (a11y)
|
|
560
|
+
|
|
561
|
+
**Add ARIA Attributes**:
|
|
562
|
+
|
|
563
|
+
```typescript
|
|
564
|
+
const AccessibleButton: FC<ButtonProps> = ({
|
|
565
|
+
children,
|
|
566
|
+
disabled,
|
|
567
|
+
loading,
|
|
568
|
+
ariaLabel,
|
|
569
|
+
...props
|
|
570
|
+
}) => {
|
|
571
|
+
return (
|
|
572
|
+
<button
|
|
573
|
+
aria-label={ariaLabel || (typeof children === 'string' ? children : undefined)}
|
|
574
|
+
aria-disabled={disabled || loading}
|
|
575
|
+
aria-busy={loading}
|
|
576
|
+
disabled={disabled || loading}
|
|
577
|
+
{...props}
|
|
578
|
+
>
|
|
579
|
+
{children}
|
|
580
|
+
</button>
|
|
581
|
+
);
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
// Icon buttons MUST have aria-label
|
|
585
|
+
const IconButton: FC<IconButtonProps> = ({ icon, onClick, ariaLabel }) => {
|
|
586
|
+
return (
|
|
587
|
+
<button
|
|
588
|
+
aria-label={ariaLabel} // Required for screen readers
|
|
589
|
+
onClick={onClick}
|
|
590
|
+
>
|
|
591
|
+
{icon}
|
|
592
|
+
</button>
|
|
593
|
+
);
|
|
594
|
+
};
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
**Semantic HTML**:
|
|
598
|
+
```typescript
|
|
599
|
+
// ❌ Wrong: div soup
|
|
600
|
+
<div onClick={onClick}>Submit</div>
|
|
601
|
+
|
|
602
|
+
// ✅ Correct: semantic button
|
|
603
|
+
<button onClick={onClick}>Submit</button>
|
|
604
|
+
|
|
605
|
+
// ❌ Wrong: generic div
|
|
606
|
+
<div>Card Title</div>
|
|
607
|
+
|
|
608
|
+
// ✅ Correct: heading
|
|
609
|
+
<h2>Card Title</h2>
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
### 8. Storybook Integration
|
|
613
|
+
|
|
614
|
+
**Generate Storybook Stories**:
|
|
615
|
+
|
|
616
|
+
```typescript
|
|
617
|
+
// Button.stories.tsx
|
|
618
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
619
|
+
import Button from './Button';
|
|
620
|
+
|
|
621
|
+
const meta: Meta<typeof Button> = {
|
|
622
|
+
title: 'Components/Button',
|
|
623
|
+
component: Button,
|
|
624
|
+
tags: ['autodocs'],
|
|
625
|
+
argTypes: {
|
|
626
|
+
variant: {
|
|
627
|
+
control: 'select',
|
|
628
|
+
options: ['primary', 'secondary', 'outline'],
|
|
629
|
+
},
|
|
630
|
+
size: {
|
|
631
|
+
control: 'select',
|
|
632
|
+
options: ['sm', 'md', 'lg'],
|
|
633
|
+
},
|
|
634
|
+
disabled: {
|
|
635
|
+
control: 'boolean',
|
|
636
|
+
},
|
|
637
|
+
loading: {
|
|
638
|
+
control: 'boolean',
|
|
639
|
+
},
|
|
640
|
+
fullWidth: {
|
|
641
|
+
control: 'boolean',
|
|
642
|
+
},
|
|
643
|
+
},
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
export default meta;
|
|
647
|
+
type Story = StoryObj<typeof Button>;
|
|
648
|
+
|
|
649
|
+
export const Primary: Story = {
|
|
650
|
+
args: {
|
|
651
|
+
variant: 'primary',
|
|
652
|
+
size: 'md',
|
|
653
|
+
children: 'Button',
|
|
654
|
+
},
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
export const Secondary: Story = {
|
|
658
|
+
args: {
|
|
659
|
+
variant: 'secondary',
|
|
660
|
+
size: 'md',
|
|
661
|
+
children: 'Button',
|
|
662
|
+
},
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
export const WithIcons: Story = {
|
|
666
|
+
args: {
|
|
667
|
+
variant: 'primary',
|
|
668
|
+
size: 'md',
|
|
669
|
+
leftIcon: <IconArrowLeft />,
|
|
670
|
+
rightIcon: <IconArrowRight />,
|
|
671
|
+
children: 'Button',
|
|
672
|
+
},
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
export const Loading: Story = {
|
|
676
|
+
args: {
|
|
677
|
+
variant: 'primary',
|
|
678
|
+
size: 'md',
|
|
679
|
+
loading: true,
|
|
680
|
+
children: 'Button',
|
|
681
|
+
},
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
export const Disabled: Story = {
|
|
685
|
+
args: {
|
|
686
|
+
variant: 'primary',
|
|
687
|
+
size: 'md',
|
|
688
|
+
disabled: true,
|
|
689
|
+
children: 'Button',
|
|
690
|
+
},
|
|
691
|
+
};
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
### 9. Testing
|
|
695
|
+
|
|
696
|
+
**Generate Component Tests**:
|
|
697
|
+
|
|
698
|
+
```typescript
|
|
699
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
700
|
+
import Button from './Button';
|
|
701
|
+
|
|
702
|
+
describe('Button', () => {
|
|
703
|
+
it('renders children correctly', () => {
|
|
704
|
+
render(<Button>Click me</Button>);
|
|
705
|
+
expect(screen.getByText('Click me')).toBeInTheDocument();
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
it('calls onClick when clicked', () => {
|
|
709
|
+
const handleClick = vi.fn();
|
|
710
|
+
render(<Button onClick={handleClick}>Click me</Button>);
|
|
711
|
+
|
|
712
|
+
fireEvent.click(screen.getByText('Click me'));
|
|
713
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
it('does not call onClick when disabled', () => {
|
|
717
|
+
const handleClick = vi.fn();
|
|
718
|
+
render(<Button disabled onClick={handleClick}>Click me</Button>);
|
|
719
|
+
|
|
720
|
+
fireEvent.click(screen.getByText('Click me'));
|
|
721
|
+
expect(handleClick).not.toHaveBeenCalled();
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
it('renders loading state correctly', () => {
|
|
725
|
+
render(<Button loading>Click me</Button>);
|
|
726
|
+
expect(screen.queryByText('Click me')).not.toBeInTheDocument();
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
it('renders icons correctly', () => {
|
|
730
|
+
render(
|
|
731
|
+
<Button leftIcon={<span data-testid="left-icon">←</span>}>
|
|
732
|
+
Click me
|
|
733
|
+
</Button>
|
|
734
|
+
);
|
|
735
|
+
|
|
736
|
+
expect(screen.getByTestId('left-icon')).toBeInTheDocument();
|
|
737
|
+
});
|
|
738
|
+
});
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
### 10. Code Generation Automation
|
|
742
|
+
|
|
743
|
+
**Full Pipeline Script**:
|
|
744
|
+
|
|
745
|
+
```typescript
|
|
746
|
+
import { FigmaImporter } from './figma-importer';
|
|
747
|
+
import { generateReactComponent } from './react-generator';
|
|
748
|
+
import fs from 'fs/promises';
|
|
749
|
+
|
|
750
|
+
async function figmaToReact(fileKey: string, componentName: string) {
|
|
751
|
+
// 1. Fetch component from Figma
|
|
752
|
+
const importer = new FigmaImporter({
|
|
753
|
+
accessToken: process.env.FIGMA_ACCESS_TOKEN!,
|
|
754
|
+
fileKey,
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
const file = await importer.fetchFile();
|
|
758
|
+
const component = findComponentByName(file.document, componentName);
|
|
759
|
+
|
|
760
|
+
if (!component) {
|
|
761
|
+
throw new Error(`Component "${componentName}" not found`);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// 2. Analyze component structure
|
|
765
|
+
const analysis = analyzeComponent(component);
|
|
766
|
+
|
|
767
|
+
// 3. Generate React component code
|
|
768
|
+
const reactCode = generateReactComponent(analysis);
|
|
769
|
+
|
|
770
|
+
// 4. Generate TypeScript types
|
|
771
|
+
const types = generateTypeScriptTypes(analysis);
|
|
772
|
+
|
|
773
|
+
// 5. Generate styled-components
|
|
774
|
+
const styles = generateStyledComponents(analysis);
|
|
775
|
+
|
|
776
|
+
// 6. Generate Storybook story
|
|
777
|
+
const story = generateStorybook(analysis);
|
|
778
|
+
|
|
779
|
+
// 7. Generate tests
|
|
780
|
+
const tests = generateTests(analysis);
|
|
781
|
+
|
|
782
|
+
// 8. Save files
|
|
783
|
+
const componentDir = `./src/components/${analysis.name}`;
|
|
784
|
+
await fs.mkdir(componentDir, { recursive: true });
|
|
785
|
+
|
|
786
|
+
await fs.writeFile(`${componentDir}/${analysis.name}.tsx`, reactCode);
|
|
787
|
+
await fs.writeFile(`${componentDir}/${analysis.name}.types.ts`, types);
|
|
788
|
+
await fs.writeFile(`${componentDir}/${analysis.name}.styles.ts`, styles);
|
|
789
|
+
await fs.writeFile(`${componentDir}/${analysis.name}.stories.tsx`, story);
|
|
790
|
+
await fs.writeFile(`${componentDir}/${analysis.name}.test.tsx`, tests);
|
|
791
|
+
await fs.writeFile(`${componentDir}/index.ts`, `export { default } from './${analysis.name}';`);
|
|
792
|
+
|
|
793
|
+
console.log(`✅ Generated React component: ${analysis.name}`);
|
|
794
|
+
console.log(`📁 Location: ${componentDir}`);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// Usage
|
|
798
|
+
figmaToReact('ABC123XYZ456', 'Button').catch(console.error);
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
## Workflow
|
|
802
|
+
|
|
803
|
+
1. Ask about Figma file and component to convert
|
|
804
|
+
2. Fetch component metadata from Figma API
|
|
805
|
+
3. Analyze component structure and variants
|
|
806
|
+
4. Ask about styling approach (styled-components, CSS modules, Tailwind)
|
|
807
|
+
5. Generate TypeScript component with props interface
|
|
808
|
+
6. Convert Figma styles to CSS/styled-components
|
|
809
|
+
7. Add responsive breakpoints if needed
|
|
810
|
+
8. Generate Storybook stories for all variants
|
|
811
|
+
9. Generate unit tests
|
|
812
|
+
10. Save all generated files and provide usage examples
|
|
813
|
+
|
|
814
|
+
## When to Use
|
|
815
|
+
|
|
816
|
+
- Converting Figma designs to React components
|
|
817
|
+
- Building design systems from Figma
|
|
818
|
+
- Automating component creation from mockups
|
|
819
|
+
- Ensuring pixel-perfect implementation
|
|
820
|
+
- Syncing Figma variants with React props
|
|
821
|
+
- Generating Storybook documentation from designs
|
|
822
|
+
|
|
823
|
+
## Best Practices
|
|
824
|
+
|
|
825
|
+
1. **Type Safety**: Always generate TypeScript interfaces
|
|
826
|
+
2. **Variants**: Map Figma component properties to React props
|
|
827
|
+
3. **Accessibility**: Include ARIA attributes and semantic HTML
|
|
828
|
+
4. **Responsive**: Generate mobile-first responsive styles
|
|
829
|
+
5. **Testing**: Create comprehensive unit tests
|
|
830
|
+
6. **Documentation**: Generate Storybook stories automatically
|
|
831
|
+
7. **Naming**: Use PascalCase for components, match Figma names
|
|
832
|
+
8. **Optimization**: Extract common styles to theme tokens
|
|
833
|
+
|
|
834
|
+
Transform Figma designs into production-ready React components!
|