import type { Meta, StoryObj } from '@storybook/nextjs-vite'; import { useState } from 'react'; import { ImageUploader } from '../components/ui/image-uploader'; const SAMPLE_IMAGE = 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=1200&q=80'; const meta = { title: 'UI/ImageUploader', component: ImageUploader, parameters: { layout: 'padded', docs: { description: { component: 'Image dropzone with preview, replace, and remove actions. Decoupled from upload logic — the consumer handles uploading via `onChange(file)`.', }, }, }, tags: ['autodocs'], decorators: [ (Story) => (
), ], } satisfies Meta; export default meta; type Story = StoryObj; export const Default: Story = { args: { onChange: () => {}, }, }; export const AllStates: Story = { args: { onChange: () => {}, }, parameters: { docs: { description: { story: 'All four states from Figma (default, hover, action, uploaded). Hover/action states are static visual representations driven by CSS classes — interact with the Default story to see real hover/drag behavior.', }, }, }, decorators: [ (Story) => (
), ], render: () => (

default

{}} />

hover

{}} />

action

{}} />

uploaded

{}} onRemove={() => {}} />
), }; export const WithFieldLabel: Story = { args: { fieldLabel: 'Cover image', onChange: () => {}, }, }; export const WithPreview: Story = { args: { value: SAMPLE_IMAGE, onChange: () => {}, onRemove: () => {}, }, }; export const PreviewWithoutRemove: Story = { args: { value: SAMPLE_IMAGE, onChange: () => {}, }, }; export const SquareAspectRatio: Story = { args: { value: SAMPLE_IMAGE, aspectRatio: '1 / 1', onChange: () => {}, onRemove: () => {}, }, decorators: [ (Story) => (
), ], }; export const ObjectFitContain: Story = { args: { value: SAMPLE_IMAGE, objectFit: 'contain', onChange: () => {}, onRemove: () => {}, }, }; export const Loading: Story = { args: { value: SAMPLE_IMAGE, loading: true, onChange: () => {}, onRemove: () => {}, }, }; export const LoadingEmpty: Story = { args: { loading: true, onChange: () => {}, }, }; export const Disabled: Story = { args: { disabled: true, onChange: () => {}, }, }; export const DisabledWithPreview: Story = { args: { value: SAMPLE_IMAGE, disabled: true, onChange: () => {}, onRemove: () => {}, }, }; export const WithError: Story = { args: { fieldLabel: 'Cover image', error: 'Image is required', onChange: () => {}, }, }; export const CustomCopy: Story = { args: { label: 'Drop your logo here', description: 'PNG or SVG, up to 2MB', accept: 'image/png,image/svg+xml', maxSize: 2 * 1024 * 1024, onChange: () => {}, }, }; export const Interactive: Story = { args: { onChange: () => {}, }, render: function InteractiveImageUploader() { const [preview, setPreview] = useState(); const [fileName, setFileName] = useState(); const handleChange = (file: File) => { setFileName(file.name); const reader = new FileReader(); reader.onload = () => setPreview(reader.result as string); reader.readAsDataURL(file); }; const handleRemove = () => { setPreview(undefined); setFileName(undefined); }; return (

{fileName ? `Selected: ${fileName}` : 'No image selected'}

); }, };