From 0b302eee30dd709ea00f58ad43d33e3a48502cbb Mon Sep 17 00:00:00 2001 From: Matias Simon Date: Wed, 16 Sep 2026 19:44:16 +0200 Subject: [PATCH 1/2] Don't let a second destroy() evict another instance `splice(indexOf(this), 1)` removes the last element whenever `indexOf` returns -1, so destroying an instance that was already unregistered -- calling `destroy()` twice is enough -- dropped an unrelated, live Dropzone out of `Dropzone.instances`. Refs #2367 --- .changeset/destroy-instances.md | 5 +++++ packages/dropzone/src/dropzone.ts | 6 +++++- packages/dropzone/test/unit-tests/all.js | 12 ++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 .changeset/destroy-instances.md diff --git a/.changeset/destroy-instances.md b/.changeset/destroy-instances.md new file mode 100644 index 000000000..102182c0b --- /dev/null +++ b/.changeset/destroy-instances.md @@ -0,0 +1,5 @@ +--- +"dropzone": patch +--- + +Stop `destroy()` removing a different instance. `Dropzone.instances.splice(indexOf(this), 1)` dropped the last entry whenever `indexOf` returned -1 — calling `destroy()` twice was enough — evicting an unrelated live Dropzone from the registry. diff --git a/packages/dropzone/src/dropzone.ts b/packages/dropzone/src/dropzone.ts index 89f8a13aa..1c4a431ab 100644 --- a/packages/dropzone/src/dropzone.ts +++ b/packages/dropzone/src/dropzone.ts @@ -547,7 +547,11 @@ export default class Dropzone extends Emitter { this.hiddenFileInput = null; } delete this.element.dropzone; - return Dropzone.instances.splice(Dropzone.instances.indexOf(this), 1); + // `indexOf` returns -1 for an instance that is no longer registered -- + // destroying twice is enough -- and `splice(-1, 1)` would then quietly + // drop the last entry, which is a different, live Dropzone. + let index = Dropzone.instances.indexOf(this); + return index === -1 ? [] : Dropzone.instances.splice(index, 1); } updateTotalUploadProgress() { diff --git a/packages/dropzone/test/unit-tests/all.js b/packages/dropzone/test/unit-tests/all.js index c2220ab09..779086464 100644 --- a/packages/dropzone/test/unit-tests/all.js +++ b/packages/dropzone/test/unit-tests/all.js @@ -807,6 +807,18 @@ describe("Dropzone", function () { dropzone.destroy(); return expect(Dropzone.instances.indexOf(dropzone) === -1).toBeTruthy(); }); + + it("should leave other instances alone when destroyed twice", function () { + let other = new Dropzone(Dropzone.createElement("
"), { url: "url" }); + + dropzone.destroy(); + // The second call finds nothing to remove. `splice(-1, 1)` used to + // take the last entry regardless, evicting an unrelated live instance. + dropzone.destroy(); + + expect(Dropzone.instances.indexOf(other) !== -1).toBeTruthy(); + other.destroy(); + }); }); describe(".filesize()", function () { From dc5d7bfb1dd450baa467d4682d9fb3d12be30758 Mon Sep 17 00:00:00 2001 From: Matias Simon Date: Wed, 16 Sep 2026 19:44:23 +0200 Subject: [PATCH 2/2] Compare tagName against FORM, not form `element.tagName` is upper case on HTML elements, so `init()`'s check never matched and `enctype="multipart/form-data"` was never set on a form Dropzone was attached to. `_addFormElementData` spells the same check `"FORM"` and does work. Refs #2367 --- .changeset/form-enctype.md | 5 +++++ packages/dropzone/src/dropzone.ts | 5 +++-- packages/dropzone/test/unit-tests/all.js | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 .changeset/form-enctype.md diff --git a/.changeset/form-enctype.md b/.changeset/form-enctype.md new file mode 100644 index 000000000..f9867a3f2 --- /dev/null +++ b/.changeset/form-enctype.md @@ -0,0 +1,5 @@ +--- +"dropzone": patch +--- + +Set `enctype="multipart/form-data"` on a form again. `init()` compared `tagName` against lower-case `"form"`, which never matches, so the attribute was never set. Only affects forms that are also submitted natively; the XHR upload is unchanged. diff --git a/packages/dropzone/src/dropzone.ts b/packages/dropzone/src/dropzone.ts index 1c4a431ab..89131f7ec 100644 --- a/packages/dropzone/src/dropzone.ts +++ b/packages/dropzone/src/dropzone.ts @@ -334,8 +334,9 @@ export default class Dropzone extends Emitter { // The function that gets called when Dropzone is initialized. You // can (and should) setup event listeners inside this function. init() { - // In case it isn't set already - if (this.element.tagName === "form") { + // In case it isn't set already. `tagName` is upper case on HTML elements, + // so this never matched and the attribute was never set. + if (this.element.tagName === "FORM") { this.element.setAttribute("enctype", "multipart/form-data"); } diff --git a/packages/dropzone/test/unit-tests/all.js b/packages/dropzone/test/unit-tests/all.js index 779086464..7d00c6452 100644 --- a/packages/dropzone/test/unit-tests/all.js +++ b/packages/dropzone/test/unit-tests/all.js @@ -764,6 +764,24 @@ describe("Dropzone", function () { }, 10); }))); + describe("init()", function () { + it("should set the enctype on a form element", function () { + let form = Dropzone.createElement("
"); + let formDropzone = new Dropzone(form, { url: "url" }); + + expect(form.getAttribute("enctype")).toBe("multipart/form-data"); + formDropzone.destroy(); + }); + + it("should leave an existing enctype alone on a non-form element", function () { + let div = Dropzone.createElement("
"); + let divDropzone = new Dropzone(div, { url: "url" }); + + expect(div.getAttribute("enctype")).toBe(null); + divDropzone.destroy(); + }); + }); + describe(".destroy()", function () { it("should properly cancel all pending uploads and remove all file references", () => new Promise((done) => {