Skip to content

fix: add link on commented text removes the comment highlight (2573)#2592

Open
miadnguyen wants to merge 2 commits intoTypeCellOS:mainfrom
miadnguyen:fix/preserve-comment-mark-on-link-edit
Open

fix: add link on commented text removes the comment highlight (2573)#2592
miadnguyen wants to merge 2 commits intoTypeCellOS:mainfrom
miadnguyen:fix/preserve-comment-mark-on-link-edit

Conversation

@miadnguyen
Copy link
Copy Markdown

@miadnguyen miadnguyen commented Mar 24, 2026

Summary

This pull request fixes an issue where adding or editing a link on commented text removes the comment highlight and comment association. After this change, a link can be added or edited on commented text while preserving both the link underline and the existing comment behavior.

Rationale

The issue was caused by LinkToolbarExtension.editLink() (from packages/core/src/extensions/LinkToolbar/LinkToolbar.ts) replacing the selected text with insertText(...) and then reapplying only the link mark. As a result, existing marks on that text, including the comment mark, were lost. Preserving existing non-link marks makes link editing behave correctly and keeps comment functionality intact.

Changes

  • Updated LinkToolbarExtension.editLink() to preserve existing marks on the selected text before replacing it
  • Reapplied existing non-link marks after insertText(...)
  • Reapplied the updated link mark after restoring the preserved marks

Impact

This change affects the link editing flow for text that already contains other marks, especially comments. It should improve behavior by preserving comment highlighting and comment accessibility when links are added or edited. The intended behavior for normal link editing remains the same.

Testing

  • Manually verified: added a link on commented text preserves the comment highlight
  • Manually verified: editing a link on bold text preserves the bold mark
  • Added Playwright e2e test in tests/src/end-to-end/linktoolbar/linktoolbar.test.ts
    verifying that bold marks are preserved when editing a link
  • Existing tests pass (pre-existing snapshot failures are unrelated platform differences)

Screenshots/Video

Below is an image showing commented text retaining the comment, highlight, and link after the fix.
image

Checklist

  • Code follows the project's coding standards.
  • Unit tests covering the new feature have been added.
  • All existing tests pass.
  • The documentation has been updated to reflect the new feature

Additional Notes

Fixes #2573

Summary by CodeRabbit

  • Bug Fixes

    • Link editing now preserves other text formatting (bold, italic, etc.) when updating a link and ensures editor focus happens after changes are applied.
  • Tests

    • Added an end-to-end test that verifies link insertion, editing, and preservation of text formatting (e.g., bold) after updating a link.

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 24, 2026

@miadnguyen is attempting to deploy a commit to the TypeCell Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 24, 2026

📝 Walkthrough

Walkthrough

The LinkToolbar editLink flow was changed to capture the existing link range, insert new text, reapply all non-link marks over the inserted span, then (re)apply the link mark; the editor focus call was moved to run after the transaction completes.

Changes

Cohort / File(s) Summary
LinkToolbar Mark Preservation
packages/core/src/extensions/LinkToolbar/LinkToolbar.ts
Updated editLink to read the existing link range, insert replacement text, collect and reapply all marks except the "link" mark onto the inserted span, then apply/set the "link" mark. Moved editor.prosemirrorView.focus() out of the transaction callback.
End-to-end Link Toolbar Test
tests/src/end-to-end/linktoolbar/linktoolbar.test.ts
Added a Playwright E2E test that creates bold text, inserts a link, opens the edit link UI, replaces the URL, and asserts the bold formatting remains on the linked text (checks strong a / a strong combinations).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I hopped in to tweak a link so neat,
I kept the marks that made text sweet,
Bold and highlights stay in play,
Links refreshed without decay,
Hooray — a tidy edit, quick and fleet!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main fix: preserving comment marks when editing links on commented text.
Linked Issues check ✅ Passed The code changes directly address issue #2573 by preserving non-link marks (like comments) during link editing, meeting the requirement that comment highlights remain intact.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing link editing behavior with preserved marks; the test addition validates the fix without introducing unrelated modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The pull request description is comprehensive and follows the required template structure, covering all key sections including Summary, Rationale, Changes, Impact, Testing, Screenshots, and Checklist.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@miadnguyen miadnguyen force-pushed the fix/preserve-comment-mark-on-link-edit branch from 3668c41 to 9da3e59 Compare April 4, 2026 22:53
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
tests/src/end-to-end/linktoolbar/linktoolbar.test.ts (1)

