From 4bf01b7fd556f1af1061b1e9a67d8cdddbfbf7e4 Mon Sep 17 00:00:00 2001 From: Thomas Sebert Date: Thu, 6 Mar 2025 10:39:26 +0100 Subject: [PATCH] [Live] Preserve file input values after render requests --- .../assets/dist/live_controller.js | 3 -- .../assets/src/Component/index.ts | 5 --- .../assets/test/controller/render.test.ts | 39 +++++++++++++++++++ 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/LiveComponent/assets/dist/live_controller.js b/src/LiveComponent/assets/dist/live_controller.js index 41177512fdd..cc3f551f872 100644 --- a/src/LiveComponent/assets/dist/live_controller.js +++ b/src/LiveComponent/assets/dist/live_controller.js @@ -2120,9 +2120,6 @@ class Component { this.backendRequest.promise.then(async (response) => { const backendResponse = new BackendResponse(response); const html = await backendResponse.getBody(); - for (const input of Object.values(this.pendingFiles)) { - input.value = ''; - } const headers = backendResponse.response.headers; if (!headers.get('Content-Type')?.includes('application/vnd.live-component+html') && !headers.get('X-Live-Redirect')) { diff --git a/src/LiveComponent/assets/src/Component/index.ts b/src/LiveComponent/assets/src/Component/index.ts index 7db1f564a7b..b8241ea352a 100644 --- a/src/LiveComponent/assets/src/Component/index.ts +++ b/src/LiveComponent/assets/src/Component/index.ts @@ -302,11 +302,6 @@ export default class Component { const backendResponse = new BackendResponse(response); const html = await backendResponse.getBody(); - // clear sent files inputs - for (const input of Object.values(this.pendingFiles)) { - input.value = ''; - } - // if the response does not contain a component, render as an error const headers = backendResponse.response.headers; if ( diff --git a/src/LiveComponent/assets/test/controller/render.test.ts b/src/LiveComponent/assets/test/controller/render.test.ts index 5dc6c64ddd7..4de4473f4c7 100644 --- a/src/LiveComponent/assets/test/controller/render.test.ts +++ b/src/LiveComponent/assets/test/controller/render.test.ts @@ -109,6 +109,45 @@ describe('LiveController rendering Tests', () => { expect((test.queryByDataModel('comment') as HTMLTextAreaElement).value).toEqual('I HAD A GREAT TIME'); }); + it('keeps file input value after a render request', async () => { + const test = await createTest( + {}, + () => ` +
+ + +
+ ` + ); + + const fileInput = test.element.querySelector('input[type="file"]') as HTMLInputElement; + const file = new File(['content'], 'test.txt', { type: 'text/plain' }); + + Object.defineProperty(fileInput, 'files', { + value: [file], + writable: false, + }); + + // Checks that the file is correctly assigned before rendering + expect(fileInput.files).not.toBeNull(); + expect(fileInput.files?.length).toBe(1); + expect(fileInput.files?.[0].name).toBe('test.txt'); + + // Simulates an AJAX request triggered by Live rendering + test.expectsAjaxCall() + .expectUpdatedData({}) + .delayResponse(100); + + getByText(test.element, 'Reload').click(); + + // Checks that the file is still present after rendering + await waitFor(() => { + expect(fileInput.files).not.toBeNull(); + expect(fileInput.files?.length).toBe(1); + expect(fileInput.files?.[0].name).toBe('test.txt'); + }); + }); + it('conserves the value of an unmapped field that was modified after a render request', async () => { const test = await createTest( { title: 'greetings' },