Skip to content

Commit cdb6938

Browse files
authored
FileMultiUpload component (#642)
1 parent 9d9b83e commit cdb6938

File tree

5 files changed

+768
-5
lines changed

5 files changed

+768
-5
lines changed
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
import { StoryFn } from "@storybook/react";
2+
import { useState } from "react";
3+
import {
4+
FileMultiUpload,
5+
FileUploadItem,
6+
} from "@/components/FileUpload/FileMultiUpload.tsx";
7+
8+
export default {
9+
component: FileMultiUpload,
10+
title: "Forms/FileMultiUpload",
11+
tags: ["file-upload", "multi-upload", "autodocs"],
12+
decorators: [
13+
(Story: StoryFn) => (
14+
<div style={{ width: "800px" }}>
15+
<Story />
16+
</div>
17+
),
18+
],
19+
argTypes: {
20+
supportedFileTypes: {
21+
control: "array",
22+
},
23+
onFileSelect: { action: "file selected" },
24+
onFileRetry: { action: "retry requested" },
25+
onFileRemove: { action: "file removed" },
26+
},
27+
};
28+
29+
export const EmptyState = {
30+
args: {
31+
title: "Upload multiple files",
32+
supportedFileTypes: [".txt", ".csv", ".json", ".sql"],
33+
files: [],
34+
onFileSelect: (file: File) => console.log("File selected:", file.name),
35+
onFileRetry: (fileId: string) => console.log("Retry file:", fileId),
36+
onFileRemove: (fileId: string) => console.log("Remove file:", fileId),
37+
},
38+
parameters: {
39+
docs: {
40+
description: {
41+
story: "Shows the `FileMultiUpload` component with no files uploaded",
42+
},
43+
},
44+
},
45+
};
46+
47+
export const WithUploadingFiles = {
48+
args: {
49+
title: "Upload multiple files",
50+
supportedFileTypes: [".txt", ".csv", ".json", ".sql"],
51+
files: [
52+
{
53+
id: "1",
54+
name: "document1.txt",
55+
size: 1024,
56+
status: "uploading" as const,
57+
progress: 45,
58+
},
59+
{
60+
id: "2",
61+
name: "spreadsheet.csv",
62+
size: 2048,
63+
status: "uploading" as const,
64+
progress: 75,
65+
},
66+
],
67+
onFileSelect: (file: File) => console.log("File selected:", file.name),
68+
onFileRetry: (fileId: string) => console.log("Retry file:", fileId),
69+
onFileRemove: (fileId: string) => console.log("Remove file:", fileId),
70+
},
71+
parameters: {
72+
docs: {
73+
description: {
74+
story: "Shows the `FileMultiUpload` component with files currently uploading",
75+
},
76+
},
77+
},
78+
};
79+
80+
export const WithSuccessFiles = {
81+
args: {
82+
title: "Upload multiple files",
83+
supportedFileTypes: [".txt", ".csv", ".json", ".sql"],
84+
files: [
85+
{
86+
id: "1",
87+
name: "document1.txt",
88+
size: 1024,
89+
status: "success" as const,
90+
progress: 100,
91+
},
92+
{
93+
id: "2",
94+
name: "spreadsheet.csv",
95+
size: 2048,
96+
status: "success" as const,
97+
progress: 100,
98+
},
99+
{
100+
id: "3",
101+
name: "config.json",
102+
size: 512,
103+
status: "success" as const,
104+
progress: 100,
105+
},
106+
],
107+
onFileSelect: (file: File) => console.log("File selected:", file.name),
108+
onFileRetry: (fileId: string) => console.log("Retry file:", fileId),
109+
onFileRemove: (fileId: string) => console.log("Remove file:", fileId),
110+
},
111+
parameters: {
112+
docs: {
113+
description: {
114+
story: "Shows the `FileMultiUpload` component with successfully uploaded files",
115+
},
116+
},
117+
},
118+
};
119+
120+
export const WithErrorFiles = {
121+
args: {
122+
title: "Upload multiple files",
123+
supportedFileTypes: [".txt", ".csv", ".json", ".sql"],
124+
files: [
125+
{
126+
id: "1",
127+
name: "document1.txt",
128+
size: 1024,
129+
status: "error" as const,
130+
progress: 0,
131+
errorMessage: "Upload failed",
132+
},
133+
{
134+
id: "2",
135+
name: "large-file.csv",
136+
size: 5242880,
137+
status: "error" as const,
138+
progress: 0,
139+
errorMessage: "File too large",
140+
},
141+
],
142+
onFileSelect: (file: File) => console.log("File selected:", file.name),
143+
onFileRetry: (fileId: string) => console.log("Retry file:", fileId),
144+
onFileRemove: (fileId: string) => console.log("Remove file:", fileId),
145+
},
146+
parameters: {
147+
docs: {
148+
description: {
149+
story: "Shows the `FileMultiUpload` component with files that failed to upload",
150+
},
151+
},
152+
},
153+
};
154+
155+
export const MixedStates = {
156+
args: {
157+
title: "Upload multiple files",
158+
supportedFileTypes: [".txt", ".csv", ".json", ".sql"],
159+
files: [
160+
{
161+
id: "1",
162+
name: "document1.txt",
163+
size: 1024,
164+
status: "success" as const,
165+
progress: 100,
166+
},
167+
{
168+
id: "2",
169+
name: "uploading-file.csv",
170+
size: 2048,
171+
status: "uploading" as const,
172+
progress: 65,
173+
},
174+
{
175+
id: "3",
176+
name: "failed-file.json",
177+
size: 512,
178+
status: "error" as const,
179+
progress: 0,
180+
errorMessage: "Network error",
181+
},
182+
{
183+
id: "4",
184+
name: "another-success.sql",
185+
size: 1536,
186+
status: "success" as const,
187+
progress: 100,
188+
},
189+
],
190+
onFileSelect: (file: File) => console.log("File selected:", file.name),
191+
onFileRetry: (fileId: string) => console.log("Retry file:", fileId),
192+
onFileRemove: (fileId: string) => console.log("Remove file:", fileId),
193+
},
194+
parameters: {
195+
docs: {
196+
description: {
197+
story:
198+
"Shows the `FileMultiUpload` component with files in various states (success, uploading, error)",
199+
},
200+
},
201+
},
202+
};
203+
204+
// Interactive example that demonstrates state management
205+
export const Interactive: StoryFn = () => {
206+
const [files, setFiles] = useState<FileUploadItem[]>([]);
207+
208+
const handleFileSelect = (file: File) => {
209+
const newFile: FileUploadItem = {
210+
id: Date.now().toString(),
211+
name: file.name,
212+
size: file.size,
213+
status: "uploading",
214+
progress: 0,
215+
};
216+
217+
setFiles(prev => [...prev, newFile]);
218+
219+
// Simulate upload progress
220+
const interval = setInterval(() => {
221+
setFiles(prev =>
222+
prev.map(f => {
223+
if (f.id === newFile.id && f.status === "uploading") {
224+
const newProgress = Math.min(f.progress + 10, 100);
225+
return {
226+
...f,
227+
progress: newProgress,
228+
status: newProgress === 100 ? "success" : "uploading",
229+
};
230+
}
231+
return f;
232+
})
233+
);
234+
}, 200);
235+
236+
setTimeout(() => clearInterval(interval), 2000);
237+
};
238+
239+
const handleFileRetry = (fileId: string) => {
240+
setFiles(prev =>
241+
prev.map(f => (f.id === fileId ? { ...f, status: "uploading", progress: 0 } : f))
242+
);
243+
244+
// Simulate retry with potential failure
245+
setTimeout(() => {
246+
setFiles(prev =>
247+
prev.map(f =>
248+
f.id === fileId
249+
? {
250+
...f,
251+
status: Math.random() > 0.5 ? "success" : "error",
252+
progress: Math.random() > 0.5 ? 100 : 0,
253+
errorMessage: Math.random() > 0.5 ? undefined : "Retry failed",
254+
}
255+
: f
256+
)
257+
);
258+
}, 1000);
259+
};
260+
261+
const handleFileRemove = (fileId: string) => {
262+
setFiles(prev => prev.filter(f => f.id !== fileId));
263+
};
264+
265+
return (
266+
<FileMultiUpload
267+
title="Upload multiple files"
268+
supportedFileTypes={[".txt", ".csv", ".json", ".sql"]}
269+
files={files}
270+
onFileSelect={handleFileSelect}
271+
onFileRetry={handleFileRetry}
272+
onFileRemove={handleFileRemove}
273+
/>
274+
);
275+
};
276+
277+
Interactive.parameters = {
278+
docs: {
279+
description: {
280+
story:
281+
"Interactive example that simulates file upload behavior with progress and state management",
282+
},
283+
},
284+
};

0 commit comments

Comments
 (0)