Skip to content

Use Case Stories Specification

Implementation specification for the Storybook use case stories defined in the Overview. Each in-scope scenario produces stories that demonstrate the user experience to business stakeholders and subject matter experts using high-fidelity mock-ups composed from the project’s UI Components.

The following must be verified before implementation begins.

  1. Component implementation complete. All 19 components defined in the UI Components document are implemented in PR #63 on branch jmpicnic/item-image-upload-components (4 runs: infrastructure, foundation, composition, grid/organism). Verification: the component Storybook builds without errors and all component story files render. Use case story work branches from jmpicnic/item-image-upload-components directly — no need to wait for the PR to merge to main.
  2. Existing checks green. Lint, Storybook build, and all unit tests pass on the working branch.
  3. Mock data module available. The shared mock data module (src/components/canary/__mocks__/image-story-data.ts) is in place with MOCK_ITEMS, ITEM_IMAGE_CONFIG, mockUpload, and mockReachabilityCheck. Additional mock objects required for use case stories (see Mock Strategy) must be added as the first task in Wave 1.

Stories in this project must follow a strict sourcing hierarchy for UI components:

  1. Canary components (@/components/canary/) — preferred. Use for all components that have a canary implementation.
  2. Vendored components (@frontend/) — fallback. Use only when no canary equivalent exists.
  3. extras components (@/components/extras/) — prohibited. Do not import from extras/ in any story file.

All components needed for the GEN::MEDIA focused stories are available in canary. These stories require zero vendored imports:

ComponentSourceImport Path
App shell: Sidebar, AppHeader, Button, etc.Canary@/components/canary/organisms/, atoms/, primitives/
ItemGrid (with ImageCellDisplay already wired)Canary@/components/canary/organisms/item-grid/item-grid
ItemDetailsCanary@/components/canary/organisms/item-details/item-details
ImageDisplayCanary@/components/canary/molecules/image-display/
ImageDropZoneCanary@/components/canary/molecules/image-drop-zone/
ImagePreviewEditorCanary@/components/canary/molecules/image-preview-editor/
ImageComparisonLayoutCanary@/components/canary/molecules/image-comparison-layout/
ImageInspectorOverlayCanary@/components/canary/molecules/image-inspector-overlay/
ImageHoverPreviewCanary@/components/canary/molecules/image-hover-preview/
ImageFormFieldCanary@/components/canary/molecules/form/image/
ImageCellDisplay, ImageCellEditorCanary@/components/canary/atoms/grid/image/
CopyrightAcknowledgmentCanary@/components/canary/atoms/copyright-acknowledgment/
ImageUploadDialogCanary@/components/canary/organisms/shared/image-upload-dialog/
Primitives: alert-dialog, checkbox, popover, progress, slider, aspect-ratioCanary@/components/canary/primitives/
Mock data: ITEM_IMAGE_CONFIG, mockUpload, mockReachabilityCheckCanary@/components/canary/__mocks__/image-story-data

Vendored Fallback — Reference Story Only

Section titled “Vendored Fallback — Reference Story Only”

The vendored ItemFormPanel is retained in a reference story that preserves the existing production form rendering. A separate canary story demonstrates the image upload components in a simplified form context. See UD-03 for this decision.

NeedVendored ComponentImport PathUsed In
Item Create form (reference)ItemFormPanel@frontend/components/items/ItemFormPanelset-image-during-creation.stories.tsx (reference story, unmodified)
Vendored CSSglobals.css@/styles/vendored/globals.cssSame story

The reference story renders the vendored form as-is — with its own embedded image handling (imageUrl text field, imageFieldError, usingDefaultImage). It does not integrate canary image components. Its purpose is to preserve a baseline of the current production form for comparison.

The canary story (set-image-during-creation-canary.stories.tsx) uses a story-local simplified form composed entirely from canary components. The canary ImageFormField and ImageUploadDialog are first-class participants, not patched into the vendored state machine.

The vendored globals.css is not needed by any story other than the reference story (UD-02). The canary globals.css (loaded globally by preview.ts) already provides:

  • All design tokens via tokens.css (oklch-based, equivalent to vendored hex values)
  • Tailwind imports (@import 'tailwindcss', @import 'tw-animate-css')
  • @theme inline block mapping CSS variables to Tailwind utilities
  • @layer base defaults, cursor policy, sidebar light-mode override, responsive control heights, scrollbar styles

The vendored CSS (@/styles/vendored/globals.css) is a 7-line wrapper that re-imports the vendored app’s globals.css and adds a @source directive for Tailwind class scanning of vendored code. It is required only because ItemFormPanel uses Tailwind classes (e.g., responsive md:block) that must be in the scan scope to generate correctly. Stories that do not render vendored components must not import the vendored CSS.

REF::ITM::0004::0006.UC uses canary components only (UD-01). The Change and Remove Item Image stories compose from canary ItemGrid (with ImageCellDisplay / ImageCellEditor), ItemDetails, ImageFormField, ImageUploadDialog, and the canary app shell. No vendored components are used.

