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.
Entry Conditions
Section titled “Entry Conditions”The following must be verified before implementation begins.
- 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 fromjmpicnic/item-image-upload-componentsdirectly — no need to wait for the PR to merge tomain. - Existing checks green. Lint, Storybook build, and all unit tests pass on the working branch.
- Mock data module available. The shared mock data module
(
src/components/canary/__mocks__/image-story-data.ts) is in place withMOCK_ITEMS,ITEM_IMAGE_CONFIG,mockUpload, andmockReachabilityCheck. Additional mock objects required for use case stories (see Mock Strategy) must be added as the first task in Wave 1.
Component Sourcing
Section titled “Component Sourcing”Stories in this project must follow a strict sourcing hierarchy for UI components:
- Canary components (
@/components/canary/) — preferred. Use for all components that have a canary implementation. - Vendored components (
@frontend/) — fallback. Use only when no canary equivalent exists. extrascomponents (@/components/extras/) — prohibited. Do not import fromextras/in any story file.
Canary Coverage
Section titled “Canary Coverage”All components needed for the GEN::MEDIA focused stories are available in
canary. These stories require zero vendored imports:
| Component | Source | Import 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 |
ItemDetails | Canary | @/components/canary/organisms/item-details/item-details |
ImageDisplay | Canary | @/components/canary/molecules/image-display/ |
ImageDropZone | Canary | @/components/canary/molecules/image-drop-zone/ |
ImagePreviewEditor | Canary | @/components/canary/molecules/image-preview-editor/ |
ImageComparisonLayout | Canary | @/components/canary/molecules/image-comparison-layout/ |
ImageInspectorOverlay | Canary | @/components/canary/molecules/image-inspector-overlay/ |
ImageHoverPreview | Canary | @/components/canary/molecules/image-hover-preview/ |
ImageFormField | Canary | @/components/canary/molecules/form/image/ |
ImageCellDisplay, ImageCellEditor | Canary | @/components/canary/atoms/grid/image/ |
CopyrightAcknowledgment | Canary | @/components/canary/atoms/copyright-acknowledgment/ |
ImageUploadDialog | Canary | @/components/canary/organisms/shared/image-upload-dialog/ |
Primitives: alert-dialog, checkbox, popover, progress, slider, aspect-ratio | Canary | @/components/canary/primitives/ |
Mock data: ITEM_IMAGE_CONFIG, mockUpload, mockReachabilityCheck | Canary | @/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.
| Need | Vendored Component | Import Path | Used In |
|---|---|---|---|
| Item Create form (reference) | ItemFormPanel | @frontend/components/items/ItemFormPanel | set-image-during-creation.stories.tsx (reference story, unmodified) |
| Vendored CSS | globals.css | @/styles/vendored/globals.css | Same 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 inlineblock mapping CSS variables to Tailwind utilities@layer basedefaults, 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.
Deliverables per Scenario
Section titled “Deliverables per Scenario”Every new scenario directory should contain:
- A
description.mdxfile with a brief description of the scenario and a reference to the scenario document and section in thedocumentation/src/content/docs/product/use-casesdirectory that needs to be translated to the published site when the site is built. - A
*.stories.tsxfile that demonstrates the scenario and its variations - A
Playgroundstory in the same*.stories.tsxfile 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.
Directory Structure
Section titled “Directory Structure”All Stories live under src/use-cases with subdirectories following the the identifier structure: <DOMAIN>::<AREA>::<USECASE>::<SCENARIO> translated to:
domain/ area/ usecase/ scenario/Sidebar Ordering
Section titled “Sidebar Ordering”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:
description.mdx- Stories in alphabetical order except the Playground story
- Playground story last
Impact on Existing Stories
Section titled “Impact on Existing Stories”ImageCellDisplay Swap (Already Complete)
Section titled “ImageCellDisplay Swap (Already Complete)”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):
ITM-0001 Browse and Search / View Items List— verify the grid renders with the newImageCellDisplay. Image thumbnails appear in the Image column. Hover over an image cell shows theImageHoverPreviewpopover. No visual regressions in non-image columns. The existingplayfunction still passes.ITM-0002 View Details / Item Details Panel— verify the grid renders correctly when the details drawer is open. Image column is visible. Existingplayfunction still passes.ITM-0004 Edit Item / Edit Inline— verify that double-clicking the image cell opensImageUploadDialogviaImageCellEditor. The existing inline editplayfunction (which edits a text cell) still passes. The image cell editing behavior is new and is covered by theGEN-MEDIA-0001::0007.FSstories.
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 Story | New Behavior to Demonstrate | Suggested Story |
|---|---|---|
ITM-0001 Browse and Search | Image column hover preview, action icons, error badge on broken images | 0001 Browse – Image Column Interactions |
ITM-0001 Browse and Search | Image column with mixed states (loaded, placeholder, error) across rows | 0001 Browse – Image Column States |
ITM-0002 View Details | Image thumbnail in detail panel header (if applicable) | 0002 Details – Image Display |
ITM-0004 Edit Item | Image 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.
Conventions
Section titled “Conventions”Story Artifact Pattern
Section titled “Story Artifact Pattern”Every scenario produces at least two story exports:
- Primary story — demonstrates the scenario’s interaction sequence.
Includes a
playfunction that exercises the flow end-to-end for automated regression testing. - 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.
Multiple Stories per Scenario
Section titled “Multiple Stories per Scenario”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.
Sidebar Title Convention
Section titled “Sidebar Title Convention”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 PickUse Cases/General Behaviors/Entity Media/GEN-MEDIA-0001 Set Entity Image/0005 Preview and Crop/Comparison DesktopUse Cases/General Behaviors/Entity Media/GEN-MEDIA-0003 View Entity Image/0001 View in Grid/Grid ThumbnailsUse Cases/Reference/Items/ITM-0003 Create Item/0010 Set Image/During Creation – CanaryUse Cases/Reference/Items/ITM-0004 Edit Item/0006 Change or Remove Image/Change Item ImageThis 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.
Description MDX Convention
Section titled “Description MDX Convention”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.
Composition Context
Section titled “Composition Context”- GEN::MEDIA stories use focused composition — the component under
demonstration is rendered in a minimal but realistic context (e.g.,
ImageUploadDialogwith mock callbacks,ImageCellDisplayin a small AG Grid). This shows the generic capability without coupling to a specific entity type. Mock data usesITEM_IMAGE_CONFIGas 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.
Mock Strategy
Section titled “Mock Strategy”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 seedsMOCK_BROKEN_IMAGE— unreachable URL (https://example.com/nonexistent-image-404.jpg)MOCK_LARGE_IMAGE— 2048x2048 picsum URLITEM_IMAGE_CONFIG—ImageFieldConfigfor Items (1:1 aspect, 10 MB max)mockUpload()— 1.5s delayed Promise returning a CDN URLmockReachabilityCheck()— 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 JPEGFileobject for file-pick and drag-drop storiesMOCK_FILE_PNG— a PNGFileobjectMOCK_FILE_OVERSIZED— a file exceedingmaxFileSizeBytesfor auto-compression storiesMOCK_FILE_BMP— an unsupported format for rejection storiesMOCK_CLIPBOARD_IMAGE_BLOB— aClipboardEvent-compatible mock for clipboard paste storiesMOCK_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 URLMOCK_EXTERNAL_URL_NON_IMAGE— a URL that returns non-image content typemockUploadSlow()— extended delay (~5s) for progress bar demonstrationmockUploadFail()— rejects after delay for error handling stories
Use Case Story Inventory
Section titled “Use Case Story Inventory”GEN — General Behaviors
Section titled “GEN — General Behaviors”MEDIA — Entity Media
Section titled “MEDIA — Entity Media”Sidebar path: Use Cases/General Behaviors/Entity Media/
Directory root: src/use-cases/general-behaviors/entity-media/
0001 — Set Entity Image
Section titled “0001 — Set Entity Image”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 File | Sidebar Title | Description |
|---|---|---|
set-image-happy-path.stories.tsx | 0001 Set Image – Happy Path | File pick → preview → crop → copyright ack → confirm. Full end-to-end with createUseCaseStories (Interactive / Stepwise / Automated). |
set-image-replace-existing.stories.tsx | 0001 Set Image – Replace Existing | Same flow but with an existing image present. Demonstrates comparison layout (desktop side-by-side and mobile tabs). |
set-image-cancel-and-warn.stories.tsx | 0001 Set Image – Cancel and Warn | User provides an image then attempts to dismiss. Demonstrates the Warn confirmation state. Covers discard and return-to-edit paths. |
0002.FS — Input Detection and Routing
Section titled “0002.FS — Input Detection and Routing”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 File | Sidebar Title | Description |
|---|---|---|
input-file-pick.stories.tsx | 0002 Input – File Pick | User clicks “Upload from computer”, selects a file via OS dialog. Playground exposes acceptedFormats and file type controls. |
input-drag-and-drop.stories.tsx | 0002 Input – Drag and Drop | User drags a file onto the drop zone. Shows idle, drag-over highlight, and drop-accepted states. |
input-clipboard-image.stories.tsx | 0002 Input – Clipboard Image | User pastes an image blob (screenshot, image editor). Shows transient data: URI preview routed to managed upload. |
input-clipboard-html.stories.tsx | 0002 Input – Clipboard HTML | User pastes from a web page (HTML with embedded URL). Shows URL extraction and validation. Partially in scope per SD-14. |
input-url-entry.stories.tsx | 0002 Input – URL Entry | User types/pastes an HTTPS URL into the text field. Shows URL pattern detection, validation, and reachability check. |
input-camera-capture.stories.tsx | 0002 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.tsx | 0002 Input – Unrecognized Text | User pastes non-image, non-URL text. Shows the inline error message and retry. |
input-data-uri.stories.tsx | 0002 Input – Data URI Text | User 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 File | Sidebar Title | Description |
|---|---|---|
formats-accepted.stories.tsx | 0003 Formats – Accepted | JPEG, PNG, WebP, HEIC files accepted. Playground controls for toggling format. |
formats-rejected.stories.tsx | 0003 Formats – Rejected | BMP, SVG, and other unsupported formats. Shows plain-language error messages. |
size-limit-exceeded.stories.tsx | 0003 Size – Limit Exceeded | File exceeds 10 MB. Shows auto-compression attempt and, if still too large, the size error message. |
size-auto-compressed.stories.tsx | 0003 Size – Auto Compressed | Oversized 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 File | Sidebar Title | Description |
|---|---|---|
url-valid-https.stories.tsx | 0004 URL – Valid HTTPS | Valid HTTPS URL passes scheme and reachability check. Shows success state. Playground exposes URL input for experimentation. |
url-rejected-scheme.stories.tsx | 0004 URL – Rejected Scheme | http:, javascript:, file: URLs rejected. Shows “Only secure web addresses (https) are accepted.” |
url-unreachable.stories.tsx | 0004 URL – Unreachable | HTTPS URL returns 404 or timeout. Shows “We couldn’t load an image from this address…” |
url-wrong-content-type.stories.tsx | 0004 URL – Wrong Content Type | URL points to non-image content. Shows “The link doesn’t point to a supported image type…“ |
0005.FS — Preview and Crop
Section titled “0005.FS — Preview and Crop”Preview, edit operations, comparison layout, and interaction states.
Framework: No — observation and interaction-focused stories. The play
function demonstrates the edit operation sequence.
| Story File | Sidebar Title | Description |
|---|---|---|
preview-new-image.stories.tsx | 0005 Preview – New Image | Image preview at 1:1 locked aspect ratio. No existing image (no comparison). Playground exposes aspectRatio control. |
preview-comparison-desktop.stories.tsx | 0005 Preview – Comparison Desktop | Replacing an existing image. Desktop layout: small reference + large preview. |
preview-comparison-mobile.stories.tsx | 0005 Preview – Comparison Mobile | Same replacement scenario at mobile viewport. Tabbed layout. |
edit-crop-zoom-rotate.stories.tsx | 0005 Edit – Crop Zoom Rotate | Demonstrates crop reposition, zoom in/out, rotate 90-degree increments, pan, and reset. Stepwise play function exercises each operation. |
preview-url-loading.stories.tsx | 0005 Preview – URL Loading | URL-sourced image with slow load. Shows shimmer placeholder, then loaded image. Timeout error on failure. |
interaction-states.stories.tsx | 0005 Interaction States | Playground showing all five states: View, EmptyImage, ProvidedImage, FailedValidation, Warn. Controls toggle between states. |
0006.FS — Confirm and Persist
Section titled “0006.FS — Confirm and Persist”Copyright acknowledgment and the confirm/cancel flow.
Framework: No — focused interaction demonstration.
| Story File | Sidebar Title | Description |
|---|---|---|
copyright-acknowledgment.stories.tsx | 0006 Copyright Acknowledgment | Shows the mandatory checkbox. Confirm button disabled until acknowledged. Playground exposes acknowledged state. |
confirm-managed-upload.stories.tsx | 0006 Confirm – Managed Upload | File input confirmed. Shows upload progress bar (presigned POST mock), success, and grid/form update. |
confirm-external-url.stories.tsx | 0006 Confirm – External URL | URL input confirmed. URL stored as-is (no upload). Entity display updates. |
cancel-no-change.stories.tsx | 0006 Cancel – No Change | User cancels. In-progress upload aborted. Entity unchanged. |
0007.FS — Grid Inline Edit Entry Point
Section titled “0007.FS — Grid Inline Edit Entry Point”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 File | Sidebar Title | Description |
|---|---|---|
grid-inline-edit-double-click.stories.tsx | 0007 Grid Edit – Double Click | Double-click on image cell opens ImageUploadDialog modal over grid. Confirm updates the row thumbnail. |
grid-inline-edit-enter-key.stories.tsx | 0007 Grid Edit – Enter Key | Select cell + Enter key triggers the same modal. Demonstrates keyboard accessibility. |
grid-inline-edit-from-inspector.stories.tsx | 0007 Grid Edit – From Inspector | Eye icon → ImageInspectorOverlay → Edit button → inspector closes → ImageUploadDialog opens. |
grid-inline-edit-cancel.stories.tsx | 0007 Grid Edit – Cancel | Modal opened, user cancels. Grid row unchanged. Overlay dismisses cleanly. |
0002 — Remove Entity Image
Section titled “0002 — Remove Entity Image”Directory: remove-entity-image/ | MDX: remove-entity-image.mdx
0001.UC — Remove Image
Section titled “0001.UC — Remove Image”Confirmation-based removal.
Framework: No — single confirmation dialog interaction.
| Story File | Sidebar Title | Description |
|---|---|---|
remove-from-form.stories.tsx | 0001 Remove – From Form | Trash icon on ImageFormField hover → confirmation dialog → confirm → placeholder shown. play function exercises the full sequence. |
remove-from-form-cancel.stories.tsx | 0001 Remove – Cancel | Same trigger, user clicks Cancel. Image unchanged. |
remove-playground.stories.tsx | 0001 Remove – Playground | Playground with controls: imageUrl (populated vs. null), entityTypeDisplayName. Reviewer can toggle states and trigger removal. |
0003 — View Entity Image
Section titled “0003 — View Entity Image”Directory: view-entity-image/ | MDX: view-entity-image.mdx
0001.UC — View Image in Grid
Section titled “0001.UC — View Image in Grid”Grid thumbnail rendering.
Framework: No — observation story. The play function verifies thumbnails
render in grid cells.
| Story File | Sidebar Title | Description |
|---|---|---|
view-grid-thumbnails.stories.tsx | 0001 View – Grid Thumbnails | AG Grid with ImageCellDisplay rendering thumbnails. Mix of loaded images, null (placeholder), and error states across rows. |
view-grid-hover-preview.stories.tsx | 0001 View – Hover Preview | Hover over a thumbnail (~500ms) shows ImageHoverPreview popover with larger image. play function hovers and verifies popover appears. |
view-grid-playground.stories.tsx | 0001 View – Playground | Playground with controls for imageUrl, entityTypeDisplayName, cell dimensions. Reviewer experiments with different image states. |
0002.FS — Image Inspector Overlay
Section titled “0002.FS — Image Inspector Overlay”Full-size image inspection modal.
Framework: No — click-to-inspect interaction.
| Story File | Sidebar Title | Description |
|---|---|---|
inspector-open-close.stories.tsx | 0002 Inspector – Open and Close | Click 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.tsx | 0002 Inspector – Edit Transition | Inspector with onEdit callback. Edit button visible. Click Edit → inspector closes → ImageUploadDialog opens. |
inspector-no-edit.stories.tsx | 0002 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 File | Sidebar Title | Description |
|---|---|---|
fallback-error-badge.stories.tsx | 0003 Fallback – Error Badge | Unreachable image URL. Placeholder with error badge overlay. Inspector click suppressed. |
fallback-loading-shimmer.stories.tsx | 0003 Fallback – Loading Shimmer | Slow-loading image. Shimmer placeholder transitions to loaded thumbnail without reflow. |
fallback-no-image.stories.tsx | 0003 Fallback – No Image | imageUrl is null. Initials placeholder without error badge. Distinguishes “no image” from “broken image”. |
fallback-playground.stories.tsx | 0003 Fallback – Playground | Playground with controls: imageUrl (valid, broken, null), entityTypeDisplayName, loading delay. |
REF — Reference Data
Section titled “REF — Reference Data”ITM — Items
Section titled “ITM — Items”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).
Reference Story (vendored)
Section titled “Reference Story (vendored)”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 File | Sidebar Title | Description |
|---|---|---|
set-image-during-creation.stories.tsx | 0010 Set Image During Creation | Full-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). |
Canary Story
Section titled “Canary Story”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 File | Sidebar Title | Description |
|---|---|---|
set-image-during-creation-canary.stories.tsx | 0010 Set Image During Creation – Canary | Full-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. |
Scenes (Canary Story)
Section titled “Scenes (Canary Story)”- Add Item form opens (empty) — simplified form with Title, SKU, Image, Publish button
- User fills Title field
- User fills SKU
- User clicks the image placeholder —
ImageUploadDialogopens - User picks a file — preview shown with 1:1 crop
- User adjusts crop
- User acknowledges copyright
- User confirms — dialog closes, image thumbnail appears in form
- User clicks Publish — item created with image
- 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 File | Sidebar Title | Description |
|---|---|---|
change-item-image.stories.tsx | 0006 Change Item Image | Full-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.tsx | 0006 Remove Item Image | Full-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. |
Story Count Summary
Section titled “Story Count Summary”| Use Case | Scenarios | Story Files | Framework Stories | Standard Stories |
|---|---|---|---|---|
| GEN-MEDIA-0001 Set Entity Image | 7 | 30 | 3 (0001.UC) | 27 |
| GEN-MEDIA-0002 Remove Entity Image | 1 | 3 | 0 | 3 |
| GEN-MEDIA-0003 View Entity Image | 3 | 10 | 0 | 10 |
| REF-ITM-0003-0010 Set Image During Creation | 1 | 2 | 1 | 1 |
| REF-ITM-0004-0006 Change or Remove Item Image | 1 | 2 | 0 | 2 |
| Total | 13 | 47 | 4 | 43 |
Plus description MDX files: 1 section (Entity Media) + 3 use case level +
13 scenario level = 17 description.mdx files.
Directory Structure
Section titled “Directory Structure”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)Implementation Waves
Section titled “Implementation Waves”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-0003View 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.FSInput Detection and Routing — all 8 storiesGEN-MEDIA-0001::0003.FSAccepted Formats and Size Limits — all 4 storiesGEN-MEDIA-0001::0004.FSURL 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.FSPreview and Crop — all 6 storiesGEN-MEDIA-0001::0006.FSConfirm 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.UCSet Image via Unified Input Surface — 3 framework storiesGEN-MEDIA-0001::0007.FSGrid Inline Edit Entry Point — all 4 storiesGEN-MEDIA-0002::0001.UCRemove Image — all 3 storiesREF-ITM-0003::0010.UCSet Item Image During Creation — 1 framework storyREF-ITM-0004::0006.UCChange 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.
Wave Gate Protocol
Section titled “Wave Gate Protocol”At the end of each wave:
npm run lintpasses.npx tsc --noEmitpasses.npm run build-storybookcompletes without errors.- All unit tests pass (
npm run test). - All new story
playfunctions pass in the Storybook test runner. - VRT check. Run VRT tests (
npx playwright test --project=vrt). TheImageCellDisplayswap and new hover/action affordances will cause visual diffs in existing stories that renderItemGrid. 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. - Changes are committed locally before proceeding to the next wave.
Component–Story Traceability
Section titled “Component–Story Traceability”| Component | Stories That Compose It |
|---|---|
ImageDisplay | All GEN-MEDIA-0003 stories, preview-*, fallback-*, inspector-* |
ImageCellDisplay | view-grid-*, grid-inline-edit-*, change-item-image, fallback-* |
ImageCellEditor | grid-inline-edit-*, change-item-image |
ImageHoverPreview | view-grid-hover-preview, remove-from-form* |
ImageInspectorOverlay | inspector-*, grid-inline-edit-from-inspector |
ImageFormField | set-image-during-creation, remove-from-form*, remove-item-image |
ImageDropZone | input-*, set-image-happy-path, set-image-replace-existing |
ImagePreviewEditor | preview-*, edit-crop-zoom-rotate, set-image-* |
ImageComparisonLayout | preview-comparison-*, set-image-replace-existing, change-item-image |
CopyrightAcknowledgment | copyright-acknowledgment, confirm-*, set-image-happy-path |
ImageUploadDialog | set-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 |
Out of Scope
Section titled “Out of Scope”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
Copyright: © Arda Systems 2025-2026, All rights reserved