Skip to content

Commit 6646957

Browse files
Merge pull request #1216 from seanpdoyle/config-disable-submitter
Configure Submitter disabling
2 parents f88bfe4 + 145faee commit 6646957

File tree

10 files changed

+162
-20
lines changed

10 files changed

+162
-20
lines changed

src/core/config/drive.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const drive = {
2+
enabled: true,
3+
progressBarDelay: 500
4+
}

src/core/config/forms.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { cancelEvent } from "../../util"
2+
3+
const submitter = {
4+
"aria-disabled": {
5+
beforeSubmit: submitter => {
6+
submitter.setAttribute("aria-disabled", "true")
7+
submitter.addEventListener("click", cancelEvent)
8+
},
9+
10+
afterSubmit: submitter => {
11+
submitter.removeAttribute("aria-disabled")
12+
submitter.removeEventListener("click", cancelEvent)
13+
}
14+
},
15+
16+
"disabled": {
17+
beforeSubmit: submitter => submitter.disabled = true,
18+
afterSubmit: submitter => submitter.disabled = false
19+
}
20+
}
21+
22+
class Config {
23+
#submitter = null
24+
25+
constructor(config) {
26+
Object.assign(this, config)
27+
}
28+
29+
get submitter() {
30+
return this.#submitter
31+
}
32+
33+
set submitter(value) {
34+
this.#submitter = submitter[value] || value
35+
}
36+
}
37+
38+
export const forms = new Config({
39+
mode: "on",
40+
submitter: "disabled"
41+
})

src/core/config/index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { drive } from "./drive"
2+
import { forms } from "./forms"
3+
4+
export const config = {
5+
drive,
6+
forms
7+
}

src/core/drive/form_submission.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { expandURL } from "../url"
33
import { clearBusyState, dispatch, getAttribute, getMetaContent, hasAttribute, markAsBusy } from "../../util"
44
import { StreamMessage } from "../streams/stream_message"
55
import { prefetchCache } from "./prefetch_cache"
6+
import { config } from "../config"
67