Building new inline canary form components (e.g., labeled text fields, select dropdowns) is out of scope for this project.

Every new scenario directory should contain:

  1. A description.mdx file with a brief description of the scenario and a reference to the scenario document and section in the documentation/src/content/docs/product/use-cases directory that needs to be translated to the published site when the site is built.
  2. A *.stories.tsx file that demonstrates the scenario and its variations
  3. A Playground story in the same *.stories.tsx file or a separate file containing an interactive story with enabled Storybook controls for the user to explore the complete range of capabilities of the scenario.

All Stories live under src/use-cases with subdirectories following the the identifier structure: <DOMAIN>::<AREA>::<USECASE>::<SCENARIO> translated to:

domain/
area/
usecase/
scenario/

The sidebar follows the directory structure. Within each section, the first entry is the description document for that subdirectory (description.mdx), followed by other files in alphabetical order then subdirectories in alphabetical order.

Stories (leaf sections) follow this order:

  1. description.mdx
  2. Stories in alphabetical order except the Playground story
  3. Playground story last

The ImageCellDisplay replacement of the inline ImageCellRenderer in item-grid-columns.tsx is already implemented on branch jmpicnic/item-image-upload-components. The column definition uses:

{
field: 'imageUrl',
cellRenderer: ImageCellDisplay,
cellRendererParams: { config: ITEM_IMAGE_CONFIG },
editable: false, // ImageCellEditor not yet wired — see 0007.FS note below
}

The existing stories compose ItemGrid without referencing the cell renderer directly, but the visual output changes (hover affordances, action icons, hover preview) and must be verified.

Note: The image column currently has editable: false. createImageCellEditor exists in the canary barrel but is not registered in the column definitions. The GEN-MEDIA-0001::0007.FS grid inline edit stories must either wire the cell editor in story-local column overrides or update item-grid-columns.tsx to enable it. This decision should be made during Wave 4 implementation.

Verification steps (performed during Wave 1):

  1. ITM-0001 Browse and Search / View Items List — verify the grid renders with the new ImageCellDisplay. Image thumbnails appear in the Image column. Hover over an image cell shows the ImageHoverPreview popover. No visual regressions in non-image columns. The existing play function still passes.
  2. ITM-0002 View Details / Item Details Panel — verify the grid renders correctly when the details drawer is open. Image column is visible. Existing play function still passes.
  3. ITM-0004 Edit Item / Edit Inline — verify that double-clicking the image cell opens ImageUploadDialog via ImageCellEditor. The existing inline edit play function (which edits a text cell) still passes. The image cell editing behavior is new and is covered by the GEN-MEDIA-0001::0007.FS stories.

These verification steps are not new stories — they are checks on existing stories after the component swap. If any existing play function breaks, fix it as part of the component implementation (not this specification).

Future Work: Image Behavior Stories for Existing Pages

Section titled “Future Work: Image Behavior Stories for Existing Pages”

The following stories are out of scope for this project but should be created in a follow-up to demonstrate the new image behaviors within the existing Item use case stories:

Existing StoryNew Behavior to DemonstrateSuggested Story
ITM-0001 Browse and SearchImage column hover preview, action icons, error badge on broken images0001 Browse – Image Column Interactions
ITM-0001 Browse and SearchImage column with mixed states (loaded, placeholder, error) across rows0001 Browse – Image Column States
ITM-0002 View DetailsImage thumbnail in detail panel header (if applicable)0002 Details – Image Display
ITM-0004 Edit ItemImage cell inline edit (double-click → upload dialog → confirm → grid update)0004 Edit – Image Cell Edit

These stories would extend the existing directories and follow the same full-app composition pattern. They are documented here for planning purposes.

Every scenario produces at least two story exports:

  1. Primary story — demonstrates the scenario’s interaction sequence. Includes a play function that exercises the flow end-to-end for automated regression testing.
  2. Playground / Controls story — exposes key props and state as Storybook controls (argTypes) so reviewers can experiment with alternatives, toggle states, and alter the UX.

Scenarios with multi-step wizard flows use the Use Case Framework (createUseCaseStories) to generate Interactive, Stepwise, and Automated variants automatically. The framework is appropriate when the workflow requires the user to complete more than one discrete step before a final action and intermediate state must be preserved between steps.

Scenarios that are observation-oriented (grid inspection, state display) or single-interaction (confirmation dialogs) use standard Storybook story exports with play functions and explicit step() calls for the primary story, plus a Playground export with argTypes for the controls variant.

A scenario may require multiple story files when it involves:

  • Alternative interaction sequences (e.g., happy path vs. error recovery)
  • Different input modes (e.g., file pick vs. drag-drop vs. clipboard paste)
  • Different contexts (e.g., form field vs. grid cell)
  • Distinct user-visible states that warrant separate demonstration

