From 24672bf6e20afce739b73bb9cae37ad3a2853ca9 Mon Sep 17 00:00:00 2001 From: Sean Doyle Date: Wed, 10 Aug 2022 16:15:58 -0400 Subject: [PATCH] Form Link Submissions: Don't remove `
` until submission is complete When an `` element annotated with `[data-turbo-method]` is clicked, Turbo Drive creates, appends, submits, and removes a `` element behind the scenes. Unfortunately, prior to this commit, the `` element is submitted then removed synchronously and immediately. This means that by the time the `submit` events (and resulting `turbo:before-fetch-request` and `turbo:submit-start` events) bubble up through the document, the `` element is already disconnected. In its absence, the `document.documentElement` serves as the event's target. This commit changes that process to defer the elements removal until _after_ it dispatches a `turbo:submit-end` event. The result is that the element is present throughout all the events, and is accessible through `event.target`. --- src/observers/form_link_click_observer.ts | 4 ++-- src/tests/fixtures/form.html | 2 +- src/tests/fixtures/test.js | 2 ++ src/tests/functional/form_submission_tests.ts | 18 ++++++++++++++++++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/observers/form_link_click_observer.ts b/src/observers/form_link_click_observer.ts index 0e6248bd3..59990627b 100644 --- a/src/observers/form_link_click_observer.ts +++ b/src/observers/form_link_click_observer.ts @@ -51,7 +51,7 @@ export class FormLinkClickObserver implements LinkClickObserverDelegate { this.delegate.submittedFormLinkToLocation(link, location, form) document.body.appendChild(form) - form.requestSubmit() - form.remove() + form.addEventListener("turbo:submit-end", () => form.remove(), { once: true }) + requestAnimationFrame(() => form.requestSubmit()) } } diff --git a/src/tests/fixtures/form.html b/src/tests/fixtures/form.html index 32a20c7ae..9cf0cfeca 100644 --- a/src/tests/fixtures/form.html +++ b/src/tests/fixtures/form.html @@ -1,5 +1,5 @@ - + Form diff --git a/src/tests/fixtures/test.js b/src/tests/fixtures/test.js index 46b7d7af7..c30a5b5d2 100644 --- a/src/tests/fixtures/test.js +++ b/src/tests/fixtures/test.js @@ -46,6 +46,8 @@ "turbo:load", "turbo:render", "turbo:before-fetch-request", + "turbo:submit-start", + "turbo:submit-end", "turbo:before-fetch-response", "turbo:visit", "turbo:before-frame-render", diff --git a/src/tests/functional/form_submission_tests.ts b/src/tests/functional/form_submission_tests.ts index ed5ea5a67..5bc9862ea 100644 --- a/src/tests/functional/form_submission_tests.ts +++ b/src/tests/functional/form_submission_tests.ts @@ -798,6 +798,24 @@ test("test form submission targeting a frame submits the Turbo-Frame header", as assert.ok(fetchOptions.headers["Turbo-Frame"], "submits with the Turbo-Frame header") }) +test("test link method form submission dispatches events from a connected element", async ({ page }) => { + await page.evaluate(() => + new MutationObserver(([record]) => { + for (const form of record.addedNodes) { + if (form instanceof HTMLFormElement) form.id = "a-form-link" + } + }).observe(document.body, { childList: true }) + ) + + await page.click("#stream-link-method-within-form-outside-frame") + await nextEventOnTarget(page, "a-form-link", "turbo:before-fetch-request") + await nextEventOnTarget(page, "a-form-link", "turbo:submit-start") + await nextEventOnTarget(page, "a-form-link", "turbo:before-fetch-response") + await nextEventOnTarget(page, "a-form-link", "turbo:submit-end") + + assert.notOk(await hasSelector(page, "a-form-link"), "the is removed") +}) + test("test link method form submission submits a single request", async ({ page }) => { let requestCounter = 0 page.on("request", () => requestCounter++)