diff --git a/src/core/confirm.ts b/src/core/confirm.ts new file mode 100644 index 000000000..b58fb9449 --- /dev/null +++ b/src/core/confirm.ts @@ -0,0 +1,5 @@ +export class Confirm { + static confirmMethod(message: string, _element: Element, _submitter: Element | undefined): Promise { + return Promise.resolve(confirm(message)) + } +} diff --git a/src/core/drive/form_submission.ts b/src/core/drive/form_submission.ts index 1cfc1a247..1a2bf1728 100644 --- a/src/core/drive/form_submission.ts +++ b/src/core/drive/form_submission.ts @@ -1,5 +1,6 @@ import { FetchRequest, FetchMethod, fetchMethodFromString } from "../../http/fetch_request" import { FetchResponse } from "../../http/fetch_response" +import { Confirm } from "../confirm" import { expandURL } from "../url" import { dispatch, getAttribute, getMetaContent, hasAttribute } from "../../util" import { StreamMessage } from "../streams/stream_message" @@ -56,14 +57,6 @@ export class FormSubmission { state = FormSubmissionState.initialized result?: FormSubmissionResult - static confirmMethod( - message: string, - _element: HTMLFormElement, - _submitter: HTMLElement | undefined - ): Promise { - return Promise.resolve(confirm(message)) - } - constructor( delegate: FormSubmissionDelegate, formElement: HTMLFormElement, @@ -126,7 +119,7 @@ export class FormSubmission { const confirmationMessage = getAttribute("data-turbo-confirm", this.submitter, this.formElement) if (typeof confirmationMessage === "string") { - const answer = await FormSubmission.confirmMethod(confirmationMessage, this.formElement, this.submitter) + const answer = await Confirm.confirmMethod(confirmationMessage, this.formElement, this.submitter) if (!answer) { return } diff --git a/src/core/index.ts b/src/core/index.ts index 7b7507c3f..38118aac9 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,6 +1,7 @@ import { Adapter } from "./native/adapter" import { FormMode, Session } from "./session" import { Cache } from "./cache" +import { Confirm } from "./confirm" import { Locatable } from "./url" import { StreamMessage } from "./streams/stream_message" import { StreamSource } from "./types" @@ -8,7 +9,6 @@ import { VisitOptions } from "./drive/visit" import { PageRenderer } from "./drive/page_renderer" import { PageSnapshot } from "./drive/page_snapshot" import { FrameRenderer } from "./frames/frame_renderer" -import { FormSubmission } from "./drive/form_submission" const session = new Session() const cache = new Cache(session) @@ -125,9 +125,9 @@ export function setProgressBarDelay(delay: number) { } export function setConfirmMethod( - confirmMethod: (message: string, element: HTMLFormElement, submitter: HTMLElement | undefined) => Promise + confirmMethod: (message: string, element: HTMLElement, submitter: HTMLElement | undefined) => Promise ) { - FormSubmission.confirmMethod = confirmMethod + Confirm.confirmMethod = confirmMethod } export function setFormMode(mode: FormMode) { diff --git a/src/core/session.ts b/src/core/session.ts index d988ef3bb..576d29106 100644 --- a/src/core/session.ts +++ b/src/core/session.ts @@ -1,6 +1,7 @@ import { Adapter } from "./native/adapter" import { BrowserAdapter, ReloadReason } from "./native/browser_adapter" import { CacheObserver } from "../observers/cache_observer" +import { Confirm } from "./confirm" import { FormSubmitObserver, FormSubmitObserverDelegate } from "../observers/form_submit_observer" import { FrameRedirector } from "./frames/frame_redirector" import { History, HistoryDelegate } from "./drive/history" @@ -191,9 +192,17 @@ export class Session ) } - followedLinkToLocation(link: Element, location: URL) { + async followedLinkToLocation(link: Element, location: URL) { const action = this.getActionForLink(link) const acceptsStreamResponse = link.hasAttribute("data-turbo-stream") + const confirmationMessage = link.getAttribute("data-turbo-confirm") + + if (typeof confirmationMessage === "string") { + const answer = await Confirm.confirmMethod(confirmationMessage, link, link) + if (!answer) { + return + } + } this.visit(location.href, { action, acceptsStreamResponse }) } diff --git a/src/tests/functional/visit_tests.ts b/src/tests/functional/visit_tests.ts index 146bcda58..d8b082444 100644 --- a/src/tests/functional/visit_tests.ts +++ b/src/tests/functional/visit_tests.ts @@ -223,3 +223,28 @@ test("test Visit with network error", async ({ page }) => { async function visitLocation(page: Page, location: string) { return page.evaluate((location) => window.Turbo.visit(location), location) } + +test("test data-turbo-confirm on anchor element without data-turbo-method", async ({ page }) => { + let confirmed = false + + page.on("dialog", (alert) => { + assert.equal(alert.message(), "Are you sure?") + alert.accept() + confirmed = true + }) + + await page.evaluate(() => { + const link = document.querySelector("#same-origin-link") + + if (link) link.dataset.turboConfirm = "Are you sure?" + }) + + assert.equal(await page.locator("#same-origin-link[data-turbo-confirm]:not([data-turbo-method])").count(), 1) + assert.equal(pathname(page.url()), "/src/tests/fixtures/visit.html") + + await page.click("#same-origin-link") + await nextEventNamed(page, "turbo:load") + + assert.isTrue(confirmed) + assert.equal(pathname(page.url()), "/src/tests/fixtures/one.html") +})