The specification below lists all required stories per scenario. Each story file is a separate sidebar entry.

Story title paths use a hierarchical structure that groups stories by use case and then by scenario, enabling fold/expand navigation in the Storybook sidebar:

Use Cases/{Domain}/{Entity}/{UC-ID Use Case Name}/{Scenario ID Scenario Name}/{Story Name}
  • GEN::MEDIA stories: Use Cases/General Behaviors/Entity Media/GEN-MEDIA-{NNNN} {Name}/{SSSS} {Scenario}/{Story}
  • REF::ITM stories: Use Cases/Reference/Items/ITM-{NNNN} {Name}/{SSSS} {Scenario}/{Story}

Examples:

Use Cases/General Behaviors/Entity Media/GEN-MEDIA-0001 Set Entity Image/0002 Input Detection/File Pick
Use Cases/General Behaviors/Entity Media/GEN-MEDIA-0001 Set Entity Image/0005 Preview and Crop/Comparison Desktop
Use Cases/General Behaviors/Entity Media/GEN-MEDIA-0003 View Entity Image/0001 View in Grid/Grid Thumbnails
Use Cases/Reference/Items/ITM-0003 Create Item/0010 Set Image/During Creation – Canary
Use Cases/Reference/Items/ITM-0004 Edit Item/0006 Change or Remove Image/Change Item Image

This adds a scenario grouping level so stakeholders can fold/expand by scenario within a use case. For scenarios with a single story, the story name is still nested under the scenario folder for consistency.

Each use case directory contains a description MDX file as its first sidebar entry. The MDX follows the template established in the Use Cases conceptual guide with sections: Header, link to full specification, Context, Scenarios table (with implementation status), and Story Artifacts table.

All MDX files must use HTML <table> elements (not markdown tables), numeric HTML entities (not named entities), and avoid bare curly braces in text.

  • GEN::MEDIA stories use focused composition — the component under demonstration is rendered in a minimal but realistic context (e.g., ImageUploadDialog with mock callbacks, ImageCellDisplay in a small AG Grid). This shows the generic capability without coupling to a specific entity type. Mock data uses ITEM_IMAGE_CONFIG as the concrete configuration.
  • REF::ITM stories use full-app context — Sidebar + AppHeader + ItemGrid (or Item form), matching the pattern established by existing Item stories (view-items-list.stories.tsx, item-details-panel.stories.tsx, edit-inline.stories.tsx). These stories emphasize how the image interaction integrates with the surrounding application. Overlap with GEN::MEDIA stories is acceptable; the REF::ITM stories need not duplicate every input-mode variation if the focused GEN::MEDIA stories cover them.

All stories use mock data only — no backend connections. Upload workflows use mockUpload() (delayed Promise simulating presigned-POST success). URL reachability checks use mockReachabilityCheck(). Image URLs use picsum.photos placeholder services with stable seeds. Mock files for items re-export from src/use-cases/reference/items/_shared/mock-data.ts.

Existing mock data (from component implementation)

Section titled “Existing mock data (from component implementation)”

