Skip to content

Tiptap: Preserve wrapping link when editing an image (closes #23013)#23019

Open
AndyButland wants to merge 2 commits into
v17/devfrom
v17/bugfix/23013-fix-image-in-link-edit
Open

Tiptap: Preserve wrapping link when editing an image (closes #23013)#23019
AndyButland wants to merge 2 commits into
v17/devfrom
v17/bugfix/23013-fix-image-in-link-edit

Conversation

@AndyButland
Copy link
Copy Markdown
Contributor

@AndyButland AndyButland commented May 29, 2026

Description

As reported in #23013, editing an image in the Tiptap RTE was discarding any <a> that wrapped the image.

Currently the rebuild inserts a fresh image node with no marks so the umbLink mark representing the wrapping link was dropped.

The fix captures and forwards the inline marks on the original image onto the replacement.

In testing I found an odd behaviour with an empty figcaption attribute being created. This was also fixed whilst preserving any existing wrapping <figure>'s attributes, by removing the Figure extension's parseHTML mirror that copied the inner <figcaption> text into the figure's figcaption attribute and defaulting the attribute to null so freshly-built figures no longer emit figcaption="".

Fixes #23013.

Testing

Automated

For automated testing I've extracted helpers (extractImageMarks, extractFigureAttrs, extractFigureImageData) to a sibling utils module, and covered with 14 unit tests.

Manual

Two starting markup shapes cover the two code paths. In each case, after editing the image, the wrapping <a> (with all its attributes) should still surround the <img> in the resulting markup.

Swap the data-udi and localLink GUID for real values from your local environment; the src URL doesn't matter (the editor regenerates it on submit).

Image wrapped directly in a link

<p>
  <a data-router-slot="disabled" href="/{localLink:49ac6099-cf51-458b-b9a4-0c1f55dc7902}" title="My Page" type="document">
    <img data-udi="umb://media/209e1915528b40c594ee7bb706d5d252" src="..." alt="My Image" width="350" height="233">
  </a>
</p>
  • Load the property in a Tiptap RTE and confirm the image renders inside the link.
  • Click the image, then the image toolbar button.
  • Change the dimensions and submit.
  • In the resulting markup, the <img> is still wrapped in <a data-router-slot="disabled" href="/{localLink:...}" title="My Page" type="document"> — all four <a> attributes intact.

Image inside a figure with a caption

<figure>
  <p>
    <a data-router-slot="disabled" href="/{localLink:49ac6099-cf51-458b-b9a4-0c1f55dc7902}" title="My Page" type="document">
      <img data-udi="umb://media/209e1915528b40c594ee7bb706d5d252" src="..." alt="My Image" width="350" height="233">
    </a>
  </p>
  <figcaption>My Caption</figcaption>
</figure>
  • Load the property and confirm the figure renders.
  • Click the image, then the image toolbar button.
  • Change the dimensions and submit, leaving the caption field as-is.
  • In the resulting markup, the figure structure is preserved, the inner <img> is still wrapped in the original <a> with all attributes, and the <figcaption> text is unchanged.
  • The <figure> element does not gain an empty figcaption="" attribute.

Copilot AI review requested due to automatic review settings May 29, 2026 13:15
@claude
Copy link
Copy Markdown

claude Bot commented May 29, 2026

Claude finished @AndyButland's task in 5m 29s —— View job


PR Review

Target: origin/v17/dev · Based on commit: cd673675

Fixes a bug where editing an image that was wrapped in an <a> discarded the surrounding link. The rebuild now captures ProseMirror marks from the original image node (or from the inner image of an atomic figure) and forwards them via insertContent, replacing the broken setImage() call. Includes extraction of figure attrs across the rebuild and a suite of 14 unit tests.

  • Other changes: figure.tiptap-extension.tsfigcaption attribute default changed from '' to null, and the parseHTML mirror (which copied <figcaption> text into the figure's figcaption HTML attribute) is removed. Freshly built figures will no longer emit figcaption="" in their HTML output. Existing stored HTML with figcaption="..." on the <figure> element continues to parse correctly (tiptap reads the attribute directly).

Suggestions

  • media-picker.tiptap-toolbar.utils.ts:4-10: UmbTiptapMarkInput and UmbFigureImageData are exported types without JSDoc. Per coding preferences, all exported types should have at least a brief description (the exported functions below them do have JSDoc). Fix this →

Approved

Good fix with solid test coverage across both the plain-image and figure code paths. The extraction helpers are clean and well-documented, and the round-trip regression tests give good confidence in the mark-preservation logic. Well done!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a Tiptap RTE regression where editing an image would drop its wrapping link by ensuring the original image node’s inline marks (notably the umbLink mark) are preserved when the image/figure is rebuilt. It also cleans up the Figure extension so newly created figures don’t emit an empty figcaption="" attribute.

Changes:

  • Preserve image marks during image replacement by extracting marks from the current selection (including when the selection lands on an atomic figure) and passing them through insertContent.
  • Preserve existing figure node attributes during figure rebuilds.
  • Simplify the Figure extension HTML parsing and default figcaption to null, with added unit tests covering these scenarios.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/media-picker/media-picker.tiptap-toolbar.utils.ts Adds helper utilities to extract image marks, enclosing figure attrs, and figure image/caption data for safe rebuilds.
src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/media-picker/media-picker.tiptap-toolbar.utils.test.ts Adds unit tests validating mark/attrs extraction and round-trip preservation via insertContent.
src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/media-picker/media-picker.tiptap-toolbar-api.ts Updates media-picker toolbar behavior to forward extracted marks and preserve figure attributes when inserting/replacing content.
src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/figure/figure.tiptap-extension.ts Stops mirroring <figcaption> text into a figure attribute and defaults figcaption to null to avoid empty attribute emission.

@claude claude Bot added the area/frontend label May 29, 2026
@leekelleher leekelleher self-requested a review June 3, 2026 11:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants