@the-bearded-bear/claude-craft 8.8.1 → 8.8.2
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/Dev/i18n/de/Angular/commands/check-compliance.md +218 -168
- package/Dev/i18n/de/Common/agents/refactoring-specialist.md +146 -69
- package/Dev/i18n/de/Common/commands/add-technology.md +166 -68
- package/Dev/i18n/de/Common/commands/setup-ci.md +216 -33
- package/Dev/i18n/de/Laravel/commands/check-compliance.md +223 -115
- package/Dev/i18n/de/Python/checklists/new-feature.md +319 -44
- package/Dev/i18n/de/React/rules/02-architecture.md +483 -322
- package/Dev/i18n/de/React/rules/03-coding-standards.md +584 -368
- package/Dev/i18n/de/Symfony/commands/check-compliance.md +186 -114
- package/Dev/i18n/de/Symfony/rules/07-testing-symfony.md +679 -10
- package/Dev/i18n/de/UIUX/commands/a11y-component.md +284 -26
- package/Dev/i18n/de/UIUX/commands/design-tokens.md +199 -35
- package/Dev/i18n/de/UIUX/commands/user-flow.md +179 -16
- package/Dev/i18n/de/VueJS/commands/check-compliance.md +227 -102
- package/Dev/i18n/es/Angular/commands/check-compliance.md +217 -167
- package/Dev/i18n/es/Common/commands/add-technology.md +158 -60
- package/Dev/i18n/es/Laravel/commands/check-compliance.md +223 -115
- package/Dev/i18n/es/React/commands/check-testing.md +263 -169
- package/Dev/i18n/es/ReactNative/commands/check-testing.md +256 -198
- package/Dev/i18n/es/Symfony/commands/check-compliance.md +189 -117
- package/Dev/i18n/es/Symfony/commands/check-testing.md +301 -102
- package/Dev/i18n/es/Symfony/commands/migration-plan.md +267 -34
- package/Dev/i18n/es/UIUX/commands/a11y-component.md +274 -16
- package/Dev/i18n/es/UIUX/commands/design-tokens.md +186 -22
- package/Dev/i18n/es/UIUX/commands/user-flow.md +173 -10
- package/Dev/i18n/es/VueJS/commands/check-compliance.md +227 -102
- package/Dev/i18n/fr/Angular/commands/check-compliance.md +217 -167
- package/Dev/i18n/fr/Common/commands/ralph-run.md +138 -60
- package/Dev/i18n/fr/Laravel/commands/check-compliance.md +223 -115
- package/Dev/i18n/fr/React/commands/check-architecture.md +370 -53
- package/Dev/i18n/fr/React/commands/check-code-quality.md +454 -64
- package/Dev/i18n/fr/React/commands/check-testing.md +476 -102
- package/Dev/i18n/fr/Symfony/commands/check-compliance.md +188 -116
- package/Dev/i18n/fr/VueJS/commands/check-compliance.md +227 -102
- package/Dev/i18n/pt/Angular/commands/check-compliance.md +225 -175
- package/Dev/i18n/pt/Common/agents/ralph-conductor.md +211 -30
- package/Dev/i18n/pt/Common/commands/add-technology.md +149 -51
- package/Dev/i18n/pt/Common/commands/ralph-run.md +239 -35
- package/Dev/i18n/pt/Flutter/commands/analyze-performance.md +331 -46
- package/Dev/i18n/pt/Flutter/commands/check-compliance.md +234 -29
- package/Dev/i18n/pt/Flutter/commands/generate-feature.md +526 -51
- package/Dev/i18n/pt/Flutter/commands/generate-widget.md +515 -27
- package/Dev/i18n/pt/Flutter/commands/golden-update.md +322 -46
- package/Dev/i18n/pt/Flutter/commands/localization-check.md +278 -37
- package/Dev/i18n/pt/Laravel/commands/check-compliance.md +212 -104
- package/Dev/i18n/pt/React/commands/accessibility-check.md +151 -42
- package/Dev/i18n/pt/React/commands/bundle-analyze.md +300 -25
- package/Dev/i18n/pt/React/commands/check-architecture.md +369 -34
- package/Dev/i18n/pt/React/commands/check-code-quality.md +440 -34
- package/Dev/i18n/pt/React/commands/check-compliance.md +232 -62
- package/Dev/i18n/pt/React/commands/check-security.md +370 -31
- package/Dev/i18n/pt/React/commands/check-testing.md +473 -43
- package/Dev/i18n/pt/React/commands/generate-component.md +454 -19
- package/Dev/i18n/pt/React/commands/generate-hook.md +425 -24
- package/Dev/i18n/pt/React/commands/storybook-story.md +520 -35
- package/Dev/i18n/pt/ReactNative/commands/app-size.md +358 -387
- package/Dev/i18n/pt/ReactNative/commands/check-code-quality.md +246 -75
- package/Dev/i18n/pt/ReactNative/commands/check-compliance.md +191 -85
- package/Dev/i18n/pt/ReactNative/commands/check-security.md +363 -115
- package/Dev/i18n/pt/ReactNative/commands/check-testing.md +269 -79
- package/Dev/i18n/pt/ReactNative/commands/generate-screen.md +491 -324
- package/Dev/i18n/pt/ReactNative/commands/native-module.md +539 -82
- package/Dev/i18n/pt/ReactNative/commands/store-prepare.md +429 -185
- package/Dev/i18n/pt/ReactNative/rules/02-architecture.md +1084 -31
- package/Dev/i18n/pt/Symfony/commands/check-compliance.md +195 -123
- package/Dev/i18n/pt/UIUX/commands/a11y-audit.md +208 -24
- package/Dev/i18n/pt/UIUX/commands/a11y-component.md +281 -23
- package/Dev/i18n/pt/UIUX/commands/design-tokens.md +197 -33
- package/Dev/i18n/pt/UIUX/commands/user-flow.md +183 -20
- package/Dev/i18n/pt/VueJS/commands/check-compliance.md +228 -103
- package/Dev/i18n/pt/Workflow/commands/analyze.md +147 -146
- package/Dev/i18n/pt/Workflow/commands/design.md +205 -204
- package/Dev/i18n/pt/Workflow/commands/implement.md +184 -184
- package/Project/i18n/de/Sprint/commands/dev.md +339 -86
- package/Project/i18n/es/Sprint/commands/dev.md +342 -91
- package/Project/i18n/pt/Sprint/commands/dev.md +343 -92
- package/bundles/cursor/.cursorrules +1 -1
- package/bundles/windsurf/.windsurfrules +1 -1
- package/package.json +1 -1
|
@@ -4,22 +4,22 @@ description: Gerar Componente React
|
|
|
4
4
|
|
|
5
5
|
# Gerar Componente React
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Gere um novo componente React com TypeScript, testes e boilerplate de estilização.
|
|
8
8
|
|
|
9
9
|
## O Que Este Comando Faz
|
|
10
10
|
|
|
11
|
-
1. **
|
|
11
|
+
1. **Geração de Componente**
|
|
12
12
|
- Criar arquivo de componente
|
|
13
13
|
- Gerar interfaces TypeScript
|
|
14
14
|
- Criar arquivo de teste
|
|
15
15
|
- Criar arquivo de estilo (CSS Module ou styled-components)
|
|
16
|
-
- Criar story Storybook (opcional)
|
|
17
|
-
- Criar barrel
|
|
16
|
+
- Criar story do Storybook (opcional)
|
|
17
|
+
- Criar exportação barrel index.ts
|
|
18
18
|
|
|
19
|
-
2. **Templates
|
|
19
|
+
2. **Templates Utilizados**
|
|
20
20
|
- Componente funcional com TypeScript
|
|
21
|
-
- Interface de
|
|
22
|
-
- Estrutura
|
|
21
|
+
- Interface de Props
|
|
22
|
+
- Estrutura básica de testes
|
|
23
23
|
- Boilerplate de estilo
|
|
24
24
|
|
|
25
25
|
3. **Arquivos Gerados**
|
|
@@ -41,15 +41,17 @@ npm run generate:component ComponentName
|
|
|
41
41
|
# Com caminho personalizado
|
|
42
42
|
npm run generate:component features/users/components/UserCard
|
|
43
43
|
|
|
44
|
-
# Com
|
|
44
|
+
# Com opções
|
|
45
45
|
npm run generate:component ComponentName --story --styled
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
## Modo
|
|
48
|
+
## Modo de Planejamento
|
|
49
49
|
|
|
50
|
-
> **O modo
|
|
50
|
+
> **O modo de planejamento é obrigatório.** Antes de executar, o Claude ativa o modo de planejamento para analisar o código impactado, propor um plano de implementação e aguardar sua validação antes de realizar qualquer alteração.
|
|
51
51
|
|
|
52
|
-
##
|
|
52
|
+
## Templates de Componente
|
|
53
|
+
|
|
54
|
+
### 1. Componente Básico
|
|
53
55
|
|
|
54
56
|
```typescript
|
|
55
57
|
// ComponentName.tsx
|
|
@@ -57,10 +59,20 @@ import { FC } from 'react';
|
|
|
57
59
|
import styles from './ComponentName.module.css';
|
|
58
60
|
|
|
59
61
|
export interface ComponentNameProps {
|
|
62
|
+
/**
|
|
63
|
+
* Descrição da prop
|
|
64
|
+
*/
|
|
60
65
|
children?: React.ReactNode;
|
|
61
66
|
className?: string;
|
|
62
67
|
}
|
|
63
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Descrição do componente ComponentName
|
|
71
|
+
*
|
|
72
|
+
* @component
|
|
73
|
+
* @example
|
|
74
|
+
* <ComponentName>Conteúdo</ComponentName>
|
|
75
|
+
*/
|
|
64
76
|
export const ComponentName: FC<ComponentNameProps> = ({
|
|
65
77
|
children,
|
|
66
78
|
className
|
|
@@ -73,13 +85,436 @@ export const ComponentName: FC<ComponentNameProps> = ({
|
|
|
73
85
|
};
|
|
74
86
|
```
|
|
75
87
|
|
|
76
|
-
|
|
88
|
+
### 2. Com Estado
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
// ComponentName.tsx
|
|
92
|
+
import { FC, useState } from 'react';
|
|
93
|
+
import styles from './ComponentName.module.css';
|
|
94
|
+
|
|
95
|
+
export interface ComponentNameProps {
|
|
96
|
+
initialValue?: string;
|
|
97
|
+
onChange?: (value: string) => void;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export const ComponentName: FC<ComponentNameProps> = ({
|
|
101
|
+
initialValue = '',
|
|
102
|
+
onChange
|
|
103
|
+
}) => {
|
|
104
|
+
const [value, setValue] = useState(initialValue);
|
|
105
|
+
|
|
106
|
+
const handleChange = (newValue: string) => {
|
|
107
|
+
setValue(newValue);
|
|
108
|
+
onChange?.(newValue);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<div className={styles.container}>
|
|
113
|
+
<input
|
|
114
|
+
value={value}
|
|
115
|
+
onChange={(e) => handleChange(e.target.value)}
|
|
116
|
+
/>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
};
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 3. Componente Composto
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
// Card.tsx
|
|
126
|
+
import { FC, ReactNode } from 'react';
|
|
127
|
+
import styles from './Card.module.css';
|
|
128
|
+
|
|
129
|
+
interface CardProps {
|
|
130
|
+
children: ReactNode;
|
|
131
|
+
className?: string;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
interface CardHeaderProps {
|
|
135
|
+
children: ReactNode;
|
|
136
|
+
className?: string;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
interface CardBodyProps {
|
|
140
|
+
children: ReactNode;
|
|
141
|
+
className?: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
interface CardFooterProps {
|
|
145
|
+
children: ReactNode;
|
|
146
|
+
className?: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export const Card: FC<CardProps> & {
|
|
150
|
+
Header: FC<CardHeaderProps>;
|
|
151
|
+
Body: FC<CardBodyProps>;
|
|
152
|
+
Footer: FC<CardFooterProps>;
|
|
153
|
+
} = ({ children, className }) => {
|
|
154
|
+
return (
|
|
155
|
+
<div className={`${styles.card} ${className || ''}`}>
|
|
156
|
+
{children}
|
|
157
|
+
</div>
|
|
158
|
+
);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
Card.Header = ({ children, className }) => {
|
|
162
|
+
return (
|
|
163
|
+
<div className={`${styles.header} ${className || ''}`}>
|
|
164
|
+
{children}
|
|
165
|
+
</div>
|
|
166
|
+
);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
Card.Body = ({ children, className }) => {
|
|
170
|
+
return (
|
|
171
|
+
<div className={`${styles.body} ${className || ''}`}>
|
|
172
|
+
{children}
|
|
173
|
+
</div>
|
|
174
|
+
);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
Card.Footer = ({ children, className }) => {
|
|
178
|
+
return (
|
|
179
|
+
<div className={`${styles.footer} ${className || ''}`}>
|
|
180
|
+
{children}
|
|
181
|
+
</div>
|
|
182
|
+
);
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// Uso
|
|
186
|
+
<Card>
|
|
187
|
+
<Card.Header>Título</Card.Header>
|
|
188
|
+
<Card.Body>Conteúdo</Card.Body>
|
|
189
|
+
<Card.Footer>Ações</Card.Footer>
|
|
190
|
+
</Card>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Template de Teste
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
// ComponentName.test.tsx
|
|
197
|
+
import { render, screen } from '@testing-library/react';
|
|
198
|
+
import { userEvent } from '@testing-library/user-event';
|
|
199
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
200
|
+
import { ComponentName } from './ComponentName';
|
|
201
|
+
|
|
202
|
+
describe('ComponentName', () => {
|
|
203
|
+
it('should render children', () => {
|
|
204
|
+
render(<ComponentName>Test Content</ComponentName>);
|
|
205
|
+
|
|
206
|
+
expect(screen.getByText('Test Content')).toBeInTheDocument();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('should apply custom className', () => {
|
|
210
|
+
const { container } = render(
|
|
211
|
+
<ComponentName className="custom">Content</ComponentName>
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
expect(container.firstChild).toHaveClass('custom');
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('should handle interactions', async () => {
|
|
218
|
+
const handleClick = vi.fn();
|
|
219
|
+
const user = userEvent.setup();
|
|
220
|
+
|
|
221
|
+
render(<ComponentName onClick={handleClick}>Click me</ComponentName>);
|
|
222
|
+
|
|
223
|
+
await user.click(screen.getByText('Click me'));
|
|
224
|
+
|
|
225
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Templates de Estilo
|
|
231
|
+
|
|
232
|
+
### CSS Modules
|
|
233
|
+
|
|
234
|
+
```css
|
|
235
|
+
/* ComponentName.module.css */
|
|
236
|
+
.container {
|
|
237
|
+
display: flex;
|
|
238
|
+
flex-direction: column;
|
|
239
|
+
gap: 1rem;
|
|
240
|
+
padding: 1rem;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.header {
|
|
244
|
+
font-size: 1.5rem;
|
|
245
|
+
font-weight: bold;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.body {
|
|
249
|
+
flex: 1;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.footer {
|
|
253
|
+
display: flex;
|
|
254
|
+
justify-content: flex-end;
|
|
255
|
+
gap: 0.5rem;
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Tailwind CSS
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// ComponentName.tsx
|
|
263
|
+
import { FC } from 'react';
|
|
264
|
+
import { cn } from '@/utils/cn';
|
|
265
|
+
|
|
266
|
+
export interface ComponentNameProps {
|
|
267
|
+
children?: React.ReactNode;
|
|
268
|
+
variant?: 'default' | 'primary' | 'secondary';
|
|
269
|
+
className?: string;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export const ComponentName: FC<ComponentNameProps> = ({
|
|
273
|
+
children,
|
|
274
|
+
variant = 'default',
|
|
275
|
+
className
|
|
276
|
+
}) => {
|
|
277
|
+
return (
|
|
278
|
+
<div
|
|
279
|
+
className={cn(
|
|
280
|
+
'flex flex-col gap-4 p-4 rounded-lg',
|
|
281
|
+
{
|
|
282
|
+
'bg-white': variant === 'default',
|
|
283
|
+
'bg-blue-500 text-white': variant === 'primary',
|
|
284
|
+
'bg-gray-200': variant === 'secondary'
|
|
285
|
+
},
|
|
286
|
+
className
|
|
287
|
+
)}
|
|
288
|
+
>
|
|
289
|
+
{children}
|
|
290
|
+
</div>
|
|
291
|
+
);
|
|
292
|
+
};
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Template Storybook
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
// ComponentName.stories.tsx
|
|
299
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
300
|
+
import { ComponentName } from './ComponentName';
|
|
301
|
+
|
|
302
|
+
const meta: Meta<typeof ComponentName> = {
|
|
303
|
+
title: 'Components/ComponentName',
|
|
304
|
+
component: ComponentName,
|
|
305
|
+
parameters: {
|
|
306
|
+
layout: 'centered'
|
|
307
|
+
},
|
|
308
|
+
tags: ['autodocs'],
|
|
309
|
+
argTypes: {
|
|
310
|
+
variant: {
|
|
311
|
+
control: 'select',
|
|
312
|
+
options: ['default', 'primary', 'secondary']
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
export default meta;
|
|
318
|
+
type Story = StoryObj<typeof ComponentName>;
|
|
319
|
+
|
|
320
|
+
export const Default: Story = {
|
|
321
|
+
args: {
|
|
322
|
+
children: 'Component Content'
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
export const Primary: Story = {
|
|
327
|
+
args: {
|
|
328
|
+
variant: 'primary',
|
|
329
|
+
children: 'Primary Component'
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
export const Secondary: Story = {
|
|
334
|
+
args: {
|
|
335
|
+
variant: 'secondary',
|
|
336
|
+
children: 'Secondary Component'
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Exportação Barrel Index
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
// index.ts
|
|
345
|
+
export { ComponentName } from './ComponentName';
|
|
346
|
+
export type { ComponentNameProps } from './ComponentName';
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Script Gerador
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
// scripts/generate-component.ts
|
|
353
|
+
import fs from 'fs/promises';
|
|
354
|
+
import path from 'path';
|
|
355
|
+
|
|
356
|
+
interface GenerateComponentOptions {
|
|
357
|
+
name: string;
|
|
358
|
+
path?: string;
|
|
359
|
+
withStory?: boolean;
|
|
360
|
+
withStyled?: boolean;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async function generateComponent(options: GenerateComponentOptions) {
|
|
364
|
+
const { name, path: componentPath = 'src/components' } = options;
|
|
365
|
+
const dir = path.join(componentPath, name);
|
|
366
|
+
|
|
367
|
+
// Criar diretório
|
|
368
|
+
await fs.mkdir(dir, { recursive: true });
|
|
369
|
+
|
|
370
|
+
// Gerar arquivo do componente
|
|
371
|
+
await fs.writeFile(
|
|
372
|
+
path.join(dir, `${name}.tsx`),
|
|
373
|
+
getComponentTemplate(name)
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
// Gerar arquivo de teste
|
|
377
|
+
await fs.writeFile(
|
|
378
|
+
path.join(dir, `${name}.test.tsx`),
|
|
379
|
+
getTestTemplate(name)
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
// Gerar arquivo de estilo
|
|
383
|
+
await fs.writeFile(
|
|
384
|
+
path.join(dir, `${name}.module.css`),
|
|
385
|
+
getStyleTemplate()
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
// Gerar arquivo index
|
|
389
|
+
await fs.writeFile(
|
|
390
|
+
path.join(dir, 'index.ts'),
|
|
391
|
+
getIndexTemplate(name)
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
// Gerar story se solicitado
|
|
395
|
+
if (options.withStory) {
|
|
396
|
+
await fs.writeFile(
|
|
397
|
+
path.join(dir, `${name}.stories.tsx`),
|
|
398
|
+
getStoryTemplate(name)
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
console.log(`✅ Component ${name} created at ${dir}`);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Executar
|
|
406
|
+
const [,, name, ...args] = process.argv;
|
|
407
|
+
const options = {
|
|
408
|
+
name,
|
|
409
|
+
withStory: args.includes('--story'),
|
|
410
|
+
withStyled: args.includes('--styled')
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
generateComponent(options);
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
## Exemplos de Uso
|
|
417
|
+
|
|
418
|
+
### Componente Simples
|
|
419
|
+
|
|
420
|
+
```bash
|
|
421
|
+
npm run generate:component Button
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
Gera:
|
|
425
|
+
```
|
|
426
|
+
src/components/Button/
|
|
427
|
+
├── Button.tsx
|
|
428
|
+
├── Button.test.tsx
|
|
429
|
+
├── Button.module.css
|
|
430
|
+
└── index.ts
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Componente de Feature com Story
|
|
434
|
+
|
|
435
|
+
```bash
|
|
436
|
+
npm run generate:component features/users/components/UserCard --story
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
Gera:
|
|
440
|
+
```
|
|
441
|
+
src/features/users/components/UserCard/
|
|
442
|
+
├── UserCard.tsx
|
|
443
|
+
├── UserCard.test.tsx
|
|
444
|
+
├── UserCard.module.css
|
|
445
|
+
├── UserCard.stories.tsx
|
|
446
|
+
└── index.ts
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
## Boas Práticas
|
|
77
450
|
|
|
78
451
|
1. **PascalCase** para nomes de componentes
|
|
79
|
-
2. **
|
|
80
|
-
3.
|
|
81
|
-
4. **
|
|
82
|
-
5. **Evitar exports
|
|
83
|
-
6. **Interface de
|
|
84
|
-
7. **Arquivo de teste**
|
|
85
|
-
8. **
|
|
452
|
+
2. **Colocation** de arquivos relacionados
|
|
453
|
+
3. Interfaces **TypeScript** para props
|
|
454
|
+
4. Comentários **JSDoc** para documentação
|
|
455
|
+
5. **Evitar exports padrão** (usar exports nomeados)
|
|
456
|
+
6. **Interface de Props** exportada separadamente
|
|
457
|
+
7. **Arquivo de teste** ao lado do componente
|
|
458
|
+
8. **Exportações barrel** para imports limpos
|
|
459
|
+
|
|
460
|
+
## Padrões de Componente
|
|
461
|
+
|
|
462
|
+
### Componente Presentacional
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
// Interface pura, sem lógica
|
|
466
|
+
export const UserCard: FC<UserCardProps> = ({ user }) => {
|
|
467
|
+
return (
|
|
468
|
+
<div>
|
|
469
|
+
<h3>{user.name}</h3>
|
|
470
|
+
<p>{user.email}</p>
|
|
471
|
+
</div>
|
|
472
|
+
);
|
|
473
|
+
};
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### Componente Container
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
// Lógica e busca de dados
|
|
480
|
+
export const UserCardContainer: FC<{ userId: string }> = ({ userId }) => {
|
|
481
|
+
const { data: user, isLoading } = useUser(userId);
|
|
482
|
+
|
|
483
|
+
if (isLoading) return <Spinner />;
|
|
484
|
+
if (!user) return <NotFound />;
|
|
485
|
+
|
|
486
|
+
return <UserCard user={user} />;
|
|
487
|
+
};
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
### Higher-Order Component (HOC)
|
|
491
|
+
|
|
492
|
+
```typescript
|
|
493
|
+
// Padrão wrapper
|
|
494
|
+
export const withAuth = <P extends object>(
|
|
495
|
+
Component: ComponentType<P>
|
|
496
|
+
) => {
|
|
497
|
+
return (props: P) => {
|
|
498
|
+
const { isAuthenticated } = useAuth();
|
|
499
|
+
|
|
500
|
+
if (!isAuthenticated) {
|
|
501
|
+
return <Navigate to="/login" />;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
return <Component {...props} />;
|
|
505
|
+
};
|
|
506
|
+
};
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
## Ferramentas
|
|
510
|
+
|
|
511
|
+
- Plop.js para geradores avançados
|
|
512
|
+
- Yeoman para scaffolding
|
|
513
|
+
- Scripts Node.js personalizados
|
|
514
|
+
- Snippets do VS Code
|
|
515
|
+
|
|
516
|
+
## Recursos
|
|
517
|
+
|
|
518
|
+
- [React Component Patterns](https://react.dev/learn/your-first-component)
|
|
519
|
+
- [TypeScript with React](https://react-typescript-cheatsheet.netlify.app/)
|
|
520
|
+
- [Testing Library](https://testing-library.com/docs/react-testing-library/intro/)
|