Skip to content

Commit

Permalink
Form Link Submissions: Don't remove <form> until submission is comp…
Browse files Browse the repository at this point in the history
…lete

When an `<a>` element annotated with `[data-turbo-method]` is clicked,
Turbo Drive creates, appends, submits, and removes a `<form>` element
behind the scenes.

Unfortunately, prior to this commit, the `<form>` 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
`<form>` 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`.
  • Loading branch information
seanpdoyle committed Aug 10, 2022
1 parent f177ee7 commit 1b30e2e
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 3 deletions.
4 changes: 2 additions & 2 deletions src/observers/form_link_click_observer.ts
Expand Up @@ -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())
}
}
2 changes: 1 addition & 1 deletion src/tests/fixtures/form.html
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html id="html">
<html id="html" data-skip-event-details="turbo:submit-start turbo:submit-end">
<head>
<meta charset="utf-8">
<title>Form</title>
Expand Down
2 changes: 2 additions & 0 deletions src/tests/fixtures/test.js
Expand Up @@ -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",
Expand Down
18 changes: 18 additions & 0 deletions src/tests/functional/form_submission_tests.ts
Expand Up @@ -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 <form> 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 <form> is removed")
})

test("test link method form submission submits a single request", async ({ page }) => {
let requestCounter = 0
page.on("request", () => requestCounter++)
Expand Down

0 comments on commit 1b30e2e

Please sign in to comment.