From 9581f5449c8861b79ee41f4c9e8a0792402efae9 Mon Sep 17 00:00:00 2001 From: Johannes Raggam Date: Fri, 5 Jun 2026 13:01:30 +0200 Subject: [PATCH] feat: Allow for default to open in new window. The link panel allows now to "Open in new window" to be set as default. --- src/extensions/link.js | 6 +- src/index.html | 3 +- src/tiptap.test.js | 121 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 3 deletions(-) diff --git a/src/extensions/link.js b/src/extensions/link.js index daef7ce..ac7ea23 100644 --- a/src/extensions/link.js +++ b/src/extensions/link.js @@ -115,8 +115,10 @@ function link_panel({ app }) { link_href.value = attrs.href; link_href.dispatchEvent(new Event("input")); } - if (attrs?.target && link_target) { - link_target.checked = true; + + // Set the target checkbox in the link modal to checked or unchecked. + if (is_link && link_target) { + link_target.checked = !!attrs?.target; link_target.dispatchEvent(new Event("input")); } diff --git a/src/index.html b/src/index.html index 7f762e2..16a424c 100644 --- a/src/index.html +++ b/src/index.html @@ -164,6 +164,7 @@

TipTap basic example

>

Visit pat-tiptap on GitHub.

+

Visit Patternslib.com in a new window.

Hello, Add / Edit Link diff --git a/src/tiptap.test.js b/src/tiptap.test.js index 654606e..c392e1e 100644 --- a/src/tiptap.test.js +++ b/src/tiptap.test.js @@ -569,6 +569,127 @@ describe("pat-tiptap", () => { }); }); + describe("5b - Link target tests", () => { + // The link panel's "Open in new window" checkbox is ``checked`` by + // default in these tests. + // When editing an existing link, the checkbox must always reflect the + // link's actual ``target`` attribute - even if the default is + // ``checked``. Otherwise a default-checked checkbox could not be unset + // for links which do not open in a new window. + + const setup = async ({ content = "" } = {}) => { + document.body.innerHTML = ` +

+ Link +
+ + + `; + const pattern = new Pattern(document.querySelector(".pat-tiptap")); + await events.await_pattern_init(pattern); + + const button_link = document.querySelector("#tiptap-external-toolbar .button-link"); // prettier-ignore + await events.await_pattern_init(new PatternModal(button_link)); + + return { pattern, button_link }; + }; + + const open_panel = async (button_link) => { + button_link.click(); + await events.await_event(document, "patterns-injected-delayed"); + await utils.timeout(1); + }; + + afterEach(() => { + document.body.innerHTML = ""; + }); + + it("5b.1 - A new link defaults to open in a new window when the checkbox is checked by default", async () => { + const { button_link } = await setup(); + + document.querySelector(".tiptap-container [contenteditable]").focus(); // Set focus to bypass toolbar check + await open_panel(button_link); + + // The checkbox is checked by default. + expect(document.querySelector("#link-panel [name=tiptap-target]").checked).toBe(true); // prettier-ignore + + document.querySelector("#link-panel [name=tiptap-href]").value = "https://patternslib.com/"; // prettier-ignore + document.querySelector("#link-panel [name=tiptap-text]").value = "Link text"; // prettier-ignore + document.querySelector("#link-panel [name=tiptap-confirm]").dispatchEvent(new Event("click")); // prettier-ignore + await utils.timeout(1); + + const anchor = document.querySelector(".tiptap-container a"); + expect(anchor).toBeTruthy(); + expect(anchor.getAttribute("target")).toBe("_blank"); + }); + + it("5b.2 - Editing a link without a target unsets the default-checked checkbox", async () => { + const { pattern, button_link } = await setup({ + content: `Link text`, + }); + + // Place the cursor within the existing link. + document.querySelector(".tiptap-container [contenteditable]").focus(); // Set focus to bypass toolbar check + pattern.editor.commands.setTextSelection(3); + + await open_panel(button_link); + + // Even though the checkbox is ``checked`` by default, it must be + // unset because the edited link has no target. + expect(document.querySelector("#link-panel [name=tiptap-target]").checked).toBe(false); // prettier-ignore + + // Confirming keeps the link without a target. + document.querySelector("#link-panel [name=tiptap-confirm]").dispatchEvent(new Event("click")); // prettier-ignore + await utils.timeout(1); + + const anchor = document.querySelector(".tiptap-container a"); + expect(anchor).toBeTruthy(); + expect(anchor.hasAttribute("target")).toBe(false); + }); + + it("5b.3 - Editing a link with a target checks the checkbox and allows unsetting the target", async () => { + const { pattern, button_link } = await setup({ + content: `Link text`, + }); + + // Place the cursor within the existing link. + document.querySelector(".tiptap-container [contenteditable]").focus(); // Set focus to bypass toolbar check + pattern.editor.commands.setTextSelection(3); + + await open_panel(button_link); + + // The checkbox reflects the link's existing target. + expect(document.querySelector("#link-panel [name=tiptap-target]").checked).toBe(true); // prettier-ignore + + // Unset the target and confirm. + document.querySelector("#link-panel [name=tiptap-target]").checked = false; + document.querySelector("#link-panel [name=tiptap-confirm]").dispatchEvent(new Event("click")); // prettier-ignore + await utils.timeout(1); + + const anchor = document.querySelector(".tiptap-container a"); + expect(anchor).toBeTruthy(); + expect(anchor.hasAttribute("target")).toBe(false); + }); + }); + it("6.1 - Adds an image within
tags including a
", async () => { document.body.innerHTML = `