Tiptap RTE: Adds support for Reusable Content of Blocks - #23195
Tiptap RTE: Adds support for Reusable Content of Blocks#23195leekelleher wants to merge 13 commits into
Conversation
- Add `allContents` observable to `UmbBlockManagerContext` merging local + external content - Switch block observation from `context.contents` to `combineLatest([layouts, allContents])`; skips blocks whose content type hasn't resolved yet (external content arrives async) - Add `data-key` attribute (layout key) to RTE block ProseMirror nodes so duplicate library element instances are tracked independently; `data-content-key` is retained for backwards-compat - Legacy markup (no `data-key`) defaults `data-key = data-content-key` on parse, which is safe because pre-v19 data always has `layout.key === layout.contentKey` - Switch `_filterUnusedBlocks` and undo stash from content-key to layout-key tracking; external blocks skip local content stash (their content lives in `#externalContentValues`) - Rename `contentKey` params to `layoutKey` in `requestPendingDeletion`, `clearPendingDeletion`, `removeOneLayout`, `removeManyLayouts`, and `delete` — semantically safe for legacy data - `umb-rte-block` exposes `data-key` as the primary property and deprecates `data-content-key` / `contentKey` (removal v21)
|
Claude finished @leekelleher's task in 7m 53s —— View job PR ReviewTarget: Wires RTE blocks to the Element Library (reusable content) feature by switching the block-tracking key from content key to layout key throughout the frontend stack, and adds observers for external content state so library element blocks render with the correct
Important
Suggestions
Request ChangesThe |
There was a problem hiding this comment.
Pull request overview
This PR updates the Tiptap RTE block integration to support Element Library (reusable/external) block references by keying RTE block instances on the layout key (data-key) rather than only the content key, and by observing external content resolution so referenced blocks appear once fetched.
Changes:
- Add
data-key(UMB_BLOCK_RTE_DATA_LAYOUT_KEY) to RTE block nodes (with legacy fallback) and switch block syncing/deletion to operate on layout keys. - Extend block manager state with
allContents(local + external) and update the Tiptap block API to re-sync when external content resolves. - Update RTE block entry UI to style referenced blocks and compute published/unpublished state correctly for external content.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap-rte/property-editor-ui-tiptap.element.ts | Extracts layout keys from markup to filter unused layouts correctly. |
| src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/block/block.tiptap-extension.ts | Adds data-key attribute to block nodes and migrates legacy markup on parse. |
| src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/block/block.tiptap-api.ts | Syncs editor DOM blocks from manager layouts and handles deletions by layout key; re-syncs when external content resolves. |
| src/Umbraco.Web.UI.Client/src/packages/rte/constants.ts | Introduces UMB_BLOCK_RTE_DATA_LAYOUT_KEY = 'data-key'. |
| src/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts | Switches unused/restore tracking from content keys to layout keys and adjusts external-content handling. |
| src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts | Adds allContents observable combining local and resolved external block content. |
| src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-manager.context.ts | Changes pending deletion tracking from content keys to layout keys. |
| src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-entries.context.ts | Updates delete API to request pending deletion by layout key. |
| src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts | Adds data-key support, reference styling, and correct published state logic for external content. |
- Remove accidental margin-top regression on all RTE block entries - Fix settings not stashed/restored for external blocks on delete+undo - Reduce deep nesting in #restoreUnusedBlocks / #setUnusedBlockLookups - De-duplicate getAttrs and insert-attrs logic in block.tiptap-extension.ts - Clarify JSDoc on allContents and _filterUnusedBlocks parameter semantics
The umb-entity-frame label tab clips when a block is at the top of the RTE without this spacing.
# Conflicts: # src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts
Reduce cognitive complexity of #restoreUnusedBlocks by extracting content/settings restoration into helper methods, use .includes()/Set.has() over indexOf()/Array.includes(), and use RegExp.exec() over String.match().
|
Adds a sibling test to the incoming v19/dev doc-walk test, asserting the walk also finds layout keys (including the legacy data-key-missing fallback), matching how #updateBlocks now discovers blocks post-merge.
Block RTE, Grid, List and Single entry elements each duplicated an identical #updateExposedState() method and its three backing observers. Move the derivation onto UmbBlockEntryContext as a single mergeObservables-based isExposed observable, consumed the same way by all four elements. Fixes a SonarQube new-code duplication gate failure on block-rte-entry.element.ts.
These two cases were merged up from v17/dev before RTE blocks gained a data-key layout attribute alongside data-content-key, so their expected markup no longer matches the block node's actual (correct) output. Use distinct data-key/data-content-key values to assert the div-nesting behaviour without relying on the legacy same-value back-fill, which is already covered by block.tiptap-extension.test.ts.
BlockRegex required data-content-key to appear immediately after the tag name (or an optional class attribute), so it never matched once data-key started being emitted before data-content-key. That left RTE blocks rendering as unreplaced placeholders on the front end after being re-saved through the editor. Widen the pattern to accept attributes on either side of data-content-key.
|
| this.#blockTypes = new Map( | ||
| blockTypes?.map((x) => [x.contentElementTypeKey, x] as [string, UmbBlockRteTypeModel]), | ||
| ); | ||
| combineLatest([context.layouts, context.allContents]), |
There was a problem hiding this comment.
For this, we have a method called observeMultiple. It is the same, just a different export.





Description
Wires up the Tiptap RTE block extension to work with Element Library (reusable content) references, following the general Reusable Content of Blocks feature that landed in #22448.
Previously, inserting a Library Element into an RTE would silently do nothing — Library Elements only have a
layoutentry (no localcontentData), so the block extension never detected them. This PR makes it all work.Technical summary (AI/Claude generated) 🤖
data-keyattribute (the layout's unique key) to RTE block nodes so the same library element can be used multiple times independently — each instance is tracked and can be deleted/undone without affecting the othersdata-keycontinues to work (legacy-safe)is-reference)Breaking changes
editor.commands.setBlock()andeditor.commands.setBlockInline()Tiptap commands now require an additionallayoutKeyparameter.How to test
// CC'ing @kjac @lauraneto, as we may need to update the C# RegEx to support the new
data-keyattribute, so that parses RTE markup to get the block references.