Skip to content

Commit

Permalink
Merge pull request #3 from hotwired/form-submitter-skip
Browse files Browse the repository at this point in the history
Enable opting-out of form submissions
  • Loading branch information
sstephenson committed Dec 31, 2020
2 parents 18b755f + 9fa7201 commit ea00632
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 6 deletions.
10 changes: 5 additions & 5 deletions src/core/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export class Session implements NavigatorDelegate {
// Link click observer delegate

willFollowLinkToLocation(link: Element, location: Location) {
return this.linkIsVisitable(link)
return this.elementIsNavigable(link)
&& this.locationIsVisitable(location)
&& this.applicationAllowsFollowingLinkToLocation(link, location)
}
Expand Down Expand Up @@ -151,8 +151,8 @@ export class Session implements NavigatorDelegate {

// Form submit observer delegate

willSubmitForm(form: HTMLFormElement, submitter?: HTMLElement) {
return true
willSubmitForm(form: HTMLFormElement, submitter?: HTMLElement): boolean {
return this.elementIsNavigable(form) && this.elementIsNavigable(submitter)
}

formSubmitted(form: HTMLFormElement, submitter?: HTMLElement) {
Expand Down Expand Up @@ -246,8 +246,8 @@ export class Session implements NavigatorDelegate {
return isAction(action) ? action : "advance"
}

linkIsVisitable(link: Element) {
const container = link.closest("[data-turbo]")
elementIsNavigable(element?: Element) {
const container = element?.closest("[data-turbo]")
if (container) {
return container.getAttribute("data-turbo") != "false"
} else {
Expand Down
5 changes: 4 additions & 1 deletion src/observers/form_submit_observer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ export class FormSubmitObserver {
if (!event.defaultPrevented) {
const form = event.target instanceof HTMLFormElement ? event.target : undefined
const submitter = event.submitter || undefined

if (form) {
if (this.delegate.willSubmitForm(form, submitter)) {
const method = submitter?.getAttribute("formmethod") || form.method

if (method != "dialog" && this.delegate.willSubmitForm(form, submitter)) {
event.preventDefault()
this.delegate.formSubmitted(form, submitter)
}
Expand Down
34 changes: 34 additions & 0 deletions src/tests/fixtures/form.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
<title>Form</title>
<script src="/src/tests/fixtures/turbo.es2017-umd.js" data-turbo-track="reload"></script>
<script src="/src/tests/fixtures/test.js"></script>
<style>
dialog {
display: block;
position: static;
}
</style>
</head>
<body>
<div id="standard">
Expand All @@ -22,13 +28,41 @@
<input type="submit">
</form>
</div>
<hr>
<div id="submitter">
<form action="/src/tests/fixtures/one.html" method="get">
<button type="submit" formmethod="post" formaction="/__turbo/redirect"
name="path" value="/src/tests/fixtures/two.html">Submit</button>
</form>
</div>
<hr>
<div id="disabled">
<form action="/__turbo/redirect" method="post" data-turbo="false">
<input type="hidden" name="path" value="/src/tests/fixtures/one.html">
<input type="submit">
</form>

<form action="/__turbo/redirect" method="post">
<input type="hidden" name="path" value="/src/tests/fixtures/one.html">
<input type="submit" data-turbo="false">
</form>
</div>
<hr>
<div id="skipped">
<dialog id="dialog-method" open>
<form method="dialog">
<button type="submit">Close</button>
</form>
</dialog>

<dialog id="dialog-formmethod" open>
<form action="/__turbo/redirect" method="post">
<input type="hidden" name="path" value="/src/tests/fixtures/one.html">
<button formmethod="dialog">Close</button>
</form>
</dialog>
</div>
<hr>
<turbo-frame id="frame">
<form action="/__turbo/redirect" method="post" class="redirect">
<input type="hidden" name="path" value="/src/tests/fixtures/frames/form.html">
Expand Down
47 changes: 47 additions & 0 deletions src/tests/functional/form_submission_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ export class FormSubmissionTests extends TurboDriveTestCase {
}

async "test standard form submission with redirect response"() {
this.listenForFormSubmissions()
const button = await this.querySelector("#standard form input[type=submit]")
await button.click()
await this.nextBody

this.assert.ok(this.turboFormSubmitted)
this.assert.equal(await this.pathname, "/src/tests/fixtures/one.html")
this.assert.equal(await this.visitAction, "advance")
}
Expand Down Expand Up @@ -80,6 +82,51 @@ export class FormSubmissionTests extends TurboDriveTestCase {
this.assert.equal(await message.getVisibleText(), "Hello!")
this.assert.equal(await this.pathname, "/src/tests/fixtures/form.html")
}

async "test form submission with Turbo disabled on the form"() {
this.listenForFormSubmissions()
await this.clickSelector('#disabled form[data-turbo="false"] input[type=submit]')
await this.nextBody
await this.querySelector("#element-id")

this.assert.notOk(await this.turboFormSubmitted)
}

async "test form submission with Turbo disabled on the submitter"() {
this.listenForFormSubmissions()
await this.clickSelector('#disabled form:not([data-turbo]) input[data-turbo="false"]')
await this.nextBody
await this.querySelector("#element-id")

this.assert.notOk(await this.turboFormSubmitted)
}

async "test form submission skipped within method=dialog"() {
this.listenForFormSubmissions()
await this.clickSelector('#dialog-method [type="submit"]')
await this.nextBeat

this.assert.notOk(await this.turboFormSubmitted)
}

async "test form submission skipped with submitter formmethod=dialog"() {
this.listenForFormSubmissions()
await this.clickSelector('#dialog-formmethod [formmethod="dialog"]')
await this.nextBeat

this.assert.notOk(await this.turboFormSubmitted)
}

listenForFormSubmissions() {
this.remote.execute(() => addEventListener("turbo:submit-start", function eventListener(event) {
removeEventListener("turbo:submit-start", eventListener, false)
document.head.insertAdjacentHTML("beforeend", `<meta name="turbo-form-submitted">`)
}, false))
}

get turboFormSubmitted(): Promise<boolean> {
return this.hasSelector("meta[name=turbo-form-submitted]")
}
}

FormSubmissionTests.registerSuite()

0 comments on commit ea00632

Please sign in to comment.