diff --git a/addons/web/static/src/core/file_input/file_input.js b/addons/web/static/src/core/file_input/file_input.js index cc18401c73c..70159beecd7 100644 --- a/addons/web/static/src/core/file_input/file_input.js +++ b/addons/web/static/src/core/file_input/file_input.js @@ -2,7 +2,7 @@ import { useService } from "@web/core/utils/hooks"; -import { Component, onMounted, useRef } from "@odoo/owl"; +import { Component, onMounted, useRef, useState } from "@odoo/owl"; /** * Custom file input @@ -26,6 +26,10 @@ export class FileInput extends Component { setup() { this.http = useService("http"); this.fileInputRef = useRef("file-input"); + this.state = useState({ + // Disables upload button if currently uploading. + isDisable: false, + }); onMounted(() => { if (this.props.autoOpen) { @@ -70,12 +74,14 @@ export class FileInput extends Component { * - resId: the id of the resModel target instance */ async onFileInputChange() { + this.state.isDisable = true; const parsedFileData = await this.uploadFiles(this.httpParams); // When calling onUpload, also pass the files to allow to get data like their names this.props.onUpload(parsedFileData, this.fileInputRef.el ? this.fileInputRef.el.files : []); // Because the input would not trigger this method if the same file name is uploaded, // we must clear the value after handling the upload this.fileInputRef.el.value = null; + this.state.isDisable = false; } /** diff --git a/addons/web/static/src/core/file_input/file_input.xml b/addons/web/static/src/core/file_input/file_input.xml index e8322906704..3ee96fe923a 100644 --- a/addons/web/static/src/core/file_input/file_input.xml +++ b/addons/web/static/src/core/file_input/file_input.xml @@ -2,7 +2,7 @@ - + @@ -14,6 +14,7 @@ t-att-multiple="props.multiUpload" t-att-accept="props.acceptedFileExtensions" t-ref="file-input" + t-att-disabled="state.isDisable" t-on-change="onFileInputChange" /> diff --git a/addons/web/static/tests/core/file_input_tests.js b/addons/web/static/tests/core/file_input_tests.js index a784adecaa4..d346d424d2f 100644 --- a/addons/web/static/tests/core/file_input_tests.js +++ b/addons/web/static/tests/core/file_input_tests.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { makeTestEnv } from "@web/../tests/helpers/mock_env"; -import { editInput, getFixture, mount, patchWithCleanup, triggerEvent } from "@web/../tests/helpers/utils"; +import { editInput, getFixture, mount, patchWithCleanup, triggerEvent, makeDeferred, nextTick } from "@web/../tests/helpers/utils"; import { FileInput } from "@web/core/file_input/file_input"; import { registry } from "@web/core/registry"; @@ -138,4 +138,29 @@ QUnit.module("Components", ({ beforeEach }) => { await editInput(target, ".o_file_input input", file); assert.verifySteps(["fake_file.txt"], "file has been uploaded a second time"); }); + + QUnit.test("Upload button is disabled if attachment upload is not finished", async (assert) => { + assert.expect(2); + + const uploadedPromise = makeDeferred(); + await createFileInput({ + mockPost: async (route, params) => { + if (route === "/web/binary/upload_attachment") { + await uploadedPromise; + } + return "[]"; + }, + props: {}, + }); + //enable button + const input = target.querySelector(".o_file_input input"); + await triggerEvent(input, null, "change", {}, { skipVisibilityCheck: true }); + + //disable button + assert.ok(input.disabled, "the upload button should be disabled on upload"); + + uploadedPromise.resolve(); + await nextTick(); + assert.notOk(input.disabled, "the upload button should be enabled for upload"); + }); });