@alixpartners/ui-components 2.2.0 → 2.3.1

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.
@@ -0,0 +1,606 @@
1
+ import { jsxs as v, jsx as i } from "react/jsx-runtime";
2
+ import { d as y, i as l, r as p, g as e, s as o, v as c, w as f, f as B } from "../../vi.bdSIJ99Y-017e_Pkz.js";
3
+ import d from "./DragAndDrop.js";
4
+ import { c as h, d as I } from "../../DragAndDrop.utils-D3xTV9EK.js";
5
+ import { u as w } from "../../index-DkTDHhag.js";
6
+ y("DragAndDrop", () => {
7
+ y("Rendering", () => {
8
+ y("Basic rendering", () => {
9
+ l("should render component with required props", () => {
10
+ p(/* @__PURE__ */ i(d, { label: "Upload File", fileExtensionsAllowed: ["pdf"], maxSize: 5 })), e(o.getByText("Upload File")).toBeInTheDocument(), e(o.getByTestId("drag-and-drop-zone")).toBeInTheDocument(), e(o.getByTestId("drag-and-drop-input")).toBeInTheDocument();
11
+ }), l("should not render label element when label prop is omitted", () => {
12
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5 })), e(o.queryByText("Upload File")).not.toBeInTheDocument(), e(o.getByTestId("drag-and-drop-zone")).toBeInTheDocument();
13
+ }), l("should render required asterisk when required prop is true", () => {
14
+ p(/* @__PURE__ */ i(d, { label: "Upload File", required: !0, fileExtensionsAllowed: ["pdf"], maxSize: 5 })), e(o.getByTestId("drag-and-drop-required")).toBeInTheDocument(), e(o.getByTestId("drag-and-drop-required")).toHaveTextContent("*");
15
+ }), l("should not render required asterisk when required prop is false", () => {
16
+ p(/* @__PURE__ */ i(d, { label: "Upload File", required: !1, fileExtensionsAllowed: ["pdf"], maxSize: 5 })), e(o.queryByTestId("drag-and-drop-required")).not.toBeInTheDocument();
17
+ }), l("should render browse button with default text for single file", () => {
18
+ p(/* @__PURE__ */ i(d, { type: "single", fileExtensionsAllowed: ["pdf"], maxSize: 5 }));
19
+ const t = o.getByText("Browse file");
20
+ e(t).toBeInTheDocument();
21
+ }), l("should render browse button with default text for multiple files", () => {
22
+ p(/* @__PURE__ */ i(d, { type: "multiple", fileExtensionsAllowed: ["pdf"], maxSize: 5 }));
23
+ const t = o.getByText("Browse files");
24
+ e(t).toBeInTheDocument();
25
+ }), l("should render drag text for single file", () => {
26
+ p(/* @__PURE__ */ i(d, { type: "single", fileExtensionsAllowed: ["pdf"], maxSize: 5 })), e(o.getByText("or drag your file")).toBeInTheDocument();
27
+ }), l("should render drag text for multiple files", () => {
28
+ p(/* @__PURE__ */ i(d, { type: "multiple", fileExtensionsAllowed: ["pdf"], maxSize: 5 })), e(o.getByText("or drag multiple files")).toBeInTheDocument();
29
+ }), l("should render file list when files are present", async () => {
30
+ const t = new File(["test"], "test.pdf", {
31
+ type: "application/pdf"
32
+ }), a = c.fn();
33
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a }));
34
+ const n = o.getByTestId("drag-and-drop-input");
35
+ await w.upload(n, t), await f(() => {
36
+ e(o.getByTestId("drag-and-drop-files-list")).toBeInTheDocument();
37
+ });
38
+ });
39
+ }), y("Disabled state", () => {
40
+ l("should render browse button as enabled by default", () => {
41
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5 }));
42
+ const t = o.getByText("Browse file");
43
+ e(t).not.toBeDisabled();
44
+ }), l("should render browse button as disabled when disabled prop is true", () => {
45
+ p(/* @__PURE__ */ i(d, { disabled: !0, fileExtensionsAllowed: ["pdf"], maxSize: 5 }));
46
+ const t = o.getByTestId("button");
47
+ e(t).toBeDisabled();
48
+ });
49
+ }), y("CSS Classes", () => {
50
+ l("should apply disabled CSS class to container when disabled prop is true", () => {
51
+ const {
52
+ container: t
53
+ } = p(/* @__PURE__ */ i(d, { disabled: !0, fileExtensionsAllowed: ["pdf"], maxSize: 5 })), a = t.firstChild;
54
+ e(a.className).toContain("disabled");
55
+ }), l("should merge custom className with default container classes", () => {
56
+ const {
57
+ container: t
58
+ } = p(/* @__PURE__ */ i(d, { className: "custom-class", fileExtensionsAllowed: ["pdf"], maxSize: 5 })), a = t.firstChild;
59
+ e(a.className).toContain("custom-class");
60
+ });
61
+ });
62
+ }), y("Text Customization", () => {
63
+ l("should use custom browse text when provided as string", () => {
64
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, texts: {
65
+ browse: "Select File"
66
+ } })), e(o.getByText("Select File")).toBeInTheDocument();
67
+ }), l("should use custom browse text when provided as function", () => {
68
+ p(/* @__PURE__ */ i(d, { type: "multiple", fileExtensionsAllowed: ["pdf"], maxSize: 5, texts: {
69
+ browse: (t) => t === "single" ? "Choose One" : "Choose Many"
70
+ } })), e(o.getByText("Choose Many")).toBeInTheDocument();
71
+ }), l("should use custom drag text when provided as string", () => {
72
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, texts: {
73
+ drag: "or drop here"
74
+ } })), e(o.getByText("or drop here")).toBeInTheDocument();
75
+ }), l("should use custom drag text when provided as function", () => {
76
+ p(/* @__PURE__ */ i(d, { type: "multiple", fileExtensionsAllowed: ["pdf"], maxSize: 5, texts: {
77
+ drag: (t) => t === "single" ? "drop one" : "drop many"
78
+ } })), e(o.getByText("drop many")).toBeInTheDocument();
79
+ }), l("should use custom constraints text when provided", () => {
80
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf", "doc"], maxSize: 10, texts: {
81
+ constraints: ({
82
+ extensions: t,
83
+ maxSize: a
84
+ }) => `Formats: ${t.join(", ")}. Max: ${a}MB`
85
+ } })), e(o.getByText(/Formats: pdf, doc. Max: 10MB/)).toBeInTheDocument();
86
+ }), l("should use custom error messages when provided", async () => {
87
+ const t = new File(["x".repeat(10485760)], "large.pdf", {
88
+ type: "application/pdf"
89
+ }), a = c.fn();
90
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 1, onUpload: a, texts: {
91
+ errors: {
92
+ sizeTooLarge: (s) => `File is too big! Maximum is ${s}MB`
93
+ }
94
+ } }));
95
+ const n = o.getByTestId("drag-and-drop-input");
96
+ await w.upload(n, t), await f(() => {
97
+ e(o.getByText(/File is too big! Maximum is 1MB/)).toBeInTheDocument();
98
+ });
99
+ });
100
+ }), y("Icon Customization", () => {
101
+ l("should use custom file icon when provided", async () => {
102
+ const t = new File(["test"], "test.pdf", {
103
+ type: "application/pdf"
104
+ }), a = c.fn();
105
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a, fileIcon: "ap-icon-document-rpm" }));
106
+ const n = o.getByTestId("drag-and-drop-input");
107
+ await w.upload(n, t), await f(() => {
108
+ const s = o.getByTestId("drag-and-drop-file-item-0").querySelector(".ap-icon-document-rpm");
109
+ e(s).toBeInTheDocument();
110
+ });
111
+ }), l("should use custom error icon when provided", async () => {
112
+ const t = new File(["test"], "test.txt", {
113
+ type: "text/plain"
114
+ }), a = c.fn();
115
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a, errorIcon: "ap-icon-alert-filled" }));
116
+ const n = o.getByTestId("drag-and-drop-input"), s = new DataTransfer();
117
+ s.items.add(t), Object.defineProperty(n, "files", {
118
+ value: s.files,
119
+ writable: !1
120
+ }), B.change(n), await f(() => {
121
+ const r = o.getByTestId("drag-and-drop-file-error-0");
122
+ e(r).toBeInTheDocument();
123
+ const u = r.querySelector(".ap-icon-alert-filled");
124
+ e(u).toBeInTheDocument();
125
+ });
126
+ }), l("should use custom browse button icon when provided", () => {
127
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, browseButtonIcon: "ap-icon-upload" }));
128
+ const t = o.getByTestId("button-icon");
129
+ e(t).toBeInTheDocument(), e(t.className).toContain("ap-icon-upload");
130
+ });
131
+ }), y("Render Props", () => {
132
+ l("should use custom renderFileItem when provided", async () => {
133
+ const t = new File(["test"], "test.pdf", {
134
+ type: "application/pdf"
135
+ }), a = c.fn(), n = c.fn((r, u, g) => /* @__PURE__ */ v("div", { children: [
136
+ "Custom: ",
137
+ r.file.name,
138
+ /* @__PURE__ */ i("button", { onClick: g, children: "Remove" })
139
+ ] }));
140
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a, renderFileItem: n }));
141
+ const s = o.getByTestId("drag-and-drop-input");
142
+ await w.upload(s, t), await f(() => {
143
+ e(o.getByTestId("custom-file-item")).toBeInTheDocument(), e(o.getByText("Custom: test.pdf")).toBeInTheDocument(), e(n).toHaveBeenCalledTimes(1);
144
+ });
145
+ }), l("should call renderFileItem with correct parameters", async () => {
146
+ const t = new File(["test"], "test.pdf", {
147
+ type: "application/pdf"
148
+ }), a = c.fn(), n = c.fn((r, u, g) => /* @__PURE__ */ i("div", { children: "Custom" }));
149
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a, renderFileItem: n }));
150
+ const s = o.getByTestId("drag-and-drop-input");
151
+ await w.upload(s, t), await f(() => {
152
+ e(n).toHaveBeenCalledWith(e.objectContaining({
153
+ file: e.any(File),
154
+ error: void 0
155
+ }), 0, e.any(Function));
156
+ });
157
+ });
158
+ }), y("File Size Formatter", () => {
159
+ l("should use custom fileSizeFormatter when provided", async () => {
160
+ const t = new File(["test"], "test.pdf", {
161
+ type: "application/pdf"
162
+ }), a = c.fn(), n = c.fn((r) => `${(r / 1024).toFixed(0)} KB`);
163
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a, fileSizeFormatter: n }));
164
+ const s = o.getByTestId("drag-and-drop-input");
165
+ await w.upload(s, t), await f(() => {
166
+ e(n).toHaveBeenCalled();
167
+ const r = o.getByTestId("drag-and-drop-file-size-0");
168
+ e(r.textContent).toBe(`${Math.round(t.size / 1024)} KB`);
169
+ });
170
+ }), l("should use default formatter when fileSizeFormatter is not provided", async () => {
171
+ const t = new File(["test"], "test.pdf", {
172
+ type: "application/pdf"
173
+ }), a = c.fn();
174
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a }));
175
+ const n = o.getByTestId("drag-and-drop-input");
176
+ await w.upload(n, t), await f(() => {
177
+ const s = o.getByTestId("drag-and-drop-file-size-0");
178
+ e(s.textContent).toBe((t.size / 1024).toFixed(2) + " KB");
179
+ });
180
+ });
181
+ }), y("Button Customization", () => {
182
+ l("should apply browseButtonProps to browse button", () => {
183
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, browseButtonProps: {
184
+ type: "primary",
185
+ variant: "danger",
186
+ size: "md"
187
+ } }));
188
+ const t = o.getByTestId("button");
189
+ e(t.className).toContain("btn-primary"), e(t.className).toContain("btn-danger"), e(t.className).toContain("btn-md");
190
+ });
191
+ }), y("File Upload Interactions", () => {
192
+ l("should call onUpload when file is selected", async () => {
193
+ const t = new File(["test"], "test.pdf", {
194
+ type: "application/pdf"
195
+ }), a = c.fn();
196
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a }));
197
+ const n = o.getByTestId("drag-and-drop-input");
198
+ await w.upload(n, t), await f(() => {
199
+ e(a).toHaveBeenCalledTimes(1), e(a).toHaveBeenCalledWith(e.arrayContaining([e.objectContaining({
200
+ file: e.any(File)
201
+ })]));
202
+ });
203
+ }), l("should handle multiple file selection", async () => {
204
+ const t = new File(["test1"], "test1.pdf", {
205
+ type: "application/pdf"
206
+ }), a = new File(["test2"], "test2.pdf", {
207
+ type: "application/pdf"
208
+ }), n = c.fn();
209
+ p(/* @__PURE__ */ i(d, { type: "multiple", fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: n }));
210
+ const s = o.getByTestId("drag-and-drop-input");
211
+ await w.upload(s, [t, a]), await f(() => {
212
+ e(n).toHaveBeenCalledTimes(1);
213
+ const r = n.mock.calls[0][0];
214
+ e(r).toHaveLength(2);
215
+ });
216
+ }), l("should call onRemoveFile when remove button is clicked", async () => {
217
+ const t = new File(["test"], "test.pdf", {
218
+ type: "application/pdf"
219
+ }), a = c.fn(), n = c.fn();
220
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a, onRemoveFile: n }));
221
+ const s = o.getByTestId("drag-and-drop-input");
222
+ await w.upload(s, t), await f(() => {
223
+ e(o.getByTestId("drag-and-drop-files-list")).toBeInTheDocument();
224
+ });
225
+ const r = o.getByTestId("drag-and-drop-remove-0");
226
+ B.click(r), e(n).toHaveBeenCalledTimes(1), e(n).toHaveBeenCalledWith("test.pdf"), await f(() => {
227
+ e(o.queryByTestId("drag-and-drop-files-list")).not.toBeInTheDocument();
228
+ });
229
+ }), l("should queue files when queueFiles is true", async () => {
230
+ const t = new File(["test1"], "test1.pdf", {
231
+ type: "application/pdf"
232
+ }), a = new File(["test2"], "test2.pdf", {
233
+ type: "application/pdf"
234
+ }), n = c.fn();
235
+ p(/* @__PURE__ */ i(d, { type: "multiple", fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: n, queueFiles: !0 }));
236
+ const s = o.getByTestId("drag-and-drop-input");
237
+ await w.upload(s, t), await f(() => {
238
+ e(n).toHaveBeenCalledTimes(1);
239
+ }), await w.upload(s, a), await f(() => {
240
+ e(n).toHaveBeenCalledTimes(2);
241
+ const r = n.mock.calls[1][0];
242
+ e(r.length).toBeGreaterThan(1);
243
+ });
244
+ });
245
+ }), y("Drag and Drop Interactions", () => {
246
+ l("should handle drag over event", () => {
247
+ const t = c.fn();
248
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onDragOver: t }));
249
+ const a = o.getByTestId("drag-and-drop-zone");
250
+ B.dragOver(a), e(t).toHaveBeenCalledTimes(1);
251
+ }), l("should handle drag leave event", () => {
252
+ const t = c.fn();
253
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onDragLeave: t }));
254
+ const a = o.getByTestId("drag-and-drop-zone");
255
+ B.dragLeave(a), e(t).toHaveBeenCalledTimes(1);
256
+ }), l("should handle drop event and call onUpload", async () => {
257
+ const t = new File(["test"], "test.pdf", {
258
+ type: "application/pdf"
259
+ }), a = c.fn(), n = c.fn();
260
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a, onDrop: n }));
261
+ const s = o.getByTestId("drag-and-drop-zone"), r = {
262
+ files: [t],
263
+ clearData: c.fn()
264
+ };
265
+ B.drop(s, {
266
+ dataTransfer: r
267
+ }), await f(() => {
268
+ e(n).toHaveBeenCalledTimes(1), e(a).toHaveBeenCalledTimes(1);
269
+ });
270
+ });
271
+ }), y("Validation", () => {
272
+ l("should validate file size and show error for oversized files", async () => {
273
+ const t = new File(["x".repeat(10485760)], "large.pdf", {
274
+ type: "application/pdf"
275
+ }), a = c.fn();
276
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 1, onUpload: a }));
277
+ const n = o.getByTestId("drag-and-drop-input");
278
+ await w.upload(n, t), await f(() => {
279
+ e(o.getByTestId("drag-and-drop-file-error-0")).toBeInTheDocument();
280
+ const s = o.getByTestId("drag-and-drop-file-error-0");
281
+ e(s.textContent).toContain("too large");
282
+ });
283
+ }), l("should validate file extension and show error for invalid extensions", async () => {
284
+ const t = new File(["test"], "test.txt", {
285
+ type: "text/plain"
286
+ }), a = c.fn();
287
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a }));
288
+ const n = o.getByTestId("drag-and-drop-input"), s = new DataTransfer();
289
+ s.items.add(t), Object.defineProperty(n, "files", {
290
+ value: s.files,
291
+ writable: !1
292
+ }), B.change(n), await f(() => {
293
+ e(o.getByTestId("drag-and-drop-file-error-0")).toBeInTheDocument();
294
+ const r = o.getByTestId("drag-and-drop-file-error-0");
295
+ e(r.textContent).toContain("format is incorrect");
296
+ });
297
+ }), l("should use custom validator when provided", async () => {
298
+ const t = new File(["test"], "test.pdf", {
299
+ type: "application/pdf"
300
+ }), a = c.fn(), n = c.fn(
301
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
302
+ (r, u) => r.name.includes("test") ? {
303
+ file: r,
304
+ error: 'Files with "test" in name are not allowed'
305
+ } : {
306
+ file: r
307
+ }
308
+ );
309
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a, validator: n }));
310
+ const s = o.getByTestId("drag-and-drop-input");
311
+ await w.upload(s, t), await f(() => {
312
+ e(n).toHaveBeenCalledTimes(1), e(o.getByTestId("drag-and-drop-file-error-0")).toBeInTheDocument(), e(o.getByText('Files with "test" in name are not allowed')).toBeInTheDocument();
313
+ });
314
+ }), l("should execute synchronous custom validator and display returned error message", async () => {
315
+ const t = new File(["test"], "test.pdf", {
316
+ type: "application/pdf"
317
+ }), a = c.fn(), n = c.fn(
318
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
319
+ (r, u) => ({
320
+ file: r,
321
+ error: "Synchronous validation failed"
322
+ })
323
+ );
324
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a, validator: n }));
325
+ const s = o.getByTestId("drag-and-drop-input");
326
+ await w.upload(s, t), await f(() => {
327
+ e(n).toHaveBeenCalledTimes(1), e(o.getByTestId("drag-and-drop-file-error-0")).toBeInTheDocument(), e(o.getByText("Synchronous validation failed")).toBeInTheDocument();
328
+ });
329
+ }), l("should accept first N files and mark excess files with error when maxFiles limit is exceeded", async () => {
330
+ const t = new File(["test1"], "test1.pdf", {
331
+ type: "application/pdf"
332
+ }), a = new File(["test2"], "test2.pdf", {
333
+ type: "application/pdf"
334
+ }), n = new File(["test3"], "test3.pdf", {
335
+ type: "application/pdf"
336
+ }), s = c.fn();
337
+ p(/* @__PURE__ */ i(d, { type: "multiple", fileExtensionsAllowed: ["pdf"], maxSize: 5, maxFiles: 2, onUpload: s }));
338
+ const r = o.getByTestId("drag-and-drop-input");
339
+ await w.upload(r, [t, a, n]), await f(() => {
340
+ e(s).toHaveBeenCalledTimes(1);
341
+ const u = s.mock.calls[0][0];
342
+ e(u).toHaveLength(3), e(u[0].error).toBeUndefined(), e(u[1].error).toBeUndefined(), e(u[2].error).toContain("Maximum 2 files allowed");
343
+ });
344
+ }), l("should only accept first file when type is single and multiple files are dropped", async () => {
345
+ const t = new File(["test1"], "test1.pdf", {
346
+ type: "application/pdf"
347
+ }), a = new File(["test2"], "test2.pdf", {
348
+ type: "application/pdf"
349
+ }), n = c.fn();
350
+ p(/* @__PURE__ */ i(d, { type: "single", fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: n }));
351
+ const s = o.getByTestId("drag-and-drop-input");
352
+ await w.upload(s, [t, a]), await f(() => {
353
+ e(n).toHaveBeenCalledTimes(1);
354
+ const r = n.mock.calls[0][0];
355
+ e(r).toHaveLength(1);
356
+ });
357
+ });
358
+ }), y("Controlled Mode", () => {
359
+ l("should use controlled value when value prop is provided", async () => {
360
+ const t = new File(["test"], "test.pdf", {
361
+ type: "application/pdf"
362
+ }), a = c.fn(), {
363
+ rerender: n
364
+ } = p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, value: t, onUpload: a }));
365
+ await f(() => {
366
+ e(o.getByTestId("drag-and-drop-files-list")).toBeInTheDocument(), e(o.getByTestId("drag-and-drop-file-name-0")).toHaveTextContent("test.pdf");
367
+ });
368
+ const s = new File(["new"], "new.pdf", {
369
+ type: "application/pdf"
370
+ });
371
+ n(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, value: s, onUpload: a })), await f(() => {
372
+ e(o.getByTestId("drag-and-drop-file-name-0")).toHaveTextContent("new.pdf");
373
+ });
374
+ }), l("should use controlled value array when value prop is array", async () => {
375
+ const t = new File(["test1"], "test1.pdf", {
376
+ type: "application/pdf"
377
+ }), a = new File(["test2"], "test2.pdf", {
378
+ type: "application/pdf"
379
+ }), n = c.fn();
380
+ p(/* @__PURE__ */ i(d, { type: "multiple", fileExtensionsAllowed: ["pdf"], maxSize: 5, value: [t, a], onUpload: n })), await f(() => {
381
+ e(o.getByTestId("drag-and-drop-files-list")).toBeInTheDocument(), e(o.getByTestId("drag-and-drop-file-name-0")).toHaveTextContent("test1.pdf"), e(o.getByTestId("drag-and-drop-file-name-1")).toHaveTextContent("test2.pdf");
382
+ });
383
+ }), l("should remove files by filename in controlled mode", async () => {
384
+ const t = new File(["test1"], "test1.pdf", {
385
+ type: "application/pdf"
386
+ }), a = new File(["test2"], "test2.pdf", {
387
+ type: "application/pdf"
388
+ });
389
+ let n = [];
390
+ const s = c.fn((m) => {
391
+ n = m;
392
+ }), r = c.fn((m) => {
393
+ n = n.filter((T) => T.file.name !== m);
394
+ }), {
395
+ rerender: u
396
+ } = p(/* @__PURE__ */ i(d, { type: "multiple", fileExtensionsAllowed: ["pdf"], maxSize: 5, value: n.map((m) => m.file), onUpload: s, onRemoveFile: r })), g = o.getByTestId("drag-and-drop-input");
397
+ await w.upload(g, [t, a]), await f(() => {
398
+ e(s).toHaveBeenCalled(), e(n.length).toBe(2);
399
+ }), u(/* @__PURE__ */ i(d, { type: "multiple", fileExtensionsAllowed: ["pdf"], maxSize: 5, value: n.map((m) => m.file), onUpload: s, onRemoveFile: r })), await f(() => {
400
+ e(o.getByTestId("drag-and-drop-file-name-0")).toHaveTextContent("test1.pdf"), e(o.getByTestId("drag-and-drop-file-name-1")).toHaveTextContent("test2.pdf");
401
+ });
402
+ const x = o.getByTestId("drag-and-drop-remove-0");
403
+ B.click(x), await f(() => {
404
+ e(r).toHaveBeenCalledTimes(1), e(r).toHaveBeenCalledWith("test1.pdf");
405
+ }), e(n.length).toBe(1), e(n.find((m) => m.file.name === "test1.pdf")).toBeUndefined(), u(/* @__PURE__ */ i(d, { type: "multiple", fileExtensionsAllowed: ["pdf"], maxSize: 5, value: n.map((m) => m.file), onUpload: s, onRemoveFile: r })), await f(() => {
406
+ e(o.getByTestId("drag-and-drop-files-list")).toBeInTheDocument();
407
+ const m = n[0];
408
+ e(o.getByTestId("drag-and-drop-file-name-0")).toHaveTextContent(m.file.name), e(o.queryByTestId("drag-and-drop-file-name-1")).not.toBeInTheDocument();
409
+ });
410
+ });
411
+ }), y("File Display", () => {
412
+ l("should display file name", async () => {
413
+ const t = new File(["test"], "test.pdf", {
414
+ type: "application/pdf"
415
+ }), a = c.fn();
416
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a }));
417
+ const n = o.getByTestId("drag-and-drop-input");
418
+ await w.upload(n, t), await f(() => {
419
+ e(o.getByTestId("drag-and-drop-file-name-0")).toHaveTextContent("test.pdf");
420
+ });
421
+ }), l("should display file size", async () => {
422
+ const t = new File(["test"], "test.pdf", {
423
+ type: "application/pdf"
424
+ }), a = c.fn();
425
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a }));
426
+ const n = o.getByTestId("drag-and-drop-input");
427
+ await w.upload(n, t), await f(() => {
428
+ e(o.getByTestId("drag-and-drop-file-size-0")).toBeInTheDocument();
429
+ });
430
+ }), l("should display error message when file has error", async () => {
431
+ const t = new File(["test"], "test.txt", {
432
+ type: "text/plain"
433
+ }), a = c.fn();
434
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a }));
435
+ const n = o.getByTestId("drag-and-drop-input"), s = new DataTransfer();
436
+ s.items.add(t), Object.defineProperty(n, "files", {
437
+ value: s.files,
438
+ writable: !1
439
+ }), B.change(n), await f(() => {
440
+ e(o.getByTestId("drag-and-drop-file-error-0")).toBeInTheDocument();
441
+ const r = o.getByTestId("drag-and-drop-file-error-0");
442
+ e(r.textContent).toContain("format is incorrect");
443
+ });
444
+ });
445
+ }), y("External Errors Prop", () => {
446
+ l("should display external errors when no validator errors exist", async () => {
447
+ const t = new File(["test"], "test.pdf", {
448
+ type: "application/pdf"
449
+ }), a = c.fn();
450
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a, errors: {
451
+ "test.pdf": "Custom external error message"
452
+ } }));
453
+ const s = o.getByTestId("drag-and-drop-input");
454
+ await w.upload(s, t), await f(() => {
455
+ e(o.getByTestId("drag-and-drop-file-error-0")).toBeInTheDocument(), e(o.getByText("Custom external error message")).toBeInTheDocument();
456
+ });
457
+ }), l("should hide external errors when validator errors exist (precedence)", async () => {
458
+ const t = new File(["test"], "test.txt", {
459
+ type: "text/plain"
460
+ }), a = c.fn();
461
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a, errors: {
462
+ "test.txt": "Custom external error message"
463
+ } }));
464
+ const s = o.getByTestId("drag-and-drop-input"), r = new DataTransfer();
465
+ r.items.add(t), Object.defineProperty(s, "files", {
466
+ value: r.files,
467
+ writable: !1
468
+ }), B.change(s), await f(() => {
469
+ e(o.getByTestId("drag-and-drop-file-error-0")).toBeInTheDocument(), e(o.getByText(/format is incorrect/)).toBeInTheDocument(), e(o.queryByText("Custom external error message")).not.toBeInTheDocument();
470
+ });
471
+ }), l("should update external errors when errors prop changes", async () => {
472
+ const t = new File(["test"], "test.pdf", {
473
+ type: "application/pdf"
474
+ }), a = c.fn(), {
475
+ rerender: n
476
+ } = p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a, value: t }));
477
+ await f(() => {
478
+ e(o.getByTestId("drag-and-drop-files-list")).toBeInTheDocument(), e(o.queryByTestId("drag-and-drop-file-error-0")).not.toBeInTheDocument();
479
+ }), n(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a, value: t, errors: {
480
+ "test.pdf": "New error message"
481
+ } })), await f(() => {
482
+ e(o.getByTestId("drag-and-drop-file-error-0")).toBeInTheDocument(), e(o.getByText("New error message")).toBeInTheDocument();
483
+ });
484
+ }), l("should clear external errors when errors prop is cleared", async () => {
485
+ const t = new File(["test"], "test.pdf", {
486
+ type: "application/pdf"
487
+ }), a = c.fn(), {
488
+ rerender: n
489
+ } = p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a, value: t, errors: {
490
+ "test.pdf": "Error message"
491
+ } }));
492
+ await f(() => {
493
+ e(o.getByTestId("drag-and-drop-file-error-0")).toBeInTheDocument();
494
+ }), n(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: a, value: t, errors: {} })), await f(() => {
495
+ e(o.queryByTestId("drag-and-drop-file-error-0")).not.toBeInTheDocument();
496
+ });
497
+ }), l("should handle external errors with duplicate filenames correctly", async () => {
498
+ const t = new File(["test1"], "test.pdf", {
499
+ type: "application/pdf"
500
+ }), a = new File(["test2"], "test.pdf", {
501
+ type: "application/pdf"
502
+ }), n = c.fn();
503
+ p(/* @__PURE__ */ i(d, { type: "multiple", fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: n, value: [t, a], errors: {
504
+ "test.pdf": "Error for duplicate filename"
505
+ } })), await f(() => {
506
+ const r = o.getAllByTestId(/drag-and-drop-file-error-/);
507
+ e(r.length).toBeGreaterThan(0);
508
+ });
509
+ });
510
+ }), y("composeValidators", () => {
511
+ l("should compose multiple validators correctly", () => {
512
+ const t = c.fn((m) => m.name.includes("test") ? {
513
+ file: m,
514
+ error: "Validator 1 error"
515
+ } : {
516
+ file: m
517
+ }), a = c.fn((m) => m.size > 1e3 ? {
518
+ file: m,
519
+ error: "Validator 2 error"
520
+ } : {
521
+ file: m
522
+ }), n = h(t, a), s = {
523
+ maxSize: 5,
524
+ fileExtensionsAllowed: ["pdf"],
525
+ type: "single",
526
+ maxFiles: 1
527
+ }, r = new File(["test"], "test.pdf", {
528
+ type: "application/pdf"
529
+ }), u = n(r, s);
530
+ e(u.error).toBe("Validator 1 error"), e(t).toHaveBeenCalledTimes(1), e(a).not.toHaveBeenCalled();
531
+ const g = new File(["x".repeat(2e3)], "valid.pdf", {
532
+ type: "application/pdf"
533
+ }), x = n(g, s);
534
+ e(x.error).toBe("Validator 2 error"), e(a).toHaveBeenCalledTimes(1);
535
+ }), l("should return first error encountered", () => {
536
+ const t = c.fn((g) => ({
537
+ file: g,
538
+ error: "First error"
539
+ })), a = c.fn((g) => ({
540
+ file: g,
541
+ error: "Second error"
542
+ })), n = h(t, a), s = {
543
+ maxSize: 5,
544
+ fileExtensionsAllowed: ["pdf"],
545
+ type: "single",
546
+ maxFiles: 1
547
+ }, r = new File(["test"], "test.pdf", {
548
+ type: "application/pdf"
549
+ }), u = n(r, s);
550
+ e(u.error).toBe("First error"), e(t).toHaveBeenCalledTimes(1), e(a).not.toHaveBeenCalled();
551
+ }), l("should return success when all validators pass", () => {
552
+ const t = c.fn((g) => ({
553
+ file: g
554
+ })), a = c.fn((g) => ({
555
+ file: g
556
+ })), n = h(t, a), s = {
557
+ maxSize: 5,
558
+ fileExtensionsAllowed: ["pdf"],
559
+ type: "single",
560
+ maxFiles: 1
561
+ }, r = new File(["test"], "test.pdf", {
562
+ type: "application/pdf"
563
+ }), u = n(r, s);
564
+ e(u.error).toBeUndefined(), e(t).toHaveBeenCalledTimes(1), e(a).toHaveBeenCalledTimes(1);
565
+ }), l("should work with defaultValidator in composition", () => {
566
+ const t = c.fn((x, m) => {
567
+ const T = I(x, m);
568
+ return T.error ? T : x.name.startsWith("reject") ? {
569
+ file: x,
570
+ error: "Custom rejection error"
571
+ } : {
572
+ file: x
573
+ };
574
+ }), a = h(I, t), n = {
575
+ maxSize: 1,
576
+ fileExtensionsAllowed: ["pdf"],
577
+ type: "single",
578
+ maxFiles: 1
579
+ }, s = new File(["x".repeat(2 * 1024 * 1024)], "large.pdf", {
580
+ type: "application/pdf"
581
+ }), r = a(s, n);
582
+ e(r.error).toContain("too large"), e(r.errorCode).toBe("SIZE_TOO_LARGE");
583
+ const u = new File(["test"], "reject.pdf", {
584
+ type: "application/pdf"
585
+ }), g = a(u, n);
586
+ e(g.error).toBe("Custom rejection error");
587
+ }), l("should integrate composeValidators with component", async () => {
588
+ const t = c.fn((u, g) => {
589
+ const x = I(u, g);
590
+ return x.error ? x : u.name.startsWith("blocked") ? {
591
+ file: u,
592
+ error: "File name blocked"
593
+ } : {
594
+ file: u
595
+ };
596
+ }), a = h(I, t), n = new File(["test"], "blocked.pdf", {
597
+ type: "application/pdf"
598
+ }), s = c.fn();
599
+ p(/* @__PURE__ */ i(d, { fileExtensionsAllowed: ["pdf"], maxSize: 5, onUpload: s, validator: a }));
600
+ const r = o.getByTestId("drag-and-drop-input");
601
+ await w.upload(r, n), await f(() => {
602
+ e(o.getByTestId("drag-and-drop-file-error-0")).toBeInTheDocument(), e(o.getByText("File name blocked")).toBeInTheDocument();
603
+ });
604
+ });
605
+ });
606
+ });
@@ -0,0 +1,30 @@
1
+ import { ValidationContext, ValidationResult, Validator } from './DragAndDrop';
2
+ /**
3
+ * Default validator function for file uploads.
4
+ * Validates file size and extension against the provided context.
5
+ * Can be used standalone or composed with custom validation logic.
6
+ *
7
+ * @param file - The file to validate
8
+ * @param context - Validation context containing maxSize, fileExtensionsAllowed, etc.
9
+ * @returns ValidationResult with the file and optional error message
10
+ */
11
+ export declare function defaultValidator(file: File, context: ValidationContext): ValidationResult;
12
+ /**
13
+ * Composes multiple validators into a single validator function.
14
+ * Validators are executed in order; the first error encountered is returned.
15
+ *
16
+ * @param validators - Array of validator functions to compose
17
+ * @returns A composed validator function
18
+ *
19
+ * @example
20
+ * const myValidator = composeValidators(
21
+ * defaultValidator,
22
+ * (file, context) => {
23
+ * if (file.name.startsWith('test')) {
24
+ * return { file, error: 'Test files not allowed' }
25
+ * }
26
+ * return { file }
27
+ * }
28
+ * )
29
+ */
30
+ export declare function composeValidators(...validators: Validator[]): Validator;
@@ -20,6 +20,8 @@ type FilePickerProps = {
20
20
  helpLink?: string;
21
21
  helpLinkText?: string;
22
22
  labelTooltip?: InputLabelTooltipProps;
23
+ onUpload?: (files: UploadFile[]) => void;
24
+ onRemoveFile?: (fileName: string) => void;
23
25
  } & InteractiveEventHandlers;
24
26
  /**
25
27
  * FilePicker component for uploading files.
@@ -42,5 +44,5 @@ type FilePickerProps = {
42
44
  * @param {InputLabelTooltipProps} props.labelTooltip - The label tooltip of the component
43
45
  * @returns {JSX.Element} The rendered FilePicker component
44
46
  */
45
- export default function FilePicker({ type, maxFiles, fileExtensionsAllowed, maxSize, queueFiles, disabled, value, label, required, placeholder, errorMessage, helpText, helpLink, helpLinkText, labelTooltip, }: FilePickerProps): import("react/jsx-runtime").JSX.Element;
47
+ export default function FilePicker({ type, maxFiles, fileExtensionsAllowed, maxSize, queueFiles, disabled, value, label, required, placeholder, errorMessage, helpText, helpLink, helpLinkText, labelTooltip, onUpload, onRemoveFile, }: FilePickerProps): import("react/jsx-runtime").JSX.Element;
46
48
  export {};