@gradio/uploadbutton 0.9.18 → 0.9.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/Index.svelte +1 -7
- package/UploadButton.test.ts +410 -53
- package/dist/Index.svelte +1 -7
- package/dist/shared/UploadButton.svelte +11 -3
- package/package.json +6 -6
- package/shared/UploadButton.svelte +11 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# @gradio/uploadbutton
|
|
2
2
|
|
|
3
|
+
## 0.9.19
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
|
|
7
|
+
- [#13336](https://github.com/gradio-app/gradio/pull/13336) [`14c8870`](https://github.com/gradio-app/gradio/commit/14c88701756c5fa57e8eaeac4df1385b9601da45) - Add UploadButton unit tests. Thanks @freddyaboulton!
|
|
8
|
+
|
|
9
|
+
### Dependency updates
|
|
10
|
+
|
|
11
|
+
- @gradio/client@2.2.1
|
|
12
|
+
- @gradio/upload@0.17.10
|
|
13
|
+
|
|
14
|
+
## 0.9.18
|
|
15
|
+
|
|
16
|
+
### Dependency updates
|
|
17
|
+
|
|
18
|
+
- @gradio/upload@0.17.9
|
|
19
|
+
|
|
20
|
+
## 0.9.18
|
|
21
|
+
|
|
22
|
+
### Dependency updates
|
|
23
|
+
|
|
24
|
+
- @gradio/client@2.2.0
|
|
25
|
+
|
|
26
|
+
## 0.9.18
|
|
27
|
+
|
|
28
|
+
### Dependency updates
|
|
29
|
+
|
|
30
|
+
- @gradio/utils@0.12.2
|
|
31
|
+
- @gradio/upload@0.17.8
|
|
32
|
+
|
|
33
|
+
## 0.9.18
|
|
34
|
+
|
|
35
|
+
### Dependency updates
|
|
36
|
+
|
|
37
|
+
- @gradio/utils@0.12.1
|
|
38
|
+
- @gradio/button@0.6.6
|
|
39
|
+
|
|
3
40
|
## 0.9.18
|
|
4
41
|
|
|
5
42
|
### Dependency updates
|
package/Index.svelte
CHANGED
|
@@ -11,13 +11,7 @@
|
|
|
11
11
|
const props = $props();
|
|
12
12
|
const gradio = new Gradio<UploadButtonEvents, UploadButtonProps>(props);
|
|
13
13
|
|
|
14
|
-
let value = $
|
|
15
|
-
|
|
16
|
-
$effect(() => {
|
|
17
|
-
if (value !== gradio.props.value) {
|
|
18
|
-
gradio.props.value = value;
|
|
19
|
-
}
|
|
20
|
-
});
|
|
14
|
+
let value = $derived(gradio.props.value);
|
|
21
15
|
|
|
22
16
|
async function handle_event(
|
|
23
17
|
detail: null | FileData | FileData[],
|
package/UploadButton.test.ts
CHANGED
|
@@ -1,79 +1,436 @@
|
|
|
1
|
-
import { test, describe, expect, vi
|
|
2
|
-
import {
|
|
1
|
+
import { test, describe, afterEach, expect, vi } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
cleanup,
|
|
4
|
+
render,
|
|
5
|
+
waitFor,
|
|
6
|
+
upload_file,
|
|
7
|
+
mock_client,
|
|
8
|
+
TEST_TXT,
|
|
9
|
+
TEST_JPG
|
|
10
|
+
} from "@self/tootils/render";
|
|
11
|
+
import { run_shared_prop_tests } from "@self/tootils/shared-prop-tests";
|
|
3
12
|
import event from "@testing-library/user-event";
|
|
4
|
-
|
|
13
|
+
|
|
5
14
|
import UploadButton from "./Index.svelte";
|
|
6
15
|
|
|
16
|
+
const base_props = {
|
|
17
|
+
label: "Upload",
|
|
18
|
+
show_label: true,
|
|
19
|
+
interactive: true,
|
|
20
|
+
value: null as any,
|
|
21
|
+
file_count: "single" as "single" | "multiple" | "directory",
|
|
22
|
+
file_types: [] as string[],
|
|
23
|
+
size: "lg" as "sm" | "md" | "lg",
|
|
24
|
+
icon: null as any,
|
|
25
|
+
variant: "secondary" as "primary" | "secondary" | "stop"
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const upload_props = {
|
|
29
|
+
...base_props,
|
|
30
|
+
root: "https://example.com",
|
|
31
|
+
client: mock_client()
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
run_shared_prop_tests({
|
|
35
|
+
component: UploadButton,
|
|
36
|
+
name: "UploadButton",
|
|
37
|
+
base_props,
|
|
38
|
+
has_label: false,
|
|
39
|
+
has_validation_error: false,
|
|
40
|
+
has_block_wrapper: false,
|
|
41
|
+
visible_false_hides: true
|
|
42
|
+
});
|
|
43
|
+
|
|
7
44
|
describe("UploadButton", () => {
|
|
8
|
-
afterEach(() =>
|
|
9
|
-
|
|
10
|
-
|
|
45
|
+
afterEach(() => cleanup());
|
|
46
|
+
|
|
47
|
+
test("renders a button with the label text", async () => {
|
|
48
|
+
const { getByRole } = await render(UploadButton, {
|
|
49
|
+
...base_props,
|
|
50
|
+
label: "Upload a File"
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
expect(getByRole("button", { name: "Upload a File" })).toBeVisible();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("button is enabled when interactive is true", async () => {
|
|
57
|
+
const { getByRole } = await render(UploadButton, {
|
|
58
|
+
...base_props,
|
|
59
|
+
interactive: true
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
expect(getByRole("button", { name: "Upload" })).toBeEnabled();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("button is disabled when interactive is false", async () => {
|
|
66
|
+
const { getByRole } = await render(UploadButton, {
|
|
67
|
+
...base_props,
|
|
68
|
+
interactive: false
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
expect(getByRole("button", { name: "Upload" })).toBeDisabled();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("Props: file_count", () => {
|
|
76
|
+
afterEach(() => cleanup());
|
|
77
|
+
|
|
78
|
+
test("file_count='single' returns a single FileData, not an array", async () => {
|
|
79
|
+
const { get_data } = await render(UploadButton, {
|
|
80
|
+
...upload_props,
|
|
81
|
+
file_count: "single"
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
await upload_file(TEST_TXT);
|
|
85
|
+
|
|
86
|
+
await waitFor(async () => {
|
|
87
|
+
const data = await get_data();
|
|
88
|
+
expect(data.value).not.toBeNull();
|
|
89
|
+
});
|
|
90
|
+
const data = await get_data();
|
|
91
|
+
expect(Array.isArray(data.value)).toBe(false);
|
|
92
|
+
expect(data.value).toHaveProperty("orig_name");
|
|
11
93
|
});
|
|
12
94
|
|
|
13
|
-
test
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
95
|
+
test("file_count='multiple' returns an array of FileData", async () => {
|
|
96
|
+
const { get_data } = await render(UploadButton, {
|
|
97
|
+
...upload_props,
|
|
98
|
+
file_count: "multiple"
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
await upload_file([TEST_TXT, TEST_JPG]);
|
|
102
|
+
|
|
103
|
+
await waitFor(async () => {
|
|
104
|
+
const data = await get_data();
|
|
105
|
+
expect(data.value).not.toBeNull();
|
|
106
|
+
});
|
|
107
|
+
const data = await get_data();
|
|
108
|
+
expect(Array.isArray(data.value)).toBe(true);
|
|
109
|
+
expect(data.value).toHaveLength(2);
|
|
110
|
+
});
|
|
17
111
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
112
|
+
// Attribute assertion: directory upload can't be driven programmatically —
|
|
113
|
+
// webkitdirectory is the browser-native mechanism and there's no behavioural
|
|
114
|
+
// equivalent we can simulate in a unit test.
|
|
115
|
+
test("file_count='directory' sets webkitdirectory attribute on input", async () => {
|
|
116
|
+
const { getByTestId } = await render(UploadButton, {
|
|
117
|
+
...base_props,
|
|
118
|
+
file_count: "directory"
|
|
24
119
|
});
|
|
25
120
|
|
|
26
|
-
const
|
|
121
|
+
const input = getByTestId("Upload-upload-button");
|
|
122
|
+
expect(input).toHaveAttribute("webkitdirectory");
|
|
123
|
+
});
|
|
124
|
+
});
|
|
27
125
|
|
|
28
|
-
|
|
126
|
+
describe("Props: file_types", () => {
|
|
127
|
+
afterEach(() => cleanup());
|
|
29
128
|
|
|
129
|
+
test("file_types maps category names to wildcard MIME types", async () => {
|
|
30
130
|
const { getByTestId } = await render(UploadButton, {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
root: "http://localhost:7860",
|
|
34
|
-
file_count: "1"
|
|
131
|
+
...base_props,
|
|
132
|
+
file_types: ["image", "audio"]
|
|
35
133
|
});
|
|
36
134
|
|
|
37
|
-
const
|
|
135
|
+
const input = getByTestId("Upload-upload-button");
|
|
136
|
+
expect(input).toHaveAttribute("accept", "image/*, audio/*");
|
|
137
|
+
});
|
|
38
138
|
|
|
39
|
-
|
|
40
|
-
await
|
|
139
|
+
test("file_types passes dot-prefixed extensions through unchanged", async () => {
|
|
140
|
+
const { getByTestId } = await render(UploadButton, {
|
|
141
|
+
...base_props,
|
|
142
|
+
file_types: [".csv", ".json"]
|
|
143
|
+
});
|
|
41
144
|
|
|
42
|
-
|
|
145
|
+
const input = getByTestId("Upload-upload-button");
|
|
146
|
+
expect(input).toHaveAttribute("accept", ".csv, .json");
|
|
43
147
|
});
|
|
44
148
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
149
|
+
test("empty file_types array results in empty accept attribute", async () => {
|
|
150
|
+
const { getByTestId } = await render(UploadButton, {
|
|
151
|
+
...base_props,
|
|
152
|
+
file_types: []
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const input = getByTestId("Upload-upload-button");
|
|
156
|
+
expect(input).toHaveAttribute("accept", "");
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("rejects files that do not match file_types and dispatches error", async () => {
|
|
160
|
+
const { listen } = await render(UploadButton, {
|
|
161
|
+
...upload_props,
|
|
162
|
+
file_types: ["image"]
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const upload = listen("upload");
|
|
166
|
+
const error = listen("error");
|
|
167
|
+
|
|
168
|
+
await upload_file(TEST_TXT);
|
|
169
|
+
|
|
170
|
+
await waitFor(() => {
|
|
171
|
+
expect(error).toHaveBeenCalledTimes(1);
|
|
172
|
+
});
|
|
173
|
+
expect(error).toHaveBeenCalledWith(
|
|
174
|
+
expect.stringContaining("Invalid file type")
|
|
175
|
+
);
|
|
176
|
+
expect(upload).not.toHaveBeenCalled();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("accepts files matching a wildcard MIME category", async () => {
|
|
180
|
+
const { listen } = await render(UploadButton, {
|
|
181
|
+
...upload_props,
|
|
182
|
+
file_types: ["image"]
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const upload = listen("upload");
|
|
186
|
+
|
|
187
|
+
await upload_file(TEST_JPG);
|
|
188
|
+
|
|
189
|
+
await waitFor(() => {
|
|
190
|
+
expect(upload).toHaveBeenCalledTimes(1);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("accepts files matching a dot-prefixed extension", async () => {
|
|
195
|
+
const { listen } = await render(UploadButton, {
|
|
196
|
+
...upload_props,
|
|
197
|
+
file_types: [".txt"]
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const upload = listen("upload");
|
|
201
|
+
|
|
202
|
+
await upload_file(TEST_TXT);
|
|
203
|
+
|
|
204
|
+
await waitFor(() => {
|
|
205
|
+
expect(upload).toHaveBeenCalledTimes(1);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("multiple file_types each allow their matching files", async () => {
|
|
210
|
+
const { listen } = await render(UploadButton, {
|
|
211
|
+
...upload_props,
|
|
212
|
+
file_types: ["image", ".txt"]
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const upload = listen("upload");
|
|
216
|
+
|
|
217
|
+
await upload_file(TEST_JPG);
|
|
218
|
+
|
|
219
|
+
await waitFor(() => {
|
|
220
|
+
expect(upload).toHaveBeenCalledTimes(1);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
await upload_file(TEST_TXT);
|
|
224
|
+
|
|
225
|
+
await waitFor(() => {
|
|
226
|
+
expect(upload).toHaveBeenCalledTimes(2);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
49
229
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
upload_files: vi.fn((f) => new Promise((res) => res({})))
|
|
55
|
-
};
|
|
230
|
+
test("rejects file not matching any of multiple file_types", async () => {
|
|
231
|
+
const { listen } = await render(UploadButton, {
|
|
232
|
+
...upload_props,
|
|
233
|
+
file_types: ["image", "audio"]
|
|
56
234
|
});
|
|
57
235
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
236
|
+
const upload = listen("upload");
|
|
237
|
+
const error = listen("error");
|
|
238
|
+
|
|
239
|
+
await upload_file(TEST_TXT);
|
|
240
|
+
|
|
241
|
+
await waitFor(() => {
|
|
242
|
+
expect(error).toHaveBeenCalledTimes(1);
|
|
243
|
+
});
|
|
244
|
+
expect(upload).not.toHaveBeenCalled();
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
describe("Props: icon", () => {
|
|
249
|
+
afterEach(() => cleanup());
|
|
250
|
+
|
|
251
|
+
test("icon renders an image inside the button when provided", async () => {
|
|
252
|
+
const { getByRole } = await render(UploadButton, {
|
|
253
|
+
...base_props,
|
|
254
|
+
icon: {
|
|
255
|
+
path: "icon.png",
|
|
256
|
+
url: "https://example.com/icon.png",
|
|
257
|
+
orig_name: "icon.png",
|
|
258
|
+
size: 100,
|
|
259
|
+
mime_type: "image/png"
|
|
67
260
|
}
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
expect(getByRole("img")).toHaveAttribute(
|
|
264
|
+
"src",
|
|
265
|
+
"https://example.com/icon.png"
|
|
266
|
+
);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test("no icon rendered when icon is null", async () => {
|
|
270
|
+
const { queryByRole } = await render(UploadButton, {
|
|
271
|
+
...base_props,
|
|
272
|
+
icon: null
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
expect(queryByRole("img")).not.toBeInTheDocument();
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
describe("Events", () => {
|
|
280
|
+
afterEach(() => cleanup());
|
|
281
|
+
|
|
282
|
+
test("no spurious change event on mount", async () => {
|
|
283
|
+
const { listen } = await render(UploadButton, {
|
|
284
|
+
...base_props,
|
|
285
|
+
value: null
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
const change = listen("change", { retrospective: true });
|
|
289
|
+
expect(change).not.toHaveBeenCalled();
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test("click event fires when button is clicked", async () => {
|
|
293
|
+
const { getByRole, listen } = await render(UploadButton, {
|
|
294
|
+
...upload_props
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
const click = listen("click");
|
|
298
|
+
await event.click(getByRole("button", { name: "Upload" }));
|
|
299
|
+
|
|
300
|
+
expect(click).toHaveBeenCalledTimes(1);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
test("upload and change events fire after file upload", async () => {
|
|
304
|
+
const { listen } = await render(UploadButton, {
|
|
305
|
+
...upload_props
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
const upload = listen("upload");
|
|
309
|
+
const change = listen("change");
|
|
310
|
+
|
|
311
|
+
await upload_file(TEST_TXT);
|
|
312
|
+
|
|
313
|
+
await waitFor(() => {
|
|
314
|
+
expect(upload).toHaveBeenCalledTimes(1);
|
|
315
|
+
});
|
|
316
|
+
expect(change).toHaveBeenCalledTimes(1);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
test("upload failure dispatches error event with the message", async () => {
|
|
320
|
+
const failing_upload = vi
|
|
321
|
+
.fn()
|
|
322
|
+
.mockRejectedValue(new Error("File too large"));
|
|
323
|
+
const { listen } = await render(UploadButton, {
|
|
324
|
+
...upload_props,
|
|
325
|
+
client: { ...mock_client(), upload: failing_upload }
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
const error = listen("error");
|
|
329
|
+
|
|
330
|
+
await upload_file(TEST_TXT);
|
|
331
|
+
|
|
332
|
+
await waitFor(() => {
|
|
333
|
+
expect(failing_upload).toHaveBeenCalled();
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
await waitFor(() => {
|
|
337
|
+
expect(error).toHaveBeenCalledTimes(1);
|
|
338
|
+
});
|
|
339
|
+
expect(error).toHaveBeenCalledWith("File too large");
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
describe("get_data / set_data", () => {
|
|
344
|
+
afterEach(() => cleanup());
|
|
345
|
+
|
|
346
|
+
test("get_data returns null initially", async () => {
|
|
347
|
+
const { get_data } = await render(UploadButton, {
|
|
348
|
+
...base_props,
|
|
349
|
+
value: null
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
const data = await get_data();
|
|
353
|
+
expect(data.value).toBeNull();
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
test("get_data returns initial value when mounted with one", async () => {
|
|
357
|
+
const { get_data } = await render(UploadButton, {
|
|
358
|
+
...base_props,
|
|
359
|
+
value: TEST_TXT
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
const data = await get_data();
|
|
363
|
+
expect(data.value).toEqual(TEST_TXT);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
test("set_data updates the value reported by get_data", async () => {
|
|
367
|
+
const { set_data, get_data } = await render(UploadButton, base_props);
|
|
368
|
+
await set_data({ value: TEST_TXT });
|
|
369
|
+
await waitFor(async () =>
|
|
370
|
+
expect((await get_data()).value).toEqual(TEST_TXT)
|
|
68
371
|
);
|
|
372
|
+
});
|
|
69
373
|
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
expect(mock.callCount).toBe(1);
|
|
75
|
-
const [data] = component.$capture_state().value;
|
|
76
|
-
expect(data).toBeTruthy();
|
|
77
|
-
assert.equal(data.name, "my-audio.wav");
|
|
374
|
+
test("upload interaction is reflected in get_data", async () => {
|
|
375
|
+
const { get_data } = await render(UploadButton, upload_props);
|
|
376
|
+
await upload_file(TEST_TXT);
|
|
377
|
+
await waitFor(async () => expect((await get_data()).value).not.toBeNull());
|
|
78
378
|
});
|
|
79
379
|
});
|
|
380
|
+
|
|
381
|
+
describe("Edge cases", () => {
|
|
382
|
+
afterEach(() => cleanup());
|
|
383
|
+
|
|
384
|
+
test("max_file_size is forwarded to client.upload", async () => {
|
|
385
|
+
const upload_spy = vi.fn(async (file_data) => file_data);
|
|
386
|
+
await render(UploadButton, {
|
|
387
|
+
...upload_props,
|
|
388
|
+
max_file_size: 100,
|
|
389
|
+
client: { ...mock_client(), upload: upload_spy }
|
|
390
|
+
});
|
|
391
|
+
await upload_file(TEST_TXT);
|
|
392
|
+
await waitFor(() => expect(upload_spy).toHaveBeenCalled());
|
|
393
|
+
expect(upload_spy).toHaveBeenCalledWith(
|
|
394
|
+
expect.anything(),
|
|
395
|
+
expect.anything(),
|
|
396
|
+
undefined,
|
|
397
|
+
100
|
|
398
|
+
);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
test("no mount-time events with initial value set", async () => {
|
|
402
|
+
const { listen } = await render(UploadButton, {
|
|
403
|
+
...base_props,
|
|
404
|
+
value: TEST_TXT
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
const change = listen("change", { retrospective: true });
|
|
408
|
+
const upload = listen("upload", { retrospective: true });
|
|
409
|
+
expect(change).not.toHaveBeenCalled();
|
|
410
|
+
expect(upload).not.toHaveBeenCalled();
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
test.todo(
|
|
415
|
+
"VISUAL: variant='primary' renders the button with primary styling — needs Playwright visual regression screenshot comparison"
|
|
416
|
+
);
|
|
417
|
+
|
|
418
|
+
test.todo(
|
|
419
|
+
"VISUAL: variant='secondary' renders the button with secondary styling — needs Playwright visual regression screenshot comparison"
|
|
420
|
+
);
|
|
421
|
+
|
|
422
|
+
test.todo(
|
|
423
|
+
"VISUAL: variant='stop' renders the button with stop/red styling — needs Playwright visual regression screenshot comparison"
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
test.todo(
|
|
427
|
+
"VISUAL: size='sm' renders a small button — needs Playwright visual regression screenshot comparison"
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
test.todo(
|
|
431
|
+
"VISUAL: size='md' renders a medium button — needs Playwright visual regression screenshot comparison"
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
test.todo(
|
|
435
|
+
"VISUAL: size='lg' renders a large button — needs Playwright visual regression screenshot comparison"
|
|
436
|
+
);
|
package/dist/Index.svelte
CHANGED
|
@@ -11,13 +11,7 @@
|
|
|
11
11
|
const props = $props();
|
|
12
12
|
const gradio = new Gradio<UploadButtonEvents, UploadButtonProps>(props);
|
|
13
13
|
|
|
14
|
-
let value = $
|
|
15
|
-
|
|
16
|
-
$effect(() => {
|
|
17
|
-
if (value !== gradio.props.value) {
|
|
18
|
-
gradio.props.value = value;
|
|
19
|
-
}
|
|
20
|
-
});
|
|
14
|
+
let value = $derived(gradio.props.value);
|
|
21
15
|
|
|
22
16
|
async function handle_event(
|
|
23
17
|
detail: null | FileData | FileData[],
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { tick } from "svelte";
|
|
3
3
|
import { BaseButton } from "@gradio/button";
|
|
4
4
|
import { prepare_files, type FileData, type Client } from "@gradio/client";
|
|
5
|
+
import { is_valid_mimetype } from "@gradio/upload";
|
|
5
6
|
|
|
6
7
|
let {
|
|
7
8
|
elem_id = "",
|
|
@@ -70,13 +71,20 @@
|
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
async function load_files(files: FileList): Promise<void> {
|
|
73
|
-
let _files: File[] = Array.from(files)
|
|
74
|
+
let _files: File[] = Array.from(files).filter((file) => {
|
|
75
|
+
const ext = "." + file.name.toLowerCase().split(".").pop();
|
|
76
|
+
if (is_valid_mimetype(accept_file_types, ext, file.type)) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
onerror?.(`Invalid file type only ${file_types?.join(", ")} allowed.`);
|
|
80
|
+
return false;
|
|
81
|
+
});
|
|
74
82
|
|
|
75
|
-
if (!
|
|
83
|
+
if (!_files.length) {
|
|
76
84
|
return;
|
|
77
85
|
}
|
|
78
86
|
if (file_count === "single") {
|
|
79
|
-
_files = [
|
|
87
|
+
_files = [_files[0]];
|
|
80
88
|
}
|
|
81
89
|
let all_file_data = await prepare_files(_files);
|
|
82
90
|
await tick();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradio/uploadbutton",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.19",
|
|
4
4
|
"description": "Gradio UI packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "",
|
|
@@ -17,13 +17,13 @@
|
|
|
17
17
|
},
|
|
18
18
|
"main": "./Index.svelte",
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@gradio/button": "^0.6.
|
|
21
|
-
"@gradio/
|
|
22
|
-
"@gradio/utils": "^0.12.
|
|
23
|
-
"@gradio/
|
|
20
|
+
"@gradio/button": "^0.6.6",
|
|
21
|
+
"@gradio/client": "^2.2.1",
|
|
22
|
+
"@gradio/utils": "^0.12.2",
|
|
23
|
+
"@gradio/upload": "^0.17.10"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@gradio/preview": "^0.16.
|
|
26
|
+
"@gradio/preview": "^0.16.2"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"svelte": "^5.48.0"
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { tick } from "svelte";
|
|
3
3
|
import { BaseButton } from "@gradio/button";
|
|
4
4
|
import { prepare_files, type FileData, type Client } from "@gradio/client";
|
|
5
|
+
import { is_valid_mimetype } from "@gradio/upload";
|
|
5
6
|
|
|
6
7
|
let {
|
|
7
8
|
elem_id = "",
|
|
@@ -70,13 +71,20 @@
|
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
async function load_files(files: FileList): Promise<void> {
|
|
73
|
-
let _files: File[] = Array.from(files)
|
|
74
|
+
let _files: File[] = Array.from(files).filter((file) => {
|
|
75
|
+
const ext = "." + file.name.toLowerCase().split(".").pop();
|
|
76
|
+
if (is_valid_mimetype(accept_file_types, ext, file.type)) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
onerror?.(`Invalid file type only ${file_types?.join(", ")} allowed.`);
|
|
80
|
+
return false;
|
|
81
|
+
});
|
|
74
82
|
|
|
75
|
-
if (!
|
|
83
|
+
if (!_files.length) {
|
|
76
84
|
return;
|
|
77
85
|
}
|
|
78
86
|
if (file_count === "single") {
|
|
79
|
-
_files = [
|
|
87
|
+
_files = [_files[0]];
|
|
80
88
|
}
|
|
81
89
|
let all_file_data = await prepare_files(_files);
|
|
82
90
|
await tick();
|