Skip to content

Commit 0fc1a91

Browse files
feat(site): support zip upload for template files (#12323)
Related to #11687
1 parent 0f9c142 commit 0fc1a91

File tree

3 files changed

+94
-40
lines changed

3 files changed

+94
-40
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { fireEvent, render, screen } from "@testing-library/react";
2+
import { FileUpload } from "./FileUpload";
3+
import { ThemeProvider } from "contexts/ThemeProvider";
4+
5+
test("accepts files with the correct extension", async () => {
6+
const onUpload = jest.fn();
7+
8+
render(
9+
<ThemeProvider>
10+
<FileUpload
11+
isUploading={false}
12+
onUpload={onUpload}
13+
removeLabel="Remove file"
14+
title="Upload file"
15+
extensions={["tar", "zip"]}
16+
/>
17+
</ThemeProvider>,
18+
);
19+
20+
const dropZone = screen.getByTestId("drop-zone");
21+
22+
const tarFile = new File([""], "file.tar");
23+
fireEvent.drop(dropZone, {
24+
dataTransfer: { files: [tarFile] },
25+
});
26+
expect(onUpload).toBeCalledWith(tarFile);
27+
onUpload.mockClear();
28+
29+
const zipFile = new File([""], "file.zip");
30+
fireEvent.drop(dropZone, {
31+
dataTransfer: { files: [zipFile] },
32+
});
33+
expect(onUpload).toBeCalledWith(zipFile);
34+
onUpload.mockClear();
35+
36+
const unsupportedFile = new File([""], "file.mp4");
37+
fireEvent.drop(dropZone, {
38+
dataTransfer: { files: [unsupportedFile] },
39+
});
40+
expect(onUpload).not.toHaveBeenCalled();
41+
});

site/src/components/FileUpload/FileUpload.tsx

+51-37
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,6 @@ import RemoveIcon from "@mui/icons-material/DeleteOutline";
88
import FileIcon from "@mui/icons-material/FolderOutlined";
99
import { css, type Interpolation, type Theme } from "@emotion/react";
1010

11-
const useFileDrop = (
12-
callback: (file: File) => void,
13-
fileTypeRequired?: string,
14-
): {
15-
onDragOver: (e: DragEvent<HTMLDivElement>) => void;
16-
onDrop: (e: DragEvent<HTMLDivElement>) => void;
17-
} => {
18-
const onDragOver = (e: DragEvent<HTMLDivElement>) => {
19-
e.preventDefault();
20-
};
21-
22-
const onDrop = (e: DragEvent<HTMLDivElement>) => {
23-
e.preventDefault();
24-
const file = e.dataTransfer.files[0];
25-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- file can be undefined
26-
if (!file) {
27-
return;
28-
}
29-
if (fileTypeRequired && file.type !== fileTypeRequired) {
30-
return;
31-
}
32-
callback(file);
33-
};
34-
35-
return {
36-
onDragOver,
37-
onDrop,
38-
};
39-
};
40-
4111
export interface FileUploadProps {
4212
isUploading: boolean;
4313
onUpload: (file: File) => void;
@@ -46,8 +16,7 @@ export interface FileUploadProps {
4616
removeLabel: string;
4717
title: string;
4818
description?: ReactNode;
49-
extension?: string;
50-
fileTypeRequired?: string;
19+
extensions?: string[];
5120
}
5221

5322
export const FileUpload: FC<FileUploadProps> = ({
@@ -58,10 +27,9 @@ export const FileUpload: FC<FileUploadProps> = ({
5827
removeLabel,
5928
title,
6029
description,
61-
extension,
62-
fileTypeRequired,
30+
extensions,
6331
}) => {
64-
const tarDrop = useFileDrop(onUpload, fileTypeRequired);
32+
const fileDrop = useFileDrop(onUpload, extensions);
6533
const inputRef = useRef<HTMLInputElement>(null);
6634
const clickable = useClickable<HTMLDivElement>(
6735
() => inputRef.current?.click(),
@@ -90,9 +58,10 @@ export const FileUpload: FC<FileUploadProps> = ({
9058
return (
9159
<>
9260
<div
61+
data-testid="drop-zone"
9362
css={[styles.root, isUploading && styles.disabled]}
9463
{...clickable}
95-
{...tarDrop}
64+
{...fileDrop}
9665
>
9766
<Stack alignItems="center" spacing={1}>
9867
{isUploading ? (
@@ -113,7 +82,7 @@ export const FileUpload: FC<FileUploadProps> = ({
11382
data-testid="file-upload"
11483
ref={inputRef}
11584
css={styles.input}
116-
accept={extension}
85+
accept={extensions?.map((ext) => `.${ext}`).join(",")}
11786
onChange={(event) => {
11887
const file = event.currentTarget.files?.[0];
11988
if (file) {
@@ -125,6 +94,47 @@ export const FileUpload: FC<FileUploadProps> = ({
12594
);
12695
};
12796

97+
const useFileDrop = (
98+
callback: (file: File) => void,
99+
extensions?: string[],
100+
): {
101+
onDragOver: (e: DragEvent<HTMLDivElement>) => void;
102+
onDrop: (e: DragEvent<HTMLDivElement>) => void;
103+
} => {
104+
const onDragOver = (e: DragEvent<HTMLDivElement>) => {
105+
e.preventDefault();
106+
};
107+
108+
const onDrop = (e: DragEvent<HTMLDivElement>) => {
109+
e.preventDefault();
110+
const file = e.dataTransfer.files[0] as File | undefined;
111+
112+
if (!file) {
113+
return;
114+
}
115+
116+
if (!extensions) {
117+
callback(file);
118+
return;
119+
}
120+
121+
const extension = file.name.split(".").pop();
122+
123+
if (!extension) {
124+
throw new Error(`File has no extension to compare with ${extensions}`);
125+
}
126+
127+
if (extensions.includes(extension)) {
128+
callback(file);
129+
}
130+
};
131+
132+
return {
133+
onDragOver,
134+
onDrop,
135+
};
136+
};
137+
128138
const styles = {
129139
root: (theme) => css`
130140
display: flex;
@@ -151,12 +161,16 @@ const styles = {
151161

152162
title: {
153163
fontSize: 16,
164+
lineHeight: "1",
154165
},
155166

156167
description: (theme) => ({
157168
color: theme.palette.text.secondary,
158169
textAlign: "center",
159170
maxWidth: 400,
171+
fontSize: 14,
172+
lineHeight: "1.5",
173+
marginTop: 4,
160174
}),
161175

162176
input: {

site/src/pages/CreateTemplatePage/TemplateUpload.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const TemplateUpload: FC<TemplateUploadProps> = ({
1818
}) => {
1919
const description = (
2020
<>
21-
The template has to be a .tar file. You can also use our{" "}
21+
The template has to be a .tar or .zip file. You can also use our{" "}
2222
<Link
2323
component={RouterLink}
2424
to="/starter-templates"
@@ -42,8 +42,7 @@ export const TemplateUpload: FC<TemplateUploadProps> = ({
4242
removeLabel="Remove file"
4343
title="Upload template"
4444
description={description}
45-
extension=".tar"
46-
fileTypeRequired="application/x-tar"
45+
extensions={["tar", "zip"]}
4746
/>
4847
);
4948
};

0 commit comments

Comments
 (0)