diff --git a/packages/core/src/comments/extension.ts b/packages/core/src/comments/extension.ts index 515a740c34..8d23c3e967 100644 --- a/packages/core/src/comments/extension.ts +++ b/packages/core/src/comments/extension.ts @@ -158,6 +158,8 @@ export const CommentsExtension = createExtension( return { key: "comments", store, + runsBefore: ["link"], + tiptapExtensions: [CommentMark], prosemirrorPlugins: [ new Plugin({ key: PLUGIN_KEY, @@ -224,7 +226,7 @@ export const CommentsExtension = createExtension( }, handleClick: (view, pos, event) => { if (event.button !== 0) { - return; + return false; } const node = view.state.doc.nodeAt(pos); @@ -235,7 +237,7 @@ export const CommentsExtension = createExtension( ...prev, selectedThreadId: undefined, })); - return; + return false; } const commentMark = node.marks.find( @@ -243,15 +245,33 @@ export const CommentsExtension = createExtension( mark.type.name === markType && mark.attrs.orphan !== true, ); - const threadId = commentMark?.attrs.threadId as - | string - | undefined; - if (threadId !== store.state.selectedThreadId) { - store.setState((prev) => ({ - ...prev, - selectedThreadId: threadId, - })); + if (!commentMark) { + // Clicked outside any comment thread. Deselect if needed but + // don't consume the event so other handlers (e.g. link + // navigation) can process it. + if (store.state.selectedThreadId !== undefined) { + store.setState((prev) => ({ + ...prev, + selectedThreadId: undefined, + })); + } + return false; + } + + const threadId = commentMark.attrs.threadId as string; + + // If the clicked thread is already selected, do nothing and let + // other handlers process the event (e.g. navigating a link). + if (threadId === store.state.selectedThreadId) { + return false; } + + store.setState((prev) => ({ + ...prev, + selectedThreadId: threadId, + })); + + return true; }, }, }), @@ -356,7 +376,6 @@ export const CommentsExtension = createExtension( }, userStore, commentEditorSchema, - tiptapExtensions: [CommentMark], } as const; }, ); diff --git a/packages/core/src/editor/managers/ExtensionManager/extensions.ts b/packages/core/src/editor/managers/ExtensionManager/extensions.ts index 21d2d86f91..7be7070865 100644 --- a/packages/core/src/editor/managers/ExtensionManager/extensions.ts +++ b/packages/core/src/editor/managers/ExtensionManager/extensions.ts @@ -5,7 +5,7 @@ import { Extension as TiptapExtension, } from "@tiptap/core"; import { Gapcursor } from "@tiptap/extensions/gap-cursor"; -import { Link } from "../../../extensions/tiptap-extensions/Link/link.js"; +import { LinkExtension } from "../../../extensions/tiptap-extensions/Link/link.js"; import { Text } from "@tiptap/extension-text"; import { createDropFileExtension } from "../../../api/clipboard/fromClipboard/fileDropExtension.js"; import { createPasteFromClipboardExtension } from "../../../api/clipboard/fromClipboard/pasteExtension.js"; @@ -73,14 +73,6 @@ export function getDefaultTiptapExtensions( SuggestionAddMark, SuggestionDeleteMark, SuggestionModificationMark, - Link.configure({ - HTMLAttributes: options.links?.HTMLAttributes ?? {}, - editor, - onClick: options.links?.onClick, - ...(options.links?.isValidLink - ? { isValidLink: options.links.isValidLink } - : {}), - }), ...(Object.values(editor.schema.styleSpecs).map((styleSpec) => { return styleSpec.implementation.mark.configure({ editor: editor, @@ -169,6 +161,13 @@ export function getDefaultExtensions( DropCursorExtension(options), FilePanelExtension(options), FormattingToolbarExtension(options), + LinkExtension({ + HTMLAttributes: options.links?.HTMLAttributes ?? {}, + onClick: options.links?.onClick, + ...(options.links?.isValidLink + ? { isValidLink: options.links.isValidLink } + : {}), + }), LinkToolbarExtension(options), NodeSelectionKeyboardExtension(), PlaceholderExtension(options), diff --git a/packages/core/src/extensions/tiptap-extensions/Link/index.ts b/packages/core/src/extensions/tiptap-extensions/Link/index.ts index 5324cbbe38..bc54c007eb 100644 --- a/packages/core/src/extensions/tiptap-extensions/Link/index.ts +++ b/packages/core/src/extensions/tiptap-extensions/Link/index.ts @@ -1,2 +1,2 @@ -export { Link } from "./link.js"; +export { Link, LinkExtension } from "./link.js"; export { isAllowedUri } from "./link.js"; diff --git a/packages/core/src/extensions/tiptap-extensions/Link/link.ts b/packages/core/src/extensions/tiptap-extensions/Link/link.ts index 5f68082b9f..1769dfa544 100644 --- a/packages/core/src/extensions/tiptap-extensions/Link/link.ts +++ b/packages/core/src/extensions/tiptap-extensions/Link/link.ts @@ -2,6 +2,7 @@ import type { PasteRuleMatch } from "@tiptap/core"; import { Mark, markPasteRule, mergeAttributes } from "@tiptap/core"; import type { Plugin } from "@tiptap/pm/state"; import type { BlockNoteEditor } from "../../../editor/BlockNoteEditor.js"; +import { createExtension } from "../../../editor/BlockNoteExtension.js"; import { autolink } from "./helpers/autolink.js"; import { findLinks } from "./helpers/linkDetector.js"; import { clickHandler } from "./helpers/clickHandler.js"; @@ -67,8 +68,6 @@ export type LinkOptions = { export const Link = Mark.create({ name: "link", - priority: 1000, - keepOnSplit: false, exitable: true, @@ -207,3 +206,33 @@ export const Link = Mark.create({ return plugins; }, }); + +type LinkExtensionOptions = { + HTMLAttributes?: Record; + onClick?: ( + event: MouseEvent, + editor: BlockNoteEditor, + ) => boolean | void; + isValidLink?: (href: string) => boolean; +}; + +/** + * BlockNote extension wrapping the {@link Link} TipTap mark. Wrapping the mark + * lets other extensions order their click handlers relative to the link click + * handler via `runsBefore: ["link"]`. + */ +export const LinkExtension = createExtension( + ({ editor, options }) => { + return { + key: "link", + tiptapExtensions: [ + Link.configure({ + HTMLAttributes: options.HTMLAttributes ?? {}, + editor, + onClick: options.onClick, + ...(options.isValidLink ? { isValidLink: options.isValidLink } : {}), + }), + ], + } as const; + }, +); diff --git a/packages/xl-ai/src/prosemirror/__snapshots__/agent.test.ts.snap b/packages/xl-ai/src/prosemirror/__snapshots__/agent.test.ts.snap index 602022d35a..54ccfe8769 100644 --- a/packages/xl-ai/src/prosemirror/__snapshots__/agent.test.ts.snap +++ b/packages/xl-ai/src/prosemirror/__snapshots__/agent.test.ts.snap @@ -12,7 +12,7 @@ exports[`agentStepToTr > Update > drop mark and link 1`] = ` "S {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world! "},{"type":"text","marks":[{"type":"bold"}],"text":"Bold text. "},{"type":"text","marks":[{"type":"link","attrs":{"href":"https://www.google.com"}}],"text":"Link."}]}]}]}]}", "R {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world! "},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":"Bold text. "},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Bold text. "},{"type":"text","marks":[{"type":"link","attrs":{"href":"https://www.google.com"}}],"text":"Link."}]}]}]}]}", "S {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world! "},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":"Bold text. "},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Bold text. "},{"type":"text","marks":[{"type":"link","attrs":{"href":"https://www.google.com"}}],"text":"Link."}]}]}]}]}", - "R {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world! "},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":"Bold text. "},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Bold text. "},{"type":"text","marks":[{"type":"link","attrs":{"href":"https://www.google.com"}},{"type":"deletion","attrs":{"id":null}}],"text":"Link."},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Link."}]}]}]}]}", + "R {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world! "},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":"Bold text. "},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Bold text. "},{"type":"text","marks":[{"type":"deletion","attrs":{"id":null}},{"type":"link","attrs":{"href":"https://www.google.com"}}],"text":"Link."},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Link."}]}]}]}]}", ] `; @@ -31,7 +31,7 @@ exports[`agentStepToTr > Update > drop mark and link and change text within mark "S {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","marks":[{"type":"deletion","attrs":{"id":null}}],"text":"Hello"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Hi"},{"type":"text","text":", world! "},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":"Bold"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Bold the"},{"type":"text","marks":[{"type":"bold"}],"text":" text. "},{"type":"text","marks":[{"type":"link","attrs":{"href":"https://www.google.com"}}],"text":"Link."}]}]}]}]}", "R {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","marks":[{"type":"deletion","attrs":{"id":null}}],"text":"Hello"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Hi"},{"type":"text","text":", world! "},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":"Bold"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Bold the"},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":" text. "},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":" text. "},{"type":"text","marks":[{"type":"link","attrs":{"href":"https://www.google.com"}}],"text":"Link."}]}]}]}]}", "S {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","marks":[{"type":"deletion","attrs":{"id":null}}],"text":"Hello"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Hi"},{"type":"text","text":", world! "},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":"Bold"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Bold the"},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":" text. "},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":" text. "},{"type":"text","marks":[{"type":"link","attrs":{"href":"https://www.google.com"}}],"text":"Link."}]}]}]}]}", - "R {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","marks":[{"type":"deletion","attrs":{"id":null}}],"text":"Hello"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Hi"},{"type":"text","text":", world! "},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":"Bold"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Bold the"},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":" text. "},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":" text. "},{"type":"text","marks":[{"type":"link","attrs":{"href":"https://www.google.com"}},{"type":"deletion","attrs":{"id":null}}],"text":"Link."},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Link."}]}]}]}]}", + "R {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","marks":[{"type":"deletion","attrs":{"id":null}}],"text":"Hello"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Hi"},{"type":"text","text":", world! "},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":"Bold"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Bold the"},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":" text. "},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":" text. "},{"type":"text","marks":[{"type":"deletion","attrs":{"id":null}},{"type":"link","attrs":{"href":"https://www.google.com"}}],"text":"Link."},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Link."}]}]}]}]}", ] `; diff --git a/tests/src/end-to-end/comments/comments.test.ts b/tests/src/end-to-end/comments/comments.test.ts index db29498359..198a8e1ced 100644 --- a/tests/src/end-to-end/comments/comments.test.ts +++ b/tests/src/end-to-end/comments/comments.test.ts @@ -32,4 +32,49 @@ test.describe("Check Comments functionality", () => { await expect(await page.locator("span.bn-thread-mark")).toBeVisible(); }); + + test("Should select thread on first click and open link on second click", async ({ + browserName, + page, + }) => { + test.skip( + browserName === "webkit", + "WebKit causes absurd failures running in Docker which aren't reproducible anywhere else.", + ); + + await focusOnEditor(page); + + await page.keyboard.type("hello"); + await page.locator("text=hello").dblclick(); + + await page.click('[data-test="addcomment"]'); + await page.waitForSelector(".bn-thread"); + + await page.keyboard.type("test comment"); + await page.click('button[data-test="save"]'); + + await page.locator("span.bn-thread-mark").first().dblclick(); + + await expect(page.locator(LINK_BUTTON_SELECTOR)).toBeVisible(); + await page.click(LINK_BUTTON_SELECTOR); + + await page.keyboard.type("https://example.com"); + await page.keyboard.press("Enter"); + + await page.keyboard.press("ArrowDown"); + await page.waitForTimeout(500); + await expect(page.locator(".bn-thread-mark-selected")).toHaveCount(0); + + const link = page.locator('a[data-inline-content-type="link"]').first(); + + // First click selects the thread without navigating. + await link.click(); + await page.waitForTimeout(500); + await expect(page.locator(".bn-thread-mark-selected")).toBeVisible(); + + // Second click on the now-selected thread navigates to the link target. + const popupPromise = page.waitForEvent("popup"); + await link.click(); + await popupPromise; + }); });