Skip to content

Commit

Permalink
Use navigatable associated form unless submitter opts out (#1246)
Browse files Browse the repository at this point in the history
When using a submitter with [form=id] attribute, it should respect the
form's navigatable state unless the submitter explicitly opts out of
turbo.

Implements the expected behavior outlined in this comment:
#1246 (comment)
  • Loading branch information
oliverguenther committed Apr 23, 2024
1 parent 9fb05e3 commit 15db627
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/core/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,8 @@ export class Session {
submissionIsNavigatable(form, submitter) {
if (this.formMode == "off") {
return false
} else if (this.submitterReferencesNavigatable(form, submitter)) {
return true
} else {
const submitterIsNavigatable = submitter ? this.elementIsNavigatable(submitter) : true

Expand All @@ -438,6 +440,13 @@ export class Session {
}
}

submitterReferencesNavigatable(form, submitter) {
const formReference = submitter.hasAttribute("form")
const optedOut = submitter.getAttribute("data-turbo") == "false"

return formReference && !optedOut && this.elementIsNavigatable(form)
}

elementIsNavigatable(element) {
const container = findClosestRecursively(element, "[data-turbo]")
const withinFrame = findClosestRecursively(element, "turbo-frame")
Expand Down
17 changes: 17 additions & 0 deletions src/tests/fixtures/form_turbo_optin_submitter.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Form</title>
<script src="/dist/turbo.es2017-umd.js" data-turbo-track="reload"></script>
<script src="/src/tests/fixtures/test.js"></script>
</head>
<body data-turbo="false">
<form id="optin-form" data-turbo="true" action="/__turbo/redirect" method="post" class="redirect">
<input type="hidden" name="path" value="/src/tests/fixtures/form.html">
<input type="hidden" name="greeting" value="Hello from a redirect">
</form>
<button id="default-behavior-submit" form="optin-form" type="submit" name="button">Submit</button>
<button id="opted-out-submit" data-turbo="false" form="optin-form" type="submit" name="button">Submit</button>
</body>
</html>
38 changes: 38 additions & 0 deletions src/tests/functional/form_turbo_option_submitter_tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { test } from "@playwright/test"
import { assert } from "chai"
import { getFromLocalStorage, nextEventNamed, readEventLogs, setLocalStorageFromEvent } from "../helpers/page"

test.beforeEach(async ({ page }) => {
await page.goto("/src/tests/fixtures/form_turbo_optin_submitter.html")
await setLocalStorageFromEvent(page, "turbo:submit-start", "formSubmitStarted", "true")
await setLocalStorageFromEvent(page, "turbo:submit-end", "formSubmitEnded", "true")
await readEventLogs(page)
})

test("allows submitting an associated data-turbo=true form without data-turbo=true on submitter", async ({ page }) => {
await page.click("#default-behavior-submit")

assert.ok(await formSubmitStarted(page), "fires turbo:submit-start")

const { fetchOptions } = await nextEventNamed(page, "turbo:before-fetch-request")

assert.ok(fetchOptions.headers["Accept"].includes("text/vnd.turbo-stream.html"))

await nextEventNamed(page, "turbo:before-fetch-response")

assert.ok(await formSubmitEnded(page), "fires turbo:submit-end")
})

test("prevents submitting an associated data-turbo=true form when explicitly opted out", async ({ page }) => {
await page.click("#opted-out-submit")

assert.notOk(await formSubmitStarted(page), "fires turbo:submit-start")
})

function formSubmitStarted(page) {
return getFromLocalStorage(page, "formSubmitStarted")
}

function formSubmitEnded(page) {
return getFromLocalStorage(page, "formSubmitEnded")
}

0 comments on commit 15db627

Please sign in to comment.