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/.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 89f8a13aa..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"); } @@ -547,7 +548,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..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) => { @@ -807,6 +825,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 () {