33-44: Replace hard-coded timeouts with locator-based waits to improve test reliability.

Lines 32 and 43 use waitForTimeout which introduces brittle, timing-dependent behavior. Use explicit visibility assertions instead to ensure deterministic waits aligned with actual UI state changes.

♻️ Suggested refactor
     await page.keyboard.press("ArrowLeft");
-    await page.waitForTimeout(500);
+    await expect(page.getByRole("button", { name: "Edit link" })).toBeVisible();

     await page.keyboard.press("Enter");
-    await page.waitForTimeout(300);
+    await expect(page.locator('a[href="https://example2.com"]')).toBeVisible();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/src/end-to-end/linktoolbar/linktoolbar.test.ts` around lines 33 - 44,
Replace the two hard-coded waitForTimeout calls with deterministic locator-based
waits: remove await page.waitForTimeout(500) and instead wait for the "Edit
link" locator to become visible using page.getByText("Edit link").waitFor({
state: "visible" }) (or expect(page.getByText("Edit link")).toBeVisible() if
using `@playwright/test`) before clicking; after pressing Enter, remove await
page.waitForTimeout(300) and wait for a concrete UI change such as the edit
dialog closing or the updated link text appearing (e.g., await
page.getByText("https://example2.com").waitFor({ state: "visible" }) or waiting
for the edit dialog locator to be hidden) so the test no longer relies on fixed
timeouts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/src/end-to-end/linktoolbar/linktoolbar.test.ts`:
- Around line 11-49: Add a focused regression test that mirrors "Should preserve
existing marks when editing a link" but creates a comment mark first: use
focusOnEditor to focus, type/select text, invoke the comment tooling (the
comment button/flow used in your app) to add a comment annotation, then add/edit
a link via LINK_BUTTON_SELECTOR and the existing editButton flow; finally assert
the comment highlight/association still exists (use the comment-specific locator
you have for comment highlights/associations, analogous to how boldText uses
"strong a, a strong") to ensure the comment mark is preserved after link
insert/edit.
- Around line 47-48: The current assertion using the broad selector assigned to
boldText (page.locator("strong a, a strong")) may match unrelated items; tighten
it to specifically target the edited link and assert both its href equals
"https://example2.com" and that it remains bold and linked. Update the locator
used for boldText to target the exact link (for example by link text or a more
specific ancestor/attribute) and add two expectations: one that the locator's
href/attribute equals "https://example2.com" and another that verifies the
element is wrapped in/has the strong styling (preserving bold formatting) using
the same locator and expect calls.

---

Nitpick comments:
In `@tests/src/end-to-end/linktoolbar/linktoolbar.test.ts`:
- Around line 33-44: Replace the two hard-coded waitForTimeout calls with
deterministic locator-based waits: remove await page.waitForTimeout(500) and
instead wait for the "Edit link" locator to become visible using
page.getByText("Edit link").waitFor({ state: "visible" }) (or
expect(page.getByText("Edit link")).toBeVisible() if using `@playwright/test`)
before clicking; after pressing Enter, remove await page.waitForTimeout(300) and
wait for a concrete UI change such as the edit dialog closing or the updated
link text appearing (e.g., await
page.getByText("https://example2.com").waitFor({ state: "visible" }) or waiting
for the edit dialog locator to be hidden) so the test no longer relies on fixed
timeouts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ef0e1404-adec-46b7-a204-1e9b0ddd69e2