The following are already available in src/components/canary/__mocks__/image-story-data.ts:

  • MOCK_ITEM_IMAGE, MOCK_ITEM_IMAGE_ALT — picsum.photos URLs with stable seeds
  • MOCK_BROKEN_IMAGE — unreachable URL (https://example.com/nonexistent-image-404.jpg)
  • MOCK_LARGE_IMAGE — 2048x2048 picsum URL
  • ITEM_IMAGE_CONFIGImageFieldConfig for Items (1:1 aspect, 10 MB max)
  • mockUpload() — 1.5s delayed Promise returning a CDN URL
  • mockReachabilityCheck() — 500ms delayed Promise, fails for URLs containing “broken”
  • MOCK_ITEMS — 4-item array with mixed image states

Additional mock data (to be added in Wave 1)

Section titled “Additional mock data (to be added in Wave 1)”

The use case stories require additional mock objects not present in the component mock module. These should be added to a use-case-specific mock file at src/use-cases/general-behaviors/entity-media/_shared/mock-data.ts:

  • MOCK_FILE_JPEG — a small JPEG File object for file-pick and drag-drop stories
  • MOCK_FILE_PNG — a PNG File object
  • MOCK_FILE_OVERSIZED — a file exceeding maxFileSizeBytes for auto-compression stories
  • MOCK_FILE_BMP — an unsupported format for rejection stories
  • MOCK_CLIPBOARD_IMAGE_BLOB — a ClipboardEvent-compatible mock for clipboard paste stories
  • MOCK_EXTERNAL_URL — a valid HTTPS URL for URL-entry stories (e.g., 'https://picsum.photos/seed/arda-external/400/400')
  • MOCK_EXTERNAL_URL_BROKEN — an unreachable HTTPS URL
  • MOCK_EXTERNAL_URL_NON_IMAGE — a URL that returns non-image content type
  • mockUploadSlow() — extended delay (~5s) for progress bar demonstration
  • mockUploadFail() — rejects after delay for error handling stories

Sidebar path: Use Cases/General Behaviors/Entity Media/

Directory root: src/use-cases/general-behaviors/entity-media/

Directory: set-entity-image/ | MDX: set-entity-image.mdx

This is the most complex use case — a multi-state interaction with five input methods, validation, preview/crop, copyright acknowledgment, and confirm/persist. The stories decompose it by scenario, with multiple stories per scenario where input modes or contexts diverge.

0001.UC — Set Image via Unified Input Surface
Section titled “0001.UC — Set Image via Unified Input Surface”

The end-to-end flow from activating the image area through providing input, previewing, acknowledging copyright, and confirming. Uses ImageUploadDialog as the primary composed component.

Framework: Yes — multi-step wizard flow (activate → provide input → preview/crop → copyright → confirm).

Story FileSidebar TitleDescription
set-image-happy-path.stories.tsx0001 Set Image – Happy PathFile pick → preview → crop → copyright ack → confirm. Full end-to-end with createUseCaseStories (Interactive / Stepwise / Automated).
set-image-replace-existing.stories.tsx0001 Set Image – Replace ExistingSame flow but with an existing image present. Demonstrates comparison layout (desktop side-by-side and mobile tabs).
set-image-cancel-and-warn.stories.tsx0001 Set Image – Cancel and WarnUser provides an image then attempts to dismiss. Demonstrates the Warn confirmation state. Covers discard and return-to-edit paths.

Each input method is a distinct interaction sequence requiring its own story.

Framework: No — single-interaction demonstrations. Each story shows the drop zone accepting a specific input type and routing it. The play function simulates the input method and verifies the correct routing.

Story FileSidebar TitleDescription
input-file-pick.stories.tsx0002 Input – File PickUser clicks “Upload from computer”, selects a file via OS dialog. Playground exposes acceptedFormats and file type controls.
input-drag-and-drop.stories.tsx0002 Input – Drag and DropUser drags a file onto the drop zone. Shows idle, drag-over highlight, and drop-accepted states.
input-clipboard-image.stories.tsx0002 Input – Clipboard ImageUser pastes an image blob (screenshot, image editor). Shows transient data: URI preview routed to managed upload.
input-clipboard-html.stories.tsx0002 Input – Clipboard HTMLUser pastes from a web page (HTML with embedded URL). Shows URL extraction and validation. Partially in scope per SD-14.
input-url-entry.stories.tsx0002 Input – URL EntryUser types/pastes an HTTPS URL into the text field. Shows URL pattern detection, validation, and reachability check.
input-camera-capture.stories.tsx0002 Input – Camera Capture”Take photo” affordance. Demonstrates the camera input method routing through the managed upload path. Detailed mobile UX deferred per SD-16.
input-unrecognized.stories.tsx0002 Input – Unrecognized TextUser pastes non-image, non-URL text. Shows the inline error message and retry.
input-data-uri.stories.tsx0002 Input – Data URI TextUser pastes a data: or blob: URI string. Shows decode attempt and routing to managed upload, or error on decode failure.
0003.FS — Accepted Formats and Size Limits
Section titled “0003.FS — Accepted Formats and Size Limits”

Validation stories showing accepted formats, rejected formats, and auto-compression.

Framework: No — single-interaction error/success demonstrations.

Story FileSidebar TitleDescription
formats-accepted.stories.tsx0003 Formats – AcceptedJPEG, PNG, WebP, HEIC files accepted. Playground controls for toggling format.
formats-rejected.stories.tsx0003 Formats – RejectedBMP, SVG, and other unsupported formats. Shows plain-language error messages.
size-limit-exceeded.stories.tsx0003 Size – Limit ExceededFile exceeds 10 MB. Shows auto-compression attempt and, if still too large, the size error message.
size-auto-compressed.stories.tsx0003 Size – Auto CompressedOversized file successfully compressed. Shows the “Your image has been optimized” inline message.
0004.FS — URL Scheme and Reachability Validation
Section titled “0004.FS — URL Scheme and Reachability Validation”

Validation stories for URL input.

Framework: No — single-interaction demonstrations.

Story FileSidebar TitleDescription
url-valid-https.stories.tsx0004 URL – Valid HTTPSValid HTTPS URL passes scheme and reachability check. Shows success state. Playground exposes URL input for experimentation.
url-rejected-scheme.stories.tsx0004 URL – Rejected Schemehttp:, javascript:, file: URLs rejected. Shows “Only secure web addresses (https) are accepted.”
url-unreachable.stories.tsx0004 URL – UnreachableHTTPS URL returns 404 or timeout. Shows “We couldn’t load an image from this address…”
url-wrong-content-type.stories.tsx0004 URL – Wrong Content TypeURL points to non-image content. Shows “The link doesn’t point to a supported image type…“

Preview, edit operations, comparison layout, and interaction states.

Framework: No — observation and interaction-focused stories. The play function demonstrates the edit operation sequence.

Story FileSidebar TitleDescription
preview-new-image.stories.tsx0005 Preview – New ImageImage preview at 1:1 locked aspect ratio. No existing image (no comparison). Playground exposes aspectRatio control.
preview-comparison-desktop.stories.tsx0005 Preview – Comparison DesktopReplacing an existing image. Desktop layout: small reference + large preview.
preview-comparison-mobile.stories.tsx0005 Preview – Comparison MobileSame replacement scenario at mobile viewport. Tabbed layout.
edit-crop-zoom-rotate.stories.tsx0005 Edit – Crop Zoom RotateDemonstrates crop reposition, zoom in/out, rotate 90-degree increments, pan, and reset. Stepwise play function exercises each operation.
preview-url-loading.stories.tsx0005 Preview – URL LoadingURL-sourced image with slow load. Shows shimmer placeholder, then loaded image. Timeout error on failure.
interaction-states.stories.tsx0005 Interaction StatesPlayground showing all five states: View, EmptyImage, ProvidedImage, FailedValidation, Warn. Controls toggle between states.

Copyright acknowledgment and the confirm/cancel flow.

Framework: No — focused interaction demonstration.

Story FileSidebar TitleDescription
copyright-acknowledgment.stories.tsx0006 Copyright AcknowledgmentShows the mandatory checkbox. Confirm button disabled until acknowledged. Playground exposes acknowledged state.
confirm-managed-upload.stories.tsx0006 Confirm – Managed UploadFile input confirmed. Shows upload progress bar (presigned POST mock), success, and grid/form update.
confirm-external-url.stories.tsx0006 Confirm – External URLURL input confirmed. URL stored as-is (no upload). Entity display updates.
cancel-no-change.stories.tsx0006 Cancel – No ChangeUser cancels. In-progress upload aborted. Entity unchanged.

Modal overlay triggered from grid cell edit.

Framework: No — grid-interaction-focused. The play function double-clicks a cell, verifies the modal opens, and exercises the upload flow.

Story FileSidebar TitleDescription
grid-inline-edit-double-click.stories.tsx0007 Grid Edit – Double ClickDouble-click on image cell opens ImageUploadDialog modal over grid. Confirm updates the row thumbnail.
grid-inline-edit-enter-key.stories.tsx0007 Grid Edit – Enter KeySelect cell + Enter key triggers the same modal. Demonstrates keyboard accessibility.
grid-inline-edit-from-inspector.stories.tsx0007 Grid Edit – From InspectorEye icon → ImageInspectorOverlay → Edit button → inspector closes → ImageUploadDialog opens.
grid-inline-edit-cancel.stories.tsx0007 Grid Edit – CancelModal opened, user cancels. Grid row unchanged. Overlay dismisses cleanly.

Directory: remove-entity-image/ | MDX: remove-entity-image.mdx

Confirmation-based removal.

Framework: No — single confirmation dialog interaction.

Story FileSidebar TitleDescription
remove-from-form.stories.tsx0001 Remove – From FormTrash icon on ImageFormField hover → confirmation dialog → confirm → placeholder shown. play function exercises the full sequence.
remove-from-form-cancel.stories.tsx0001 Remove – CancelSame trigger, user clicks Cancel. Image unchanged.
remove-playground.stories.tsx0001 Remove – PlaygroundPlayground with controls: imageUrl (populated vs. null), entityTypeDisplayName. Reviewer can toggle states and trigger removal.

Directory: view-entity-image/ | MDX: view-entity-image.mdx

Grid thumbnail rendering.

Framework: No — observation story. The play function verifies thumbnails render in grid cells.

Story FileSidebar TitleDescription
view-grid-thumbnails.stories.tsx0001 View – Grid ThumbnailsAG Grid with ImageCellDisplay rendering thumbnails. Mix of loaded images, null (placeholder), and error states across rows.
view-grid-hover-preview.stories.tsx0001 View – Hover PreviewHover over a thumbnail (~500ms) shows ImageHoverPreview popover with larger image. play function hovers and verifies popover appears.
view-grid-playground.stories.tsx0001 View – PlaygroundPlayground with controls for imageUrl, entityTypeDisplayName, cell dimensions. Reviewer experiments with different image states.

Full-size image inspection modal.

Framework: No — click-to-inspect interaction.

Story FileSidebar TitleDescription
inspector-open-close.stories.tsx0002 Inspector – Open and CloseClick thumbnail → modal with full-size image. Dismiss via Escape, click outside, or close button. play function exercises all three dismiss methods.
inspector-edit-transition.stories.tsx0002 Inspector – Edit TransitionInspector with onEdit callback. Edit button visible. Click Edit → inspector closes → ImageUploadDialog opens.
inspector-no-edit.stories.tsx0002 Inspector – No Edit (Read Only)Inspector without onEdit. No Edit button shown. Read-only inspection.
0003.FS — Thumbnail Fallback and Error State
Section titled “0003.FS — Thumbnail Fallback and Error State”

Error handling and loading states in grid cells.

Framework: No — state-display stories.

Story FileSidebar TitleDescription
fallback-error-badge.stories.tsx0003 Fallback – Error BadgeUnreachable image URL. Placeholder with error badge overlay. Inspector click suppressed.
fallback-loading-shimmer.stories.tsx0003 Fallback – Loading ShimmerSlow-loading image. Shimmer placeholder transitions to loaded thumbnail without reflow.
fallback-no-image.stories.tsx0003 Fallback – No ImageimageUrl is null. Initials placeholder without error badge. Distinguishes “no image” from “broken image”.
fallback-playground.stories.tsx0003 Fallback – PlaygroundPlayground with controls: imageUrl (valid, broken, null), entityTypeDisplayName, loading delay.

Sidebar path: Use Cases/Reference/Items/

Directory root: src/use-cases/reference/items/ (existing)

0003::0010.UC — Set Item Image During Creation
Section titled “0003::0010.UC — Set Item Image During Creation”

Directory: create-item/

(Extends the existing ITM-0003 Create Item section.)

This scenario has two stories: a reference story preserving the vendored production form, and a canary story demonstrating the image upload components in a simplified form context (UD-03).

Component sourcing: Vendored ItemFormPanel rendered within the canary app shell. The vendored form uses its own embedded image handling (URL text field, not canary image components). This story is a baseline for comparison — it shows the current production form behavior. Requires vendored CSS (@/styles/vendored/globals.css). Follows the Canary Refactor/ pattern from src/canary-refactor/reference/items/item-detail.stories.tsx.

Framework: No — standard story wrapping the vendored component.

Story FileSidebar TitleDescription
set-image-during-creation.stories.tsx0010 Set Image During CreationFull-app context: Sidebar + AppHeader + vendored Add Item slide-over form. Preserves the current production form rendering as a reference baseline. Vendored image handling (URL text field).

Component sourcing: 100% canary. A story-local simplified Item form composed from canary primitives (Input, Label, Button) and the canary ImageFormField. The form shows enough context (Title, SKU, image area, Publish button) for stakeholders to understand “this is what adding an item with an image looks like” using the new canary image components. The canary ImageFormField and ImageUploadDialog are first-class participants — not adapted into a vendored state machine.

Framework: Yes — multi-step wizard flow (open form → fill fields → activate image → provide input → preview/crop → copyright → confirm → publish).

Story FileSidebar TitleDescription
set-image-during-creation-canary.stories.tsx0010 Set Image During Creation – CanaryFull-app context: Sidebar + AppHeader + canary simplified Add Item form with ImageFormField. User fills item fields, activates the image area, provides an image (file pick — happy path), acknowledges copyright, confirms. createUseCaseStories generates Interactive / Stepwise / Automated. Emphasis on the canary image upload flow integrated into a form context.
  1. Add Item form opens (empty) — simplified form with Title, SKU, Image, Publish button
  2. User fills Title field
  3. User fills SKU
  4. User clicks the image placeholder — ImageUploadDialog opens
  5. User picks a file — preview shown with 1:1 crop
  6. User adjusts crop
  7. User acknowledges copyright
  8. User confirms — dialog closes, image thumbnail appears in form
  9. User clicks Publish — item created with image
  10. Success screen
0004::0006.UC — Change or Remove Item Image
Section titled “0004::0006.UC — Change or Remove Item Image”

Directory: edit-item/ (existing)

(Extends the existing ITM-0004 Edit Item section.)

Component sourcing: 100% canary (UD-01). These stories compose from ItemGrid (with ImageCellDisplay / ImageCellEditor), ItemDetails (with ImageFormField), ImageUploadDialog, and the canary app shell (Sidebar + AppHeader). No vendored components. This matches the pattern already established by the existing edit-inline.stories.tsx in the same directory.

Framework: No — grid-context interaction demonstration.

Story FileSidebar TitleDescription
change-item-image.stories.tsx0006 Change Item ImageFull-app context: Items list grid. User double-clicks the image cell of an item that already has an image. ImageUploadDialog opens with comparison layout showing existing vs. new. User provides new image, confirms. Grid row updates. play function exercises the full sequence.
remove-item-image.stories.tsx0006 Remove Item ImageFull-app context: Items list grid with item detail panel open. User hovers the image in the ImageFormField, clicks trash icon, confirms removal. Image reverts to initials placeholder.

Use CaseScenariosStory FilesFramework StoriesStandard Stories
GEN-MEDIA-0001 Set Entity Image7303 (0001.UC)27
GEN-MEDIA-0002 Remove Entity Image1303
GEN-MEDIA-0003 View Entity Image310010
REF-ITM-0003-0010 Set Image During Creation1211
REF-ITM-0004-0006 Change or Remove Item Image1202
Total1347443

Plus description MDX files: 1 section (Entity Media) + 3 use case level + 13 scenario level = 17 description.mdx files.

Follows the domain/area/usecase/scenario/ convention from Deliverables per Scenario. Each scenario directory contains a description.mdx and its story files.

src/use-cases/
├── general-behaviors/
│ ├── general-behaviors.mdx # (existing)
│ └── entity-media/
│ ├── description.mdx # Entity Media section
│ ├── _shared/
│ │ └── mock-data.ts # Use-case-specific mocks
│ ├── set-entity-image/
│ │ ├── description.mdx # Use case description
│ │ ├── 0001-set-image/
│ │ │ ├── description.mdx
│ │ │ ├── happy-path.stories.tsx
│ │ │ ├── replace-existing.stories.tsx
│ │ │ └── cancel-and-warn.stories.tsx
│ │ ├── 0002-input-detection/
│ │ │ ├── description.mdx
│ │ │ ├── file-pick.stories.tsx
│ │ │ ├── drag-and-drop.stories.tsx
│ │ │ ├── clipboard-image.stories.tsx
│ │ │ ├── clipboard-html.stories.tsx
│ │ │ ├── url-entry.stories.tsx
│ │ │ ├── camera-capture.stories.tsx
│ │ │ ├── unrecognized.stories.tsx
│ │ │ └── data-uri.stories.tsx
│ │ ├── 0003-formats-and-size/
│ │ │ ├── description.mdx
│ │ │ ├── accepted.stories.tsx
│ │ │ ├── rejected.stories.tsx
│ │ │ ├── limit-exceeded.stories.tsx
│ │ │ └── auto-compressed.stories.tsx
│ │ ├── 0004-url-validation/
│ │ │ ├── description.mdx
│ │ │ ├── valid-https.stories.tsx
│ │ │ ├── rejected-scheme.stories.tsx
│ │ │ ├── unreachable.stories.tsx
│ │ │ └── wrong-content-type.stories.tsx
│ │ ├── 0005-preview-and-crop/
│ │ │ ├── description.mdx
│ │ │ ├── new-image.stories.tsx
│ │ │ ├── comparison-desktop.stories.tsx
│ │ │ ├── comparison-mobile.stories.tsx
│ │ │ ├── crop-zoom-rotate.stories.tsx
│ │ │ ├── url-loading.stories.tsx
│ │ │ └── interaction-states.stories.tsx
│ │ ├── 0006-confirm-and-persist/
│ │ │ ├── description.mdx
│ │ │ ├── copyright-acknowledgment.stories.tsx
│ │ │ ├── managed-upload.stories.tsx
│ │ │ ├── external-url.stories.tsx
│ │ │ └── cancel-no-change.stories.tsx
│ │ └── 0007-grid-inline-edit/
│ │ ├── description.mdx
│ │ ├── double-click.stories.tsx
│ │ ├── enter-key.stories.tsx
│ │ ├── from-inspector.stories.tsx
│ │ └── cancel.stories.tsx
│ ├── remove-entity-image/
│ │ ├── description.mdx # Use case description
│ │ └── 0001-remove-image/
│ │ ├── description.mdx
│ │ ├── from-form.stories.tsx
│ │ ├── cancel.stories.tsx
│ │ └── playground.stories.tsx
│ └── view-entity-image/
│ ├── description.mdx # Use case description
│ ├── 0001-view-in-grid/
│ │ ├── description.mdx
│ │ ├── grid-thumbnails.stories.tsx
│ │ ├── hover-preview.stories.tsx
│ │ └── playground.stories.tsx
│ ├── 0002-inspector-overlay/
│ │ ├── description.mdx
│ │ ├── open-and-close.stories.tsx
│ │ ├── edit-transition.stories.tsx
│ │ └── no-edit.stories.tsx
│ └── 0003-thumbnail-fallback/
│ ├── description.mdx
│ ├── error-badge.stories.tsx
│ ├── loading-shimmer.stories.tsx
│ ├── no-image.stories.tsx
│ └── playground.stories.tsx
├── reference/
│ └── items/ # (existing)
│ ├── create-item/
│ │ └── 0010-set-image/
│ │ ├── description.mdx
│ │ ├── during-creation.stories.tsx # Reference (vendored)
│ │ └── during-creation-canary.stories.tsx # Canary
│ └── edit-item/ # (existing)
│ ├── edit-inline.stories.tsx # (existing)
│ └── 0006-change-or-remove-image/
│ ├── description.mdx
│ ├── change-item-image.stories.tsx
│ └── remove-item-image.stories.tsx
└── list-views/ # (existing)

Stories are ordered by component dependency (matching the component implementation waves) and by use case complexity (simplest observation stories first, complex multi-step flows last).

Wave 1 — View and Fallback Stories (depends on: Wave 1–2 components)

Section titled “Wave 1 — View and Fallback Stories (depends on: Wave 1–2 components)”

Stories that compose ImageDisplay, ImageCellDisplay, ImageHoverPreview, and ImageInspectorOverlay. These are observation-oriented and use standard story exports.

Scope:

  • GEN-MEDIA-0003 View Entity Image — all 10 stories
  • Description MDX for view-entity-image/

Gate: Storybook builds, all new stories render, play functions pass.

Wave 2 — Input and Validation Stories (depends on: Wave 1–2 components)

Section titled “Wave 2 — Input and Validation Stories (depends on: Wave 1–2 components)”

Stories that compose ImageDropZone in isolation. Focused on input detection, format validation, and URL validation.

Scope:

  • GEN-MEDIA-0001::0002.FS Input Detection and Routing — all 8 stories
  • GEN-MEDIA-0001::0003.FS Accepted Formats and Size Limits — all 4 stories
  • GEN-MEDIA-0001::0004.FS URL Scheme and Reachability Validation — all 4 stories

Gate: Storybook builds, all new stories render, play functions pass.

Wave 3 — Preview, Edit, and Confirm Stories (depends on: Wave 1–3 components)

Section titled “Wave 3 — Preview, Edit, and Confirm Stories (depends on: Wave 1–3 components)”

Stories that compose ImagePreviewEditor, ImageComparisonLayout, CopyrightAcknowledgment, and ImageUploadDialog.

Scope:

  • GEN-MEDIA-0001::0005.FS Preview and Crop — all 6 stories
  • GEN-MEDIA-0001::0006.FS Confirm and Persist — all 4 stories
  • Description MDX for set-entity-image/

Gate: Storybook builds, all new stories render, play functions pass.

Wave 4 — End-to-End and Integration Stories (depends on: all components)

Section titled “Wave 4 — End-to-End and Integration Stories (depends on: all components)”

Full-flow stories composing ImageUploadDialog end-to-end, grid inline edit stories, removal stories, and full-app REF::ITM integration stories.

Scope:

  • GEN-MEDIA-0001::0001.UC Set Image via Unified Input Surface — 3 framework stories
  • GEN-MEDIA-0001::0007.FS Grid Inline Edit Entry Point — all 4 stories
  • GEN-MEDIA-0002::0001.UC Remove Image — all 3 stories
  • REF-ITM-0003::0010.UC Set Item Image During Creation — 1 framework story
  • REF-ITM-0004::0006.UC Change or Remove Item Image — 2 stories
  • Description MDX for entity-media/, remove-entity-image/

Gate: Storybook builds, all stories render, all play functions pass, full Storybook test runner passes.

At the end of each wave:

  1. npm run lint passes.
  2. npx tsc --noEmit passes.
  3. npm run build-storybook completes without errors.
  4. All unit tests pass (npm run test).
  5. All new story play functions pass in the Storybook test runner.
  6. VRT check. Run VRT tests (npx playwright test --project=vrt). The ImageCellDisplay swap and new hover/action affordances will cause visual diffs in existing stories that render ItemGrid. For each failure: evaluate whether the diff reflects the intended change (new image cell rendering, hover preview, action icons). If acceptable, retake the baseline snapshot (./tools/generate-vrt-baselines.sh). If unexpected, investigate and fix before proceeding.
  7. Changes are committed locally before proceeding to the next wave.
ComponentStories That Compose It
ImageDisplayAll GEN-MEDIA-0003 stories, preview-*, fallback-*, inspector-*
ImageCellDisplayview-grid-*, grid-inline-edit-*, change-item-image, fallback-*
ImageCellEditorgrid-inline-edit-*, change-item-image
ImageHoverPreviewview-grid-hover-preview, remove-from-form*
ImageInspectorOverlayinspector-*, grid-inline-edit-from-inspector
ImageFormFieldset-image-during-creation, remove-from-form*, remove-item-image
ImageDropZoneinput-*, set-image-happy-path, set-image-replace-existing
ImagePreviewEditorpreview-*, edit-crop-zoom-rotate, set-image-*
ImageComparisonLayoutpreview-comparison-*, set-image-replace-existing, change-item-image
CopyrightAcknowledgmentcopyright-acknowledgment, confirm-*, set-image-happy-path
ImageUploadDialogset-image-*, confirm-*, cancel-*, grid-inline-edit-*, set-image-during-creation, change-item-image
Badge (error-overlay)fallback-error-badge
Avatar (getInitials)fallback-no-image, view-grid-thumbnails

The following are explicitly excluded from this specification:

  • Remove background edit operation (SD-02)
  • Bulk CSV image column UX (SD-03)
  • Fetch-and-store external URLs (SD-01)
  • Retention policy for superseded images (SD-04)
  • Cross-tenant image sharing (SD-05)
  • Mobile camera capture detailed UX (SD-16)
  • Hover-to-enlarge and lazy loading (SD-15 — deferred)

Copyright: (c) Arda Systems 2025-2026, All rights reserved