diff --git a/src/core/session.ts b/src/core/session.ts index 2e68cacd6..388277b16 100644 --- a/src/core/session.ts +++ b/src/core/session.ts @@ -230,7 +230,7 @@ export class Session return ( this.elementIsNavigatable(form) && - (!submitter || this.formElementIsNavigatable(submitter)) && + (!submitter || this.submitterIsNavigatable(submitter)) && locationIsVisitable(expandURL(action), this.snapshot.rootLocation) ) } @@ -382,13 +382,12 @@ export class Session // Helpers - formElementIsNavigatable(element?: Element) { + submitterIsNavigatable(element: Element) { if (this.formMode == "off") { return false } - if (this.formMode == "optin") { - const form = element?.closest("form[data-turbo]") - return form?.getAttribute("data-turbo") == "true" + if (this.formMode == "optin" && hasForm(element) && element.form) { + return element.form.closest('[data-turbo="true"]') != null } return this.elementIsNavigatable(element) } @@ -449,3 +448,7 @@ const deprecatedLocationPropertyDescriptors = { }, }, } + +function hasForm(element: Element): element is Element & { form: HTMLFormElement | null } { + return "form" in element +} diff --git a/src/tests/fixtures/form.html b/src/tests/fixtures/form.html index 937b85285..db01a9914 100644 --- a/src/tests/fixtures/form.html +++ b/src/tests/fixtures/form.html @@ -82,10 +82,12 @@

Form

-
+
+ +
diff --git a/src/tests/functional/form_submission_tests.ts b/src/tests/functional/form_submission_tests.ts index ae7b7a638..787ec2408 100644 --- a/src/tests/functional/form_submission_tests.ts +++ b/src/tests/functional/form_submission_tests.ts @@ -1037,6 +1037,13 @@ test("test form submission with form mode optin and form enabled", async ({ page assert.ok(await formSubmitStarted(page)) }) +test("test form submission with form mode optin and form enabled from submitter outside form", async ({ page }) => { + await page.evaluate(() => window.Turbo.setFormMode("optin")) + await page.click("#standard button[form=turbo-enabled-form]") + + assert.ok(await formSubmitStarted(page)) +}) + test("test turbo:before-fetch-request fires on the form element", async ({ page }) => { await page.click('#targets-frame form.one [type="submit"]') assert.ok(await nextEventOnTarget(page, "form_one", "turbo:before-fetch-request"))