Storybook stories for the `ImageUploader` UI component, covering all visual states, prop variants, and an interactive upload simulation. ## Key Components | Export | Description | |--------|-------------| | `Default` | Bare uploader with no initial value | | `AllStates` | Side-by-side grid of all four Figma states: default, hover, action, uploaded | | `WithFieldLabel` | Uploader with an accessible label above the dropzone | | `WithPreview` | Pre-populated image preview with remove action | | `PreviewWithoutRemove` | Preview state with `onRemove` omitted | | `SquareAspectRatio` | Custom `1 / 1` aspect ratio at 320px width | | `ObjectFitContain` | Image preview using `contain` fit instead of the default `cover` | | `Loading` | Spinner overlay on an existing preview | | `LoadingEmpty` | Spinner overlay on the empty dropzone | | `Disabled` / `DisabledWithPreview` | Non-interactive states | | `WithError` | Field-level validation error message | | `CustomCopy` | Custom label, description, accepted MIME types, and max file size | | `Interactive` | Fully wired story using `useState` + `FileReader` to simulate real upload flow | ## Usage Example ```typescript // Interactive story pattern — wire up local state to preview uploads import { useState } from 'react'; import { ImageUploader } from '../components/ui/image-uploader'; function Demo() { const [preview, setPreview] = useState(); const handleChange = (file: File) => { const reader = new FileReader(); reader.onload = () => setPreview(reader.result as string); reader.readAsDataURL(file); }; return ( setPreview(undefined)} fieldLabel="Cover image" /> ); } ``` > `ImageUploader` is intentionally decoupled from upload logic — it calls `onChange(file)` and the consumer owns the upload, preview URL management, and remove behaviour.