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( + {}, + () => ` + <div ${initComponent({}, { debounce: 1 })}> + <input type="file" data-model="file"> + <button data-action="live#$render">Reload</button> + </div> + ` + ); + + 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' },