78
export const FormSubmissionState = {
89
initialized: "initialized",
@@ -22,7 +23,7 @@ export const FormEnctype = {
2223
export class FormSubmission {
2324
state = FormSubmissionState.initialized
2425

25-
static confirmMethod(message, _element, _submitter) {
26+
static confirmMethod(message) {
2627
return Promise.resolve(confirm(message))
2728
}
2829

@@ -78,7 +79,11 @@ export class FormSubmission {
7879
const confirmationMessage = getAttribute("data-turbo-confirm", this.submitter, this.formElement)
7980

8081
if (typeof confirmationMessage === "string") {
81-
const answer = await FormSubmission.confirmMethod(confirmationMessage, this.formElement, this.submitter)
82+
const confirmMethod = typeof config.forms.confirm === "function" ?
83+
config.forms.confirm :
84+
FormSubmission.confirmMethod
85+
86+
const answer = await confirmMethod(confirmationMessage, this.formElement, this.submitter)
8287
if (!answer) {
8388
return
8489
}
@@ -116,7 +121,7 @@ export class FormSubmission {
116121

117122
requestStarted(_request) {
118123
this.state = FormSubmissionState.waiting
119-
this.submitter?.setAttribute("disabled", "")
124+
if (this.submitter) config.forms.submitter.beforeSubmit(this.submitter)
120125
this.setSubmitsWith()
121126
markAsBusy(this.formElement)
122127
dispatch("turbo:submit-start", {
@@ -162,7 +167,7 @@ export class FormSubmission {
162167

163168
requestFinished(_request) {
164169
this.state = FormSubmissionState.stopped
165-
this.submitter?.removeAttribute("disabled")
170+
if (this.submitter) config.forms.submitter.afterSubmit(this.submitter)
166171
this.resetSubmitterText()
167172
clearBusyState(this.formElement)
168173
dispatch("turbo:submit-end", {

src/core/index.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import { Session } from "./session"
22
import { PageRenderer } from "./drive/page_renderer"
33
import { PageSnapshot } from "./drive/page_snapshot"
44
import { FrameRenderer } from "./frames/frame_renderer"
5-
import { FormSubmission } from "./drive/form_submission"
65
import { fetch, recentRequests } from "../http/fetch"
6+
import { config } from "./config"
77

88
const session = new Session(recentRequests)
99
const { cache, navigator } = session
10-
export { navigator, session, cache, PageRenderer, PageSnapshot, FrameRenderer, fetch }
10+
export { navigator, session, cache, PageRenderer, PageSnapshot, FrameRenderer, fetch, config }
1111

1212
/**
1313
* Starts the main session.
@@ -97,13 +97,22 @@ export function clearCache() {
9797
* @param delay Time to delay in milliseconds
9898
*/
9999
export function setProgressBarDelay(delay) {
100-
session.setProgressBarDelay(delay)
100+
console.warn(
101+
"Please replace `Turbo.setProgressBarDelay(delay)` with `Turbo.config.drive.progressBarDelay = delay`. The top-level function is deprecated and will be removed in a future version of Turbo.`"
102+
)
103+
config.drive.progressBarDelay = delay
101104
}
102105

103106
export function setConfirmMethod(confirmMethod) {
104-
FormSubmission.confirmMethod = confirmMethod
107+
console.warn(
108+
"Please replace `Turbo.setConfirmMethod(confirmMethod)` with `Turbo.config.forms.confirm = confirmMethod`. The top-level function is deprecated and will be removed in a future version of Turbo.`"
109+
)
110+
config.forms.confirm = confirmMethod
105111
}
106112

107113
export function setFormMode(mode) {
108-
session.setFormMode(mode)
114+
console.warn(
115+
"Please replace `Turbo.setFormMode(mode)` with `Turbo.config.forms.mode = mode`. The top-level function is deprecated and will be removed in a future version of Turbo.`"
116+
)
117+
config.forms.mode = mode
109118
}

src/core/session.js

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { PageView } from "./drive/page_view"
1818
import { FrameElement } from "../elements/frame_element"
1919
import { Preloader } from "./drive/preloader"
2020
import { Cache } from "./cache"
21+
import { config } from "./config"
2122

2223
export class Session {
2324
navigator = new Navigator(this)
@@ -37,11 +38,8 @@ export class Session {
3738
streamMessageRenderer = new StreamMessageRenderer()
3839
cache = new Cache(this)
3940

40-
drive = true
4141
enabled = true
42-
progressBarDelay = 500
4342
started = false
44-
formMode = "on"
4543
#pageRefreshDebouncePeriod = 150
4644

4745
constructor(recentRequests) {
@@ -131,11 +129,19 @@ export class Session {
131129
}
132130

133131
setProgressBarDelay(delay) {
132+
console.warn(
133+
"Please replace `session.setProgressBarDelay(delay)` with `session.progressBarDelay = delay`. The function is deprecated and will be removed in a future version of Turbo.`"
134+
)
135+
134136
this.progressBarDelay = delay
135137
}
136138

137-
setFormMode(mode) {
138-
this.formMode = mode
139+
set progressBarDelay(delay) {
140+
config.drive.progressBarDelay = delay
141+
}
142+
143+
get progressBarDelay() {
144+
return config.drive.progressBarDelay
139145
}
140146

141147
get location() {
@@ -425,12 +431,12 @@ export class Session {
425431
// Helpers
426432

427433
submissionIsNavigatable(form, submitter) {
428-
if (this.formMode == "off") {
434+
if (config.forms.mode == "off") {
429435
return false
430436
} else {
431437
const submitterIsNavigatable = submitter ? this.elementIsNavigatable(submitter) : true
432438

433-
if (this.formMode == "optin") {
439+
if (config.forms.mode == "optin") {
434440
return submitterIsNavigatable && form.closest('[data-turbo="true"]') != null
435441
} else {
436442
return submitterIsNavigatable && this.elementIsNavigatable(form)
@@ -443,7 +449,7 @@ export class Session {
443449
const withinFrame = findClosestRecursively(element, "turbo-frame")
444450

445451
// Check if Drive is enabled on the session or we're within a Frame.
446-
if (this.drive || withinFrame) {
452+
if (config.drive.enabled || withinFrame) {
447453
// Element is navigatable by default, unless `data-turbo="false"`.
448454
if (container) {
449455
return container.getAttribute("data-turbo") != "false"

src/tests/fixtures/drive_disabled.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
})
1616
</script>
1717
<script>
18-
Turbo.session.drive = false
18+
Turbo.config.drive = false
1919
</script>
2020
</head>
2121
<body>

src/tests/functional/form_submission_tests.js

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,22 @@ test("standard POST form submission toggles submitter [disabled] attribute", asy
240240
)
241241
})
242242

243+
test("standard POST form submission toggles submitter [aria-disabled=true] attribute", async ({ page }) => {
244+
await page.evaluate(() => window.Turbo.config.forms.submitter = "aria-disabled")
245+
await page.click("#standard-post-form-submit")
246+
247+
assert.equal(
248+
await nextAttributeMutationNamed(page, "standard-post-form-submit", "aria-disabled"),
249+
"true",
250+
"sets [aria-disabled=true] on the submitter"
251+
)
252+
assert.equal(
253+
await nextAttributeMutationNamed(page, "standard-post-form-submit", "aria-disabled"),
254+
null,
255+
"removes [aria-disabled] from the submitter"
256+
)
257+
})
258+
243259
test("replaces input value with data-turbo-submits-with on form submission", async ({ page }) => {
244260
page.click("#submits-with-form-input")
245261

@@ -410,6 +426,22 @@ test("standard GET form submission toggles submitter [disabled] attribute", asyn
410426
)
411427
})
412428

429+
test("standard GET form submission toggles submitter [aria-disabled] attribute", async ({ page }) => {
430+
await page.evaluate(() => window.Turbo.config.forms.submitter = "aria-disabled")
431+
await page.click("#standard-get-form-submit")
432+
433+
assert.equal(
434+
await nextAttributeMutationNamed(page, "standard-get-form-submit", "aria-disabled"),
435+
"true",
436+
"sets [aria-disabled] on the submitter"
437+
)
438+
assert.equal(
439+
await nextAttributeMutationNamed(page, "standard-get-form-submit", "aria-disabled"),
440+
null,
441+
"removes [aria-disabled] from the submitter"
442+
)
443+
})
444+
413445
test("standard GET form submission appending keys", async ({ page }) => {
414446
await page.goto("/src/tests/fixtures/form.html?query=1")
415447
await page.click("#standard form.conflicting-values input[type=submit]")
@@ -692,6 +724,22 @@ test("frame POST form targeting frame toggles submitter's [disabled] attribute",
692724
)
693725
})
694726

727+
test("frame POST form targeting frame toggles submitter's [aria-disabled] attribute", async ({ page }) => {
728+
await page.evaluate(() => window.Turbo.config.forms.submitter = "aria-disabled")
729+
await page.click("#targets-frame-post-form-submit")
730+
731+
assert.equal(
732+
await nextAttributeMutationNamed(page, "targets-frame-post-form-submit", "aria-disabled"),
733+
"true",
734+
"sets [aria-disabled] on the submitter"
735+
)
736+
assert.equal(
737+
await nextAttributeMutationNamed(page, "targets-frame-post-form-submit", "aria-disabled"),
738+
null,
739+
"removes [aria-disabled] from the submitter"
740+
)
741+
})
742+
695743
test("frame GET form targeting frame submission", async ({ page }) => {
696744
await page.click("#targets-frame-get-form-submit")
697745

@@ -731,6 +779,22 @@ test("frame GET form targeting frame toggles submitter's [disabled] attribute",
731779
)
732780
})
733781

782+
test("frame GET form targeting frame toggles submitter's [aria-disabled] attribute", async ({ page }) => {
783+
await page.evaluate(() => window.Turbo.config.forms.submitter = "aria-disabled")
784+
await page.click("#targets-frame-get-form-submit")
785+
786+
assert.equal(
787+
await nextAttributeMutationNamed(page, "targets-frame-get-form-submit", "aria-disabled"),
788+
"true",
789+
"sets [aria-disabled] on the submitter"
790+
)
791+
assert.equal(
792+
await nextAttributeMutationNamed(page, "targets-frame-get-form-submit", "aria-disabled"),
793+
null,
794+
"removes [aria-disabled] from the submitter"
795+
)
796+
})
797+
734798
test("frame form GET submission from submitter referencing another frame", async ({ page }) => {
735799
await page.click("#frame form[method=get] [type=submit][data-turbo-frame=hello]")
736800
await nextBeat()
@@ -1142,7 +1206,7 @@ test("following a link with [data-turbo-method] and [data-turbo=true] set when h
11421206
test("following a link with [data-turbo-method] and [data-turbo=true] set when Turbo.session.drive = false", async ({
11431207
page
11441208
}) => {
1145-
await page.evaluate(() => (window.Turbo.session.drive = false))
1209+
await page.evaluate(() => (window.Turbo.config.drive = false))
11461210

11471211
const link = await page.locator("#turbo-method-post-to-targeted-frame")
11481212
await link.evaluate((link) => link.setAttribute("data-turbo", "true"))
@@ -1163,7 +1227,7 @@ test("following a link with [data-turbo-method] set when html[data-turbo=false]"
11631227
})
11641228

11651229
test("following a link with [data-turbo-method] set when Turbo.session.drive = false", async ({ page }) => {
1166-
await page.evaluate(() => (window.Turbo.session.drive = false))
1230+
await page.evaluate(() => (window.Turbo.config.drive = false))
11671231
await page.click("#turbo-method-post-to-targeted-frame")
11681232

11691233
assert.equal(await page.textContent("h1"), "Hello", "treats link full-page navigation")

src/tests/unit/export_tests.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ test("Turbo interface", () => {
1515
assert.equal(typeof Turbo.setConfirmMethod, "function")
1616
assert.equal(typeof Turbo.setFormMode, "function")
1717
assert.equal(typeof Turbo.cache, "object")
18+
assert.equal(typeof Turbo.config, "object")
1819
assert.equal(typeof Turbo.cache.clear, "function")
1920
assert.equal(typeof Turbo.navigator, "object")
2021
assert.equal(typeof Turbo.session, "object")

src/util.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ export function dispatch(eventName, { target, cancelable, detail } = {}) {
4545
return event
4646
}
4747

48+
export function cancelEvent(event) {
49+
event.preventDefault()
50+
event.stopImmediatePropagation()
51+
}
52+
4853
export function nextRepaint() {
4954
if (document.visibilityState === "hidden") {
5055
return nextEventLoopTick()

0 commit comments

Comments
 (0)