📥 Commits

Reviewing files that changed from the base of the PR and between 9da3e59 and 34e5340.

📒 Files selected for processing (1)
  • tests/src/end-to-end/linktoolbar/linktoolbar.test.ts

Comment on lines +11 to +49
test("Should preserve existing marks when editing a link", async ({
page,
}) => {
await focusOnEditor(page);

// Type bold text
await page.keyboard.type("hello");
await page.keyboard.press("Shift+Home");

// Make it bold via formatting toolbar
await page.waitForSelector(`[data-test="bold"]`);
await page.click(`[data-test="bold"]`);

// Add link
await page.keyboard.press("Shift+Home");
await page.waitForSelector(LINK_BUTTON_SELECTOR);
await page.click(LINK_BUTTON_SELECTOR);
await page.keyboard.type("https://example.com");
await page.keyboard.press("Enter");

// Move cursor back onto the linked text to trigger link toolbar
await page.keyboard.press("ArrowLeft");
await page.waitForTimeout(500);

// Click Edit link button
const editButton = page.getByText("Edit link");
await editButton.waitFor({ state: "visible" });
await editButton.click();

await page.keyboard.press("Control+A");
await page.keyboard.type("https://example2.com");
await page.keyboard.press("Enter");

await page.waitForTimeout(300);

// Verify bold mark is still present on the text
const boldText = page.locator("strong a, a strong");
await expect(boldText).toBeVisible();
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Regression test does not cover the reported comment-mark bug.

Line 11 onward validates bold preservation, but issue #2573 is specifically about preserving the comment mark (highlight + association/accessibility) when adding/editing a link. This can pass while the original bug still regresses.

Please add a scenario that creates commented text first, then adds/edits a link and verifies the comment highlight/association remains.

🧰 Tools
🪛 ESLint

[error] 36-36: Avoid destructuring queries from render result, use screen.getByText instead

(testing-library/prefer-screen-queries)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/src/end-to-end/linktoolbar/linktoolbar.test.ts` around lines 11 - 49,
Add a focused regression test that mirrors "Should preserve existing marks when
editing a link" but creates a comment mark first: use focusOnEditor to focus,
type/select text, invoke the comment tooling (the comment button/flow used in
your app) to add a comment annotation, then add/edit a link via
LINK_BUTTON_SELECTOR and the existing editButton flow; finally assert the
comment highlight/association still exists (use the comment-specific locator you
have for comment highlights/associations, analogous to how boldText uses "strong
a, a strong") to ensure the comment mark is preserved after link insert/edit.

Comment on lines +47 to +48
const boldText = page.locator("strong a, a strong");
await expect(boldText).toBeVisible();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

For Playwright tests, what are robust selector patterns to assert nested formatted text relationships (e.g., bold + link on the same node) without broad false-positive CSS matches?

💡 Result:

Robust selector patterns in Playwright for asserting nested formatted text relationships (e.g., bold + link on the same node) prioritize precise, structure-aware locators over broad CSS to avoid false positives. Key patterns from official docs and best practices: 1. Use CSS :has pseudo-class for parent-child relationships: e.g., page.locator('p:has(strong):has(a)') selects paragraphs containing both bold () and link () children. Combine with :has-text for content: 'p:has-text("Important"):has(strong a)'. 2. Chain locators with filter: Start with a container, filter by nested elements. e.g.: const container = page.getByRole('paragraph'); const formatted = container.filter({ has: page.locator('strong:has(a)') }); await expect(formatted).toBeVisible; 3. Locator chaining with >> : e.g., 'article >> strong >> a' for bold containing link, or '*article >> strong >> a' to keep the article. Use :text or :text-is for precise text matching within: 'article :text("Click"):has(a)'. 4. Assertions for formatting: Use expect(locator).toHaveCSS('font-weight', 'bold') on links or text nodes to verify styles without class dependencies. For structure, expect(locator).toContainText or .toHaveText with regex, combined with .locator('strong').toBeVisible. 5. Best practices to avoid false positives: - Prefer role/text locators over pure CSS: page.getByRole('link', { name: /regex/ }).locator('..').locator('strong'). - Add data-testid to formatted containers: '[data-testid="bold-link"]'. - Use :visible and nth-match for specificity: 'strong:visible:nth-match(1):has(a)'. - Avoid deep CSS chains or brittle classes; test user-visible structure. These ensure assertions pass only when exact nested formatting exists, resilient to style/CSS changes.

Citations:


🏁 Script executed:

wc -l tests/src/end-to-end/linktoolbar/linktoolbar.test.ts

Repository: TypeCellOS/BlockNote

Length of output: 119


🏁 Script executed:

sed -n '1,70p' tests/src/end-to-end/linktoolbar/linktoolbar.test.ts

Repository: TypeCellOS/BlockNote

Length of output: 1691


🏁 Script executed:

rg ':has\(' tests/ -A 2 -B 2 | head -50

Repository: TypeCellOS/BlockNote

Length of output: 46


🏁 Script executed:

rg 'hasText|:has-text' tests/ -A 2 -B 2 | head -50

Repository: TypeCellOS/BlockNote

Length of output: 431


🏁 Script executed:

rg 'page\.locator\(' tests/src/end-to-end/ -A 2 | head -80

Repository: TypeCellOS/BlockNote

Length of output: 5820


🏁 Script executed:

rg 'page\.locator\([^,]+,\s*{' tests/src/end-to-end/ -A 3

Repository: TypeCellOS/BlockNote

Length of output: 177


🏁 Script executed:

rg 'page\.locator.*hasText' tests/src/end-to-end/ -A 2

Repository: TypeCellOS/BlockNote

Length of output: 46


🏁 Script executed:

rg 'locator\([^)]+\{' tests/src/end-to-end/basics/ -A 2 | head -30

Repository: TypeCellOS/BlockNote

Length of output: 304


Tighten the assertion to verify both the updated URL and preserved formatting.

The selector strong a, a strong is too broad and could match unrelated bold links on the page. It should verify that the edited link has the new URL (https://example2.com) and the text remains bold+linked.

✅ More specific assertion
-    const boldText = page.locator("strong a, a strong");
-    await expect(boldText).toBeVisible();
+    const updatedLink = page.locator('a[href="https://example2.com"]', {
+      hasText: "hello",
+    });
+    await expect(updatedLink).toBeVisible();
+
+    const boldLinkedText = page.locator("strong").filter({
+      has: page.locator('a[href="https://example2.com"]'),
+    }).filter({ hasText: "hello" });
+    await expect(boldLinkedText).toBeVisible();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const boldText = page.locator("strong a, a strong");
await expect(boldText).toBeVisible();
const updatedLink = page.locator('a[href="https://example2.com"]', {
hasText: "hello",
});
await expect(updatedLink).toBeVisible();
const boldLinkedText = page.locator("strong").filter({
has: page.locator('a[href="https://example2.com"]'),
}).filter({ hasText: "hello" });
await expect(boldLinkedText).toBeVisible();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/src/end-to-end/linktoolbar/linktoolbar.test.ts` around lines 47 - 48,
The current assertion using the broad selector assigned to boldText
(page.locator("strong a, a strong")) may match unrelated items; tighten it to
specifically target the edited link and assert both its href equals
"https://example2.com" and that it remains bold and linked. Update the locator
used for boldText to target the exact link (for example by link text or a more
specific ancestor/attribute) and add two expectations: one that the locator's
href/attribute equals "https://example2.com" and another that verifies the
element is wrapped in/has the strong styling (preserving bold formatting) using
the same locator and expect calls.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add link on commented text removes the comment highlight

1 participant