Run 2: Foundation Components
Run 2 implements Wave 1 of the image upload component set — five leaf-level components that have no dependencies on other new components from this project. All work is committed to the shared feature branch in a single worktree, and the five tasks are independent of each other within this run.
Repository: ux-prototype (Arda-cards/ux-prototype)
Base branch: jmpicnic/item-image-upload-components (branched from main)
Parallelism: Tasks 2.1 through 2.5 may run concurrently across 2-3 agents,
each in its own worktree and task branch, merging back to the base branch at
the end of the wave.
Entry Criteria
Section titled “Entry Criteria”Before any work in this run begins, both of the following must be satisfied:
- Run 1 exit gate passed — execute
bash run-1-infrastructure/validate-exit.shand the script must exit 0. - Base branch up-to-date —
jmpicnic/item-image-upload-componentscontains all Run 1 commits.
Artifact Specifications
Section titled “Artifact Specifications”Each task below produces five files. The naming pattern is consistent across all
components: <ComponentName>.tsx, <ComponentName>.test.tsx,
<ComponentName>.stories.tsx, <ComponentName>.mdx, and index.ts.
Task 2.1 — CopyrightAcknowledgment (atom)
Section titled “Task 2.1 — CopyrightAcknowledgment (atom)”Location: src/components/canary/atoms/copyright-acknowledgment/
| Artifact | Description |
|---|---|
CopyrightAcknowledgment.tsx | Checkbox + label that surfaces the copyright agreement required before image submission. |
CopyrightAcknowledgment.test.tsx | 7 unit tests covering checked/unchecked state, disabled state, label text, onChange propagation, and accessibility attributes. |
CopyrightAcknowledgment.stories.tsx | 5 stories: Unchecked (default), Checked, Disabled, WithLongLabel, Controlled. |
CopyrightAcknowledgment.mdx | Prose overview, Canvas embeds for each story, props table. |
index.ts | Barrel export of CopyrightAcknowledgment and its props type. |
Dependencies (from Run 1): ShadCN checkbox primitive.
Acceptance criteria: All 7 unit tests pass; all 5 stories render without console errors in Storybook.
Task 2.2 — ImageDisplay (molecule)
Section titled “Task 2.2 — ImageDisplay (molecule)”Location: src/components/canary/molecules/image-display/
| Artifact | Description |
|---|---|
ImageDisplay.tsx | Renders an item image in four visual states: loaded, loading (skeleton), error (Badge overlay), and no-image (placeholder). |
ImageDisplay.test.tsx | 9 unit tests covering each of the four states, alt text, error message text, skeleton presence, and aspect-ratio container dimensions. |
ImageDisplay.stories.tsx | 8 stories: Loaded, Loading, Error, NoImage, SmallSize, LargeSize, WithCaption, InteractiveSizeVariants. |
ImageDisplay.mdx | Prose overview, Canvas embeds, props table including state enum values. |
index.ts | Barrel export of ImageDisplay and its props type. |
Dependencies (from Run 1): ShadCN skeleton primitive; Badge component
with the error-overlay variant; getInitials utility from
src/types/canary/utilities/.
Acceptance criteria: All 9 unit tests pass; all 4 display states (loaded, loading, error, no-image) render correctly in Storybook stories.
Task 2.3 — ImageDropZone (molecule)
Section titled “Task 2.3 — ImageDropZone (molecule)”Location: src/components/canary/molecules/image-drop-zone/
| Artifact | Description |
|---|---|
ImageDropZone.tsx | Accepts image files via drag-and-drop, URL text input, and clipboard paste. Validates MIME type and file size against ImageFieldConfig. |
ImageDropZone.test.tsx | 10 unit tests covering drag-enter/drag-leave visual state, file-drop acceptance, URL input submission, paste event handling, rejection of invalid MIME types, and rejection of oversized files. |
ImageDropZone.stories.tsx | 6 stories: Default, DragActive, WithUrlInput, WithPasteHint, Disabled, CompactVariant. |
ImageDropZone.mdx | Prose overview, Canvas embeds, props table including ImageFieldConfig shape. |
index.ts | Barrel export of ImageDropZone and its props type. |
Dependencies (from Run 1): react-dropzone library (installed in Run 1);
ImageFieldConfig type from src/types/canary/utilities/.
Acceptance criteria: All 10 unit tests pass; drag-drop, URL, and paste input paths are exercised and visually correct in stories.
Task 2.4 — ImagePreviewEditor (molecule)
Section titled “Task 2.4 — ImagePreviewEditor (molecule)”Location: src/components/canary/molecules/image-preview-editor/
| Artifact | Description |
|---|---|
ImagePreviewEditor.tsx | Inline crop/zoom/rotate editor wrapping react-easy-crop. Exposes crop area, zoom level, and rotation as controlled props. |
ImagePreviewEditor.test.tsx | 6 unit tests covering initial render, zoom slider value propagation, rotation button increments, crop area change callback, aspect-ratio locking, and reset-to-defaults behavior. |
ImagePreviewEditor.stories.tsx | 7 stories: Default, Zoomed, Rotated, SquareCrop, FreeformCrop, Disabled, WithResetButton. |
ImagePreviewEditor.mdx | Prose overview, Canvas embeds, props table including crop-area geometry type. |
index.ts | Barrel export of ImagePreviewEditor and its props type. |
Dependencies (from Run 1): react-easy-crop library (installed in Run 1);
ShadCN slider primitive.
Acceptance criteria: All 6 unit tests pass; crop, zoom, and rotate controls respond correctly in Storybook stories.
Task 2.5 — Avatar (modification)
Section titled “Task 2.5 — Avatar (modification)”Location: src/components/canary/atoms/avatar/ (existing component)
| Artifact | Description |
|---|---|
Avatar.tsx (modified) | Import getInitials from src/types/canary/utilities/ instead of the inline implementation; keep external API identical. |
Avatar.test.tsx (augmented) | 4 new unit tests added: getInitials delegation verified, initials derived correctly for single-word names, initials capped at two characters, and existing snapshot test still passes. |
Avatar.stories.tsx (augmented) | 5 new stories added: SingleWordName, LongNameTwoInitials, NumericFallback, WithImageAndInitialsFallback, SizesShowcase. |
Avatar.mdx (updated) | Add section documenting the getInitials integration and updated props table if any prop signatures changed. |
index.ts | Unchanged — re-export is already present. |
Dependencies (from Run 1): getInitials utility from
src/types/canary/utilities/.
Acceptance criteria: The 4 new unit tests pass; all pre-existing Avatar tests remain green (no regressions); the 5 new stories render without errors.
Task List
Section titled “Task List”| # | Task | Location | Unit Tests | Stories | Depends On |
|---|---|---|---|---|---|
| 2.1 | CopyrightAcknowledgment atom | atoms/copyright-acknowledgment/ | 7 | 5 | Run 1: checkbox primitive |
| 2.2 | ImageDisplay molecule | molecules/image-display/ | 9 | 8 | Run 1: skeleton, Badge error-overlay, getInitials |
| 2.3 | ImageDropZone molecule | molecules/image-drop-zone/ | 10 | 6 | Run 1: react-dropzone, ImageFieldConfig |
| 2.4 | ImagePreviewEditor molecule | molecules/image-preview-editor/ | 6 | 7 | Run 1: react-easy-crop, slider primitive |
| 2.5 | Avatar modification | atoms/avatar/ | 4 new | 5 new | Run 1: getInitials |
| Total | 36 | 31 |
Internal Dependency Graph
Section titled “Internal Dependency Graph”All five tasks in Run 2 are fully independent of each other. They share only the Run 1 outputs and may be executed concurrently.
Run 1 outputs (checkbox, skeleton, Badge error-overlay, getInitials, react-dropzone, ImageFieldConfig, react-easy-crop, slider) | +---> Task 2.1 CopyrightAcknowledgment | +---> Task 2.2 ImageDisplay | +---> Task 2.3 ImageDropZone | +---> Task 2.4 ImagePreviewEditor | +---> Task 2.5 Avatar (modification)Recommended agent grouping when running with parallel agents:
- Agent A: Tasks 2.1 and 2.5 (smallest by test count; natural pairing of atom-level work).
- Agent B: Task 2.2 (ImageDisplay — 9 tests, 8 stories; medium complexity).
- Agent C: Tasks 2.3 and 2.4 (both depend on external library integration; natural pairing).
Exit Criteria
Section titled “Exit Criteria”All of the following must be satisfied before handing off to Run 3:
npm run lintexits 0 in the worktree (zero errors, zero warnings).npm run build-storybookexits 0 (Storybook bundle produced without errors).npm run testexits 0 (all Vitest suites pass).- 36 new unit tests pass: 7 (2.1) + 9 (2.2) + 10 (2.3) + 6 (2.4) + 4 (2.5).
- 31 new or updated stories render: 5 (2.1) + 8 (2.2) + 6 (2.3) + 7 (2.4)
- 5 (2.5).
- All 5 components export correctly from their respective
index.tsfiles:CopyrightAcknowledgment,ImageDisplay,ImageDropZone,ImagePreviewEditor, and the updatedAvatar. - No pre-existing test regressions — the full test suite count for Avatar increases by exactly 4; no previously passing tests are red.
A validate-exit.sh script in this directory encodes checks 1-3 as an
automated gate. Checks 4-7 are verified by inspecting test output.
Handoff
Section titled “Handoff”Consumed (from Run 1)
Section titled “Consumed (from Run 1)”| Artifact | Produced By | Consumed By Task |
|---|---|---|
ShadCN checkbox primitive | Run 1 | 2.1 CopyrightAcknowledgment |
ShadCN skeleton primitive | Run 1 | 2.2 ImageDisplay |
Badge component with error-overlay variant | Run 1 | 2.2 ImageDisplay |
getInitials utility (src/types/canary/utilities/) | Run 1 | 2.2 ImageDisplay, 2.5 Avatar |
react-dropzone npm package | Run 1 | 2.3 ImageDropZone |
ImageFieldConfig type (src/types/canary/utilities/) | Run 1 | 2.3 ImageDropZone |
react-easy-crop npm package | Run 1 | 2.4 ImagePreviewEditor |
ShadCN slider primitive | Run 1 | 2.4 ImagePreviewEditor |
Produced (for downstream runs)
Section titled “Produced (for downstream runs)”| Artifact | Consumed By |
|---|---|
ImageDisplay molecule | Run 3 (ImageHoverPreview, ImageComparisonLayout, ImageFormField), Run 4 (ImageCellDisplay) |
ImageDropZone molecule | Run 4 (ImageUploadDialog) |
ImagePreviewEditor molecule | Run 4 (ImageUploadDialog via ImageComparisonLayout) |
CopyrightAcknowledgment atom | Run 4 (ImageUploadDialog) |
Avatar modification | No downstream consumers in this project — modification is a housekeeping improvement only. |
Copyright: (c) Arda Systems 2025-2026, All rights reserved
Copyright: © Arda Systems 2025-2026, All rights reserved