workflow-agent-cli 1.1.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/dist/chunk-VFN3BY56.js +120 -0
- package/dist/chunk-VFN3BY56.js.map +1 -0
- package/dist/chunk-X2NQJ2ZY.js +170 -0
- package/dist/chunk-X2NQJ2ZY.js.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +1206 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/index.d.ts +8 -0
- package/dist/config/index.js +11 -0
- package/dist/config/index.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/schema-CiJ4W7in.d.ts +97 -0
- package/dist/scripts/postinstall.d.ts +1 -0
- package/dist/scripts/postinstall.js +73 -0
- package/dist/scripts/postinstall.js.map +1 -0
- package/dist/validators/index.d.ts +16 -0
- package/dist/validators/index.js +17 -0
- package/dist/validators/index.js.map +1 -0
- package/package.json +80 -0
- package/templates/AGENT_EDITING_INSTRUCTIONS.md +887 -0
- package/templates/BRANCHING_STRATEGY.md +442 -0
- package/templates/COMPONENT_LIBRARY.md +611 -0
- package/templates/CUSTOM_SCOPE_TEMPLATE.md +228 -0
- package/templates/DEPLOYMENT_STRATEGY.md +509 -0
- package/templates/Guidelines.md +62 -0
- package/templates/LIBRARY_INVENTORY.md +615 -0
- package/templates/PROJECT_TEMPLATE_README.md +347 -0
- package/templates/SCOPE_CREATION_WORKFLOW.md +286 -0
- package/templates/SELF_IMPROVEMENT_MANDATE.md +298 -0
- package/templates/SINGLE_SOURCE_OF_TRUTH.md +492 -0
- package/templates/TESTING_STRATEGY.md +801 -0
- package/templates/_TEMPLATE_EXAMPLE.md +28 -0
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
# Component Library
|
|
2
|
+
|
|
3
|
+
> **Purpose**: This document is the **single source of truth** for all UI components in ProjectHub. Before creating or modifying any UI element, consult this guide.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Quick Reference](#quick-reference)
|
|
10
|
+
2. [Decision Tree](#decision-tree)
|
|
11
|
+
3. [Design Tokens](#design-tokens)
|
|
12
|
+
4. [UI Primitives](#ui-primitives)
|
|
13
|
+
5. [Feature Components](#feature-components)
|
|
14
|
+
6. [Component Audit Checklist](#component-audit-checklist)
|
|
15
|
+
7. [Testing Requirements](#testing-requirements)
|
|
16
|
+
8. [Feature Flags](#feature-flags)
|
|
17
|
+
9. [Storybook](#storybook)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Quick Reference
|
|
22
|
+
|
|
23
|
+
### When to Use What
|
|
24
|
+
|
|
25
|
+
| Need | Component | Storybook Path |
|
|
26
|
+
| ------------------- | -------------------- | ---------------------------------------- |
|
|
27
|
+
| Status indicator | `StatusBadge` | `/story/statusbadge--default` |
|
|
28
|
+
| Priority indicator | `PriorityBadge` | `/story/prioritybadge--default` |
|
|
29
|
+
| Loading spinner | `Spinner` | `/story/ui-spinner--default` |
|
|
30
|
+
| Empty data state | `EmptyState` | `/story/emptystate--default` |
|
|
31
|
+
| Confirmation prompt | `ConfirmationDialog` | `/story/ui-confirmation-dialog--default` |
|
|
32
|
+
| Form in modal | `FormDialog` | `/story/formdialog--default` |
|
|
33
|
+
| Data list | `DataTable` | `/story/datatable--default` |
|
|
34
|
+
| Page title | `PageHeader` | `/story/pageheader--default` |
|
|
35
|
+
| User avatar + name | `MemberCard` | `/story/membercard--default` |
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Decision Tree
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
Need to render UI?
|
|
43
|
+
│
|
|
44
|
+
├─► Does a component exist in components/ui/?
|
|
45
|
+
│ ├─► YES: Use it with existing variants
|
|
46
|
+
│ │ └─► Need different behavior?
|
|
47
|
+
│ │ ├─► Can be achieved with props? → Use props
|
|
48
|
+
│ │ └─► Cannot? → Add variant, update Storybook + tests
|
|
49
|
+
│ │
|
|
50
|
+
│ └─► NO: Check Feature Components below
|
|
51
|
+
│ ├─► Exists? → Use it
|
|
52
|
+
│ └─► Doesn't exist?
|
|
53
|
+
│ │
|
|
54
|
+
│ ├─► Is it a one-off? → Ask: "Will this be used again?"
|
|
55
|
+
│ │ ├─► YES → Create new component with:
|
|
56
|
+
│ │ │ • Feature flag
|
|
57
|
+
│ │ │ • Storybook story
|
|
58
|
+
│ │ │ • Unit tests + snapshots
|
|
59
|
+
│ │ │
|
|
60
|
+
│ │ └─► NO → Implement inline (rare, justify in PR)
|
|
61
|
+
│ │
|
|
62
|
+
│ └─► Create new component (follow Component Creation Workflow)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Design Tokens
|
|
68
|
+
|
|
69
|
+
All design tokens are centralized in `lib/design-tokens.ts`. **Never hardcode colors or sizes.**
|
|
70
|
+
|
|
71
|
+
### Usage
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import {
|
|
75
|
+
statusColors,
|
|
76
|
+
priorityColors,
|
|
77
|
+
getAvatarGradient,
|
|
78
|
+
badgeSizes,
|
|
79
|
+
spinnerSizes
|
|
80
|
+
} from "@/lib/design-tokens";
|
|
81
|
+
|
|
82
|
+
// Status colors
|
|
83
|
+
<Badge className={statusColors["in-progress"]}>In Progress</Badge>
|
|
84
|
+
|
|
85
|
+
// Priority colors
|
|
86
|
+
<Badge className={priorityColors.high}>High</Badge>
|
|
87
|
+
|
|
88
|
+
// Avatar gradient (deterministic by name)
|
|
89
|
+
<Avatar className={getAvatarGradient(user.name)}>
|
|
90
|
+
{initials}
|
|
91
|
+
</Avatar>
|
|
92
|
+
|
|
93
|
+
// Sizes
|
|
94
|
+
<div className={badgeSizes.md}>...</div>
|
|
95
|
+
<div className={spinnerSizes.lg}>...</div>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Available Tokens
|
|
99
|
+
|
|
100
|
+
| Token | Values | Purpose |
|
|
101
|
+
| ------------------------ | ------------------------------- | ------------------------- |
|
|
102
|
+
| `statusColors` | todo, in-progress, review, done | Task status badge colors |
|
|
103
|
+
| `statusLabels` | Human-readable status names | Display labels |
|
|
104
|
+
| `statusIcons` | Lucide icon names | Icons for each status |
|
|
105
|
+
| `priorityColors` | low, medium, high, critical | Priority badge colors |
|
|
106
|
+
| `priorityLabels` | Human-readable priority names | Display labels |
|
|
107
|
+
| `priorityIcons` | Lucide icon names | Icons for each priority |
|
|
108
|
+
| `avatarGradients` | 5 vibrant gradients | User avatar backgrounds |
|
|
109
|
+
| `avatarGradientsNeutral` | 5 neutral gradients | Subtle avatar backgrounds |
|
|
110
|
+
| `badgeSizes` | sm, md, lg | Badge padding/text sizes |
|
|
111
|
+
| `iconSizes` | sm, md, lg | Icon dimensions |
|
|
112
|
+
| `spinnerSizes` | xs, sm, md, lg, xl | Spinner dimensions |
|
|
113
|
+
| `focusRing` | default, inset, none | Focus state styles |
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## UI Primitives
|
|
118
|
+
|
|
119
|
+
Located in `components/ui/`. These are the building blocks.
|
|
120
|
+
|
|
121
|
+
### Layout & Structure
|
|
122
|
+
|
|
123
|
+
| Component | Purpose | Variants | Test File |
|
|
124
|
+
| ----------------- | ---------------------- | ------------------------------------------- | --------- |
|
|
125
|
+
| `card.tsx` | Content container | Header, Title, Description, Content, Footer | - |
|
|
126
|
+
| `dialog.tsx` | Modal dialogs | Default, with close button | - |
|
|
127
|
+
| `sheet.tsx` | Side panels | Positions: top, right, bottom, left | - |
|
|
128
|
+
| `drawer.tsx` | Bottom drawer (mobile) | Default | - |
|
|
129
|
+
| `tabs.tsx` | Tab navigation | Default | - |
|
|
130
|
+
| `separator.tsx` | Visual dividers | Horizontal, vertical | - |
|
|
131
|
+
| `scroll-area.tsx` | Scrollable containers | Default | - |
|
|
132
|
+
| `resizable.tsx` | Resizable panels | Default | - |
|
|
133
|
+
| `collapsible.tsx` | Expandable sections | Default | - |
|
|
134
|
+
| `accordion.tsx` | Multiple collapsibles | Default | - |
|
|
135
|
+
| `sidebar.tsx` | Layout sidebar | Collapsible states | - |
|
|
136
|
+
|
|
137
|
+
### Form Elements
|
|
138
|
+
|
|
139
|
+
| Component | Purpose | Variants | Test File |
|
|
140
|
+
| ------------------ | --------------------- | ----------------------------------------------------------------------------------- | --------- |
|
|
141
|
+
| `button.tsx` | Actions | default, destructive, outline, secondary, ghost, link; Sizes: default, sm, lg, icon | - |
|
|
142
|
+
| `input.tsx` | Text input | Default | - |
|
|
143
|
+
| `textarea.tsx` | Multi-line input | Default | - |
|
|
144
|
+
| `select.tsx` | Dropdown select | Size: sm, default | - |
|
|
145
|
+
| `multi-select.tsx` | Multi-select dropdown | Default | - |
|
|
146
|
+
| `checkbox.tsx` | Checkbox input | Default | - |
|
|
147
|
+
| `switch.tsx` | Toggle switch | Default | - |
|
|
148
|
+
| `radio-group.tsx` | Radio options | Default | - |
|
|
149
|
+
| `slider.tsx` | Range slider | Default | - |
|
|
150
|
+
| `calendar.tsx` | Date picker | Default | - |
|
|
151
|
+
| `input-otp.tsx` | OTP input | Default | - |
|
|
152
|
+
| `form.tsx` | Form primitives | FormField, FormItem, FormLabel, FormControl, FormMessage | - |
|
|
153
|
+
| `label.tsx` | Form labels | Default | - |
|
|
154
|
+
|
|
155
|
+
### Feedback & Display
|
|
156
|
+
|
|
157
|
+
| Component | Purpose | Variants | Feature Flag | Test File |
|
|
158
|
+
| ------------------------- | -------------------- | ---------------------------------------- | --------------------- | ------------------------------ |
|
|
159
|
+
| `badge.tsx` | Status labels | default, secondary, destructive, outline | - | - |
|
|
160
|
+
| `alert.tsx` | Alert messages | default, destructive | - | - |
|
|
161
|
+
| `alert-dialog.tsx` | Confirmation modals | Default | - | - |
|
|
162
|
+
| `skeleton.tsx` | Loading placeholders | Default | - | - |
|
|
163
|
+
| `progress.tsx` | Progress bar | Default | - | - |
|
|
164
|
+
| `spinner.tsx` | Loading spinner | xs, sm, md, lg, xl | `SPINNER` | `spinner.test.tsx` |
|
|
165
|
+
| `confirmation-dialog.tsx` | Standardized confirm | default, destructive | `CONFIRMATION_DIALOG` | `confirmation-dialog.test.tsx` |
|
|
166
|
+
| `sonner.tsx` | Toast notifications | Default | - | - |
|
|
167
|
+
| `tooltip.tsx` | Tooltips | Default | - | - |
|
|
168
|
+
| `hover-card.tsx` | Hover tooltips | Default | - | - |
|
|
169
|
+
| `popover.tsx` | Popovers | Default | - | - |
|
|
170
|
+
|
|
171
|
+
### Navigation
|
|
172
|
+
|
|
173
|
+
| Component | Purpose | Variants | Test File |
|
|
174
|
+
| --------------------- | ----------------- | -------- | --------- |
|
|
175
|
+
| `dropdown-menu.tsx` | Dropdown menus | Default | - |
|
|
176
|
+
| `context-menu.tsx` | Right-click menus | Default | - |
|
|
177
|
+
| `menubar.tsx` | Menu bar | Default | - |
|
|
178
|
+
| `navigation-menu.tsx` | Navigation | Default | - |
|
|
179
|
+
| `breadcrumb.tsx` | Breadcrumbs | Default | - |
|
|
180
|
+
| `command.tsx` | Command palette | Default | - |
|
|
181
|
+
| `pagination.tsx` | Page navigation | Default | - |
|
|
182
|
+
|
|
183
|
+
### Data Display
|
|
184
|
+
|
|
185
|
+
| Component | Purpose | Variants | Test File |
|
|
186
|
+
| ------------------ | ---------------------- | ------------------------------- | --------- |
|
|
187
|
+
| `table.tsx` | Data tables | Header, Body, Row, Cell, Footer | - |
|
|
188
|
+
| `avatar.tsx` | User avatars | Image + fallback | - |
|
|
189
|
+
| `chart.tsx` | Data visualization | Recharts wrapper | - |
|
|
190
|
+
| `carousel.tsx` | Content carousel | Default | - |
|
|
191
|
+
| `aspect-ratio.tsx` | Aspect ratio container | Default | - |
|
|
192
|
+
|
|
193
|
+
### Toggles & Groups
|
|
194
|
+
|
|
195
|
+
| Component | Purpose | Variants | Test File |
|
|
196
|
+
| ------------------ | ------------------- | -------- | --------- |
|
|
197
|
+
| `toggle.tsx` | Toggle button | Default | - |
|
|
198
|
+
| `toggle-group.tsx` | Toggle button group | Default | - |
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Feature Components
|
|
203
|
+
|
|
204
|
+
Located in `components/`. These are composed from UI primitives.
|
|
205
|
+
|
|
206
|
+
### Badges & Indicators
|
|
207
|
+
|
|
208
|
+
| Component | Purpose | Feature Flag | Storybook | Test File |
|
|
209
|
+
| ------------------- | ------------------ | -------------- | ------------------------------- | ---------------------- |
|
|
210
|
+
| `PriorityBadge.tsx` | Priority indicator | - | `/story/prioritybadge--default` | - |
|
|
211
|
+
| `StatusBadge.tsx` | Status indicator | `STATUS_BADGE` | `/story/statusbadge--default` | `StatusBadge.test.tsx` |
|
|
212
|
+
|
|
213
|
+
### Layout Components
|
|
214
|
+
|
|
215
|
+
| Component | Purpose | Feature Flag | Storybook | Test File |
|
|
216
|
+
| ---------------- | -------------------- | ------------- | ---------------------------- | --------------------- |
|
|
217
|
+
| `EmptyState.tsx` | Empty data display | `EMPTY_STATE` | `/story/emptystate--default` | `EmptyState.test.tsx` |
|
|
218
|
+
| `PageHeader.tsx` | Page title + actions | `PAGE_HEADER` | `/story/pageheader--default` | `PageHeader.test.tsx` |
|
|
219
|
+
| `MemberCard.tsx` | User avatar + info | `MEMBER_CARD` | `/story/membercard--default` | `MemberCard.test.tsx` |
|
|
220
|
+
|
|
221
|
+
### Data Components
|
|
222
|
+
|
|
223
|
+
| Component | Purpose | Feature Flag | Storybook | Test File |
|
|
224
|
+
| --------------- | --------------------------------- | ------------ | --------------------------- | -------------------- |
|
|
225
|
+
| `DataTable.tsx` | Table with sort/filter/pagination | `DATA_TABLE` | `/story/datatable--default` | `DataTable.test.tsx` |
|
|
226
|
+
|
|
227
|
+
### Form Components
|
|
228
|
+
|
|
229
|
+
| Component | Purpose | Feature Flag | Storybook | Test File |
|
|
230
|
+
| ------------------------ | --------------------------- | --------------------- | ------------------------------------ | ----------------------------- |
|
|
231
|
+
| `FormDialog.tsx` | Dialog with form | `FORM_DIALOG` | `/story/formdialog--default` | `FormDialog.test.tsx` |
|
|
232
|
+
| `ConfirmationDialog.tsx` | Confirm destructive actions | `CONFIRMATION_DIALOG` | `/story/confirmationdialog--default` | `ConfirmationDialog.test.tsx` |
|
|
233
|
+
| `Spinner.tsx` | Loading indicator | `SPINNER` | `/story/spinner--default` | `Spinner.test.tsx` |
|
|
234
|
+
|
|
235
|
+
### Existing Feature Components (No Flag)
|
|
236
|
+
|
|
237
|
+
| Component | Purpose | Location |
|
|
238
|
+
| ----------------------- | ----------------------- | ------------- |
|
|
239
|
+
| `TaskCard.tsx` | Kanban task card | `components/` |
|
|
240
|
+
| `TaskDialog.tsx` | Task create/edit dialog | `components/` |
|
|
241
|
+
| `SprintDialog.tsx` | Sprint create/edit | `components/` |
|
|
242
|
+
| `BoardDialog.tsx` | Board create/edit | `components/` |
|
|
243
|
+
| `ColumnDialog.tsx` | Column create/edit | `components/` |
|
|
244
|
+
| `WorkspaceDialog.tsx` | Workspace create/edit | `components/` |
|
|
245
|
+
| `Header.tsx` | App header | `components/` |
|
|
246
|
+
| `Sidebar.tsx` | App sidebar | `components/` |
|
|
247
|
+
| `NotificationPanel.tsx` | Notifications list | `components/` |
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Dialog Patterns
|
|
252
|
+
|
|
253
|
+
All form dialogs should follow consistent patterns for user experience.
|
|
254
|
+
|
|
255
|
+
### When to Use Each Dialog Component
|
|
256
|
+
|
|
257
|
+
| Use Case | Component | Example |
|
|
258
|
+
| --------------------------------- | ------------------------------- | --------------------------- |
|
|
259
|
+
| Form with multiple fields | `FormDialog` | Create Sprint, Create Board |
|
|
260
|
+
| Destructive confirmation | `ConfirmationDialog` | Delete Task, Remove Member |
|
|
261
|
+
| Simple confirmation | `ConfirmationDialog` | Confirm Action |
|
|
262
|
+
| Complex form (many tabs/sections) | Custom with `Dialog` primitives | TaskDialog |
|
|
263
|
+
|
|
264
|
+
### FormDialog Usage
|
|
265
|
+
|
|
266
|
+
```tsx
|
|
267
|
+
import {
|
|
268
|
+
FormDialog,
|
|
269
|
+
FormDialogSection,
|
|
270
|
+
FormDialogField,
|
|
271
|
+
useFormDialog,
|
|
272
|
+
} from '@/components/FormDialog';
|
|
273
|
+
|
|
274
|
+
function CreateSprintDialog() {
|
|
275
|
+
const { isOpen, isSubmitting, setIsSubmitting, open, close } = useFormDialog();
|
|
276
|
+
const [name, setName] = useState('');
|
|
277
|
+
|
|
278
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
279
|
+
e.preventDefault();
|
|
280
|
+
setIsSubmitting(true);
|
|
281
|
+
try {
|
|
282
|
+
await createSprint({ name });
|
|
283
|
+
close();
|
|
284
|
+
} finally {
|
|
285
|
+
setIsSubmitting(false);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
return (
|
|
290
|
+
<>
|
|
291
|
+
<Button onClick={open}>Create Sprint</Button>
|
|
292
|
+
<FormDialog
|
|
293
|
+
open={isOpen}
|
|
294
|
+
onOpenChange={(open) => !open && close()}
|
|
295
|
+
title="Create Sprint"
|
|
296
|
+
description="Add a new sprint to your project"
|
|
297
|
+
onSubmit={handleSubmit}
|
|
298
|
+
isSubmitting={isSubmitting}
|
|
299
|
+
submitText="Create"
|
|
300
|
+
gradientTitle // Use gradient for creation dialogs
|
|
301
|
+
size="md"
|
|
302
|
+
>
|
|
303
|
+
<FormDialogField label="Name" required htmlFor="sprint-name">
|
|
304
|
+
<Input
|
|
305
|
+
id="sprint-name"
|
|
306
|
+
value={name}
|
|
307
|
+
onChange={(e) => setName(e.target.value)}
|
|
308
|
+
placeholder="Sprint 1"
|
|
309
|
+
/>
|
|
310
|
+
</FormDialogField>
|
|
311
|
+
</FormDialog>
|
|
312
|
+
</>
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Dialog Size Guidelines
|
|
318
|
+
|
|
319
|
+
| Size | Max Width | Use For |
|
|
320
|
+
| ------ | --------- | --------------------------------- |
|
|
321
|
+
| `sm` | 400px | Simple confirmation, single field |
|
|
322
|
+
| `md` | 500px | Standard forms (2-5 fields) |
|
|
323
|
+
| `lg` | 600px | Larger forms with sections |
|
|
324
|
+
| `xl` | 800px | Complex forms, data tables |
|
|
325
|
+
| `full` | 90vw | Very large content, multi-step |
|
|
326
|
+
|
|
327
|
+
### Title Styling
|
|
328
|
+
|
|
329
|
+
| Style | Use For | Prop |
|
|
330
|
+
| -------- | ------------------------------------------- | --------------------------------- |
|
|
331
|
+
| Gradient | Creation dialogs (Create Sprint, New Board) | `gradientTitle={true}` |
|
|
332
|
+
| Plain | Edit dialogs, Settings | `gradientTitle={false}` (default) |
|
|
333
|
+
|
|
334
|
+
### FormDialogSection
|
|
335
|
+
|
|
336
|
+
Use sections to group related fields:
|
|
337
|
+
|
|
338
|
+
```tsx
|
|
339
|
+
<FormDialog {...props}>
|
|
340
|
+
<FormDialogSection title="Basic Info">
|
|
341
|
+
<FormDialogField label="Name" required>
|
|
342
|
+
<Input {...nameProps} />
|
|
343
|
+
</FormDialogField>
|
|
344
|
+
<FormDialogField label="Description">
|
|
345
|
+
<Textarea {...descProps} />
|
|
346
|
+
</FormDialogField>
|
|
347
|
+
</FormDialogSection>
|
|
348
|
+
|
|
349
|
+
<FormDialogSection title="Options">
|
|
350
|
+
<FormDialogField label="Active">
|
|
351
|
+
<Switch {...activeProps} />
|
|
352
|
+
</FormDialogField>
|
|
353
|
+
</FormDialogSection>
|
|
354
|
+
</FormDialog>
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Error Handling
|
|
358
|
+
|
|
359
|
+
Use `FormDialogField` error prop for validation:
|
|
360
|
+
|
|
361
|
+
```tsx
|
|
362
|
+
<FormDialogField label="Email" required error={errors.email?.message} htmlFor="email">
|
|
363
|
+
<Input id="email" {...register('email')} />
|
|
364
|
+
</FormDialogField>
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## Component Audit Checklist
|
|
370
|
+
|
|
371
|
+
Before creating or modifying a component, complete this checklist:
|
|
372
|
+
|
|
373
|
+
### Pre-Work Audit
|
|
374
|
+
|
|
375
|
+
- [ ] Checked `components/ui/` for existing primitive
|
|
376
|
+
- [ ] Checked Feature Components list above
|
|
377
|
+
- [ ] Checked Storybook for visual examples
|
|
378
|
+
- [ ] Verified no existing component meets the need
|
|
379
|
+
- [ ] If modifying: reviewed current tests and stories
|
|
380
|
+
|
|
381
|
+
### New Component Requirements
|
|
382
|
+
|
|
383
|
+
- [ ] Component file created: `components/[Name].tsx` or `components/ui/[name].tsx`
|
|
384
|
+
- [ ] Uses design tokens from `lib/design-tokens.ts`
|
|
385
|
+
- [ ] Has clear prop interface with JSDoc
|
|
386
|
+
- [ ] Has feature flag (if applicable)
|
|
387
|
+
- [ ] Storybook story created: `[Name].stories.tsx`
|
|
388
|
+
- [ ] Unit tests created: `[Name].test.tsx`
|
|
389
|
+
- [ ] Snapshot tests for each variant
|
|
390
|
+
- [ ] Added to this document (COMPONENT_LIBRARY.md)
|
|
391
|
+
|
|
392
|
+
### Variant Addition Requirements
|
|
393
|
+
|
|
394
|
+
- [ ] Existing tests still pass
|
|
395
|
+
- [ ] New variant has Storybook story
|
|
396
|
+
- [ ] New variant has snapshot test
|
|
397
|
+
- [ ] This document updated if needed
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## Testing Requirements
|
|
402
|
+
|
|
403
|
+
Every component in the library must have comprehensive tests.
|
|
404
|
+
|
|
405
|
+
### Test File Structure
|
|
406
|
+
|
|
407
|
+
```
|
|
408
|
+
components/
|
|
409
|
+
├── StatusBadge.tsx
|
|
410
|
+
├── StatusBadge.stories.tsx
|
|
411
|
+
├── StatusBadge.test.tsx
|
|
412
|
+
└── __snapshots__/
|
|
413
|
+
└── StatusBadge.test.tsx.snap
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### Required Test Coverage
|
|
417
|
+
|
|
418
|
+
#### Behavioral Tests
|
|
419
|
+
|
|
420
|
+
```typescript
|
|
421
|
+
describe('StatusBadge', () => {
|
|
422
|
+
it('should render without crashing', () => {});
|
|
423
|
+
it('should render all status variants', () => {});
|
|
424
|
+
it('should apply correct colors for each status', () => {});
|
|
425
|
+
it('should respect size prop', () => {});
|
|
426
|
+
it('should apply custom className', () => {});
|
|
427
|
+
it('should handle feature flag disabled state', () => {});
|
|
428
|
+
it('should handle feature flag enabled state', () => {});
|
|
429
|
+
});
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
#### Snapshot Tests
|
|
433
|
+
|
|
434
|
+
```typescript
|
|
435
|
+
describe("Snapshots", () => {
|
|
436
|
+
it.each(["todo", "in-progress", "review", "done"])
|
|
437
|
+
("should match snapshot for status: %s", (status) => {
|
|
438
|
+
const { container } = render(<StatusBadge status={status} />);
|
|
439
|
+
expect(container).toMatchSnapshot();
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it.each(["sm", "md", "lg"])
|
|
443
|
+
("should match snapshot for size: %s", (size) => {
|
|
444
|
+
const { container } = render(<StatusBadge status="todo" size={size} />);
|
|
445
|
+
expect(container).toMatchSnapshot();
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
### Running Tests
|
|
451
|
+
|
|
452
|
+
```bash
|
|
453
|
+
# Run all component tests
|
|
454
|
+
pnpm test:components
|
|
455
|
+
|
|
456
|
+
# Watch mode
|
|
457
|
+
pnpm test:components:watch
|
|
458
|
+
|
|
459
|
+
# Update snapshots (after intentional visual changes)
|
|
460
|
+
pnpm test:update-snapshots
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## Feature Flags
|
|
466
|
+
|
|
467
|
+
Components under development use feature flags for controlled rollout.
|
|
468
|
+
|
|
469
|
+
### Available Flags
|
|
470
|
+
|
|
471
|
+
| Flag | Component | Default |
|
|
472
|
+
| --------------------- | ---------------------- | ------- |
|
|
473
|
+
| `STATUS_BADGE` | StatusBadge | `false` |
|
|
474
|
+
| `SPINNER` | Spinner | `false` |
|
|
475
|
+
| `CONFIRMATION_DIALOG` | ConfirmationDialog | `false` |
|
|
476
|
+
| `EMPTY_STATE` | EmptyState | `false` |
|
|
477
|
+
| `FORM_DIALOG` | FormDialog | `false` |
|
|
478
|
+
| `DATA_TABLE` | DataTable | `false` |
|
|
479
|
+
| `PAGE_HEADER` | PageHeader | `false` |
|
|
480
|
+
| `MEMBER_CARD` | MemberCard | `false` |
|
|
481
|
+
| `DESIGN_TOKENS` | Design token migration | `false` |
|
|
482
|
+
|
|
483
|
+
### Enabling Flags
|
|
484
|
+
|
|
485
|
+
Add to `.env.local`:
|
|
486
|
+
|
|
487
|
+
```bash
|
|
488
|
+
NEXT_PUBLIC_FEATURE_STATUS_BADGE=true
|
|
489
|
+
NEXT_PUBLIC_FEATURE_SPINNER=true
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### Using Flags in Code
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
import { isFeatureEnabled, FeatureFlag } from "@/lib/feature-flags";
|
|
496
|
+
|
|
497
|
+
function TaskRow({ task }) {
|
|
498
|
+
return (
|
|
499
|
+
<div>
|
|
500
|
+
{isFeatureEnabled(FeatureFlag.STATUS_BADGE) ? (
|
|
501
|
+
<StatusBadge status={task.status} />
|
|
502
|
+
) : (
|
|
503
|
+
<LegacyStatusDisplay status={task.status} />
|
|
504
|
+
)}
|
|
505
|
+
</div>
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### Testing with Flags
|
|
511
|
+
|
|
512
|
+
```typescript
|
|
513
|
+
import { mockFeatureFlag, clearFeatureFlagMocks } from '@/lib/feature-flags';
|
|
514
|
+
|
|
515
|
+
describe('Component with feature flag', () => {
|
|
516
|
+
afterEach(() => {
|
|
517
|
+
clearFeatureFlagMocks();
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
it('should render new component when flag enabled', () => {
|
|
521
|
+
mockFeatureFlag(FeatureFlag.STATUS_BADGE, true);
|
|
522
|
+
// test new behavior
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
it('should render fallback when flag disabled', () => {
|
|
526
|
+
mockFeatureFlag(FeatureFlag.STATUS_BADGE, false);
|
|
527
|
+
// test fallback behavior
|
|
528
|
+
});
|
|
529
|
+
});
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
## Storybook
|
|
535
|
+
|
|
536
|
+
Local Storybook for visual documentation and testing.
|
|
537
|
+
|
|
538
|
+
### Running Storybook
|
|
539
|
+
|
|
540
|
+
```bash
|
|
541
|
+
pnpm storybook
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
Opens at http://localhost:6006
|
|
545
|
+
|
|
546
|
+
### Story Structure
|
|
547
|
+
|
|
548
|
+
```typescript
|
|
549
|
+
// StatusBadge.stories.tsx
|
|
550
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
551
|
+
import { StatusBadge } from "./StatusBadge";
|
|
552
|
+
|
|
553
|
+
const meta: Meta<typeof StatusBadge> = {
|
|
554
|
+
title: "Components/StatusBadge",
|
|
555
|
+
component: StatusBadge,
|
|
556
|
+
tags: ["autodocs"],
|
|
557
|
+
argTypes: {
|
|
558
|
+
status: {
|
|
559
|
+
control: "select",
|
|
560
|
+
options: ["todo", "in-progress", "review", "done"],
|
|
561
|
+
},
|
|
562
|
+
size: {
|
|
563
|
+
control: "select",
|
|
564
|
+
options: ["sm", "md", "lg"],
|
|
565
|
+
},
|
|
566
|
+
},
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
export default meta;
|
|
570
|
+
type Story = StoryObj<typeof StatusBadge>;
|
|
571
|
+
|
|
572
|
+
export const Default: Story = {
|
|
573
|
+
args: {
|
|
574
|
+
status: "todo",
|
|
575
|
+
size: "md",
|
|
576
|
+
},
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
// Additional stories for specific states
|
|
580
|
+
export const InProgress: Story = {
|
|
581
|
+
args: {
|
|
582
|
+
status: "in-progress",
|
|
583
|
+
},
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
export const AllSizes: Story = {
|
|
587
|
+
render: () => (
|
|
588
|
+
<div className="flex gap-4">
|
|
589
|
+
<StatusBadge status="todo" size="sm" />
|
|
590
|
+
<StatusBadge status="todo" size="md" />
|
|
591
|
+
<StatusBadge status="todo" size="lg" />
|
|
592
|
+
</div>
|
|
593
|
+
),
|
|
594
|
+
};
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
### Story Guidelines
|
|
598
|
+
|
|
599
|
+
1. **Keep stories minimal** - rely on Controls addon for testing variants
|
|
600
|
+
2. **One default story** per component with all props controllable
|
|
601
|
+
3. **Additional stories** only for complex compositions or states
|
|
602
|
+
4. **Use `tags: ["autodocs"]`** for auto-generated documentation
|
|
603
|
+
5. **Provide meaningful argTypes** with control types
|
|
604
|
+
|
|
605
|
+
---
|
|
606
|
+
|
|
607
|
+
## Changelog
|
|
608
|
+
|
|
609
|
+
| Date | Change |
|
|
610
|
+
| ---------- | ----------------------------------------------- |
|
|
611
|
+
| 2026-01-10 | Initial component library documentation created |
|