import type { Meta, StoryObj } from "@storybook/react"; import { useState } from "react"; import { FileUploadField } from "./FileUploadField"; import type { UploadedFile } from "../types/fields"; const meta: Meta = { title: "Widgets/Onboarding/Fields/FileUploadField", component: FileUploadField, parameters: { layout: "centered", docs: { description: { component: ` File upload field for collecting documents and images in onboarding flows. Features: - **Drag & Drop** - Drop zone with customizable text - **File Constraints** - Accept types, max size, max files - **Progress Tracking** - Upload progress per file - **Error Handling** - Per-file and field-level errors \`\`\`tsx import { FileUploadField } from '@mdxui/widgets' \`\`\` `, }, }, }, tags: ["autodocs"], argTypes: { required: { control: "boolean" }, disabled: { control: "boolean" }, showFileSize: { control: "boolean" }, maxFiles: { control: "number", description: "Maximum number of files" }, accept: { control: "text", description: "Accepted file types (e.g., '.pdf,.doc')" }, }, decorators: [ (Story) => (
), ], }; export default meta; type Story = StoryObj; // Helper to create mock files function createMockFile(name: string, _size: number, type: string): File { const blob = new Blob([""], { type }); return new File([blob], name, { type }); } // Sample uploaded files const sampleFiles: UploadedFile[] = [ { id: "1", file: createMockFile("document.pdf", 1024 * 500, "application/pdf"), progress: 100, status: "success", }, { id: "2", file: createMockFile("image.png", 1024 * 200, "image/png"), preview: "https://via.placeholder.com/100", progress: 100, status: "success", }, ]; // Wrapper to handle controlled state function FileUploadFieldWithState( props: Omit, "onChange" | "onRemove">, ) { const [files, setFiles] = useState(props.files || []); const handleChange = (fileList: FileList) => { const newFiles: UploadedFile[] = Array.from(fileList).map((file, index) => ({ id: `${Date.now()}-${index}`, file, progress: 100, status: "success" as const, })); setFiles((prev) => [...prev, ...newFiles]); }; const handleRemove = (fileId: string) => { setFiles((prev) => prev.filter((f) => f.id !== fileId)); }; return ( ); } /** * Default file upload with drag & drop zone. * Use Controls to adjust: accept, maxFiles, maxSize, required, disabled */ export const Default: Story = { render: (args) => , args: { id: "files", label: "Upload Files", description: "Drag and drop files here or click to browse", files: [], }, }; /** * Shows uploaded files with progress and status. * Demonstrates file list display and removal. */ export const WithFiles: Story = { name: "With Uploaded Files", render: (args) => , args: { id: "files", label: "Uploaded Files", maxFiles: 5, files: sampleFiles, }, }; /** * Displays error states for failed uploads. * Shows field-level and per-file error messages. */ export const WithError: Story = { name: "With Error", args: { id: "files", label: "Upload Files", files: [ { id: "error", file: createMockFile("corrupt.zip", 1024 * 100, "application/zip"), progress: 0, status: "error", error: "File is corrupted or invalid", }, ], error: "Please upload valid files", onChange: () => {}, onRemove: () => {}, }, };