Fix rich text editor paste over a select-all selection

What does this MR do and why?

The rich text editor pastes markdown by deleting the current selection, showing a loading indicator where the new content will land, rendering the markdown through the API, and swapping the indicator for the rendered content once it returns. With the whole document selected, the first paste deleted everything and the indicator spun forever, with no content ever inserted.

Three bugs combined to cause this. After a select-all deletion, the indicator legitimately lands at document position 0, but the code checked its position with if (!pos) return, which treats position 0 as "not found" and exits after the deletion has already run. The indicator's position was also captured from the selection before the deletion ran, so it did not match the cursor position left inside the empty paragraph, and pasted block content could not replace it. Last, a failed markdown render request showed the error alert but never removed the indicator.

The fix reads the indicator's position after the deletion runs, inside the same transaction, and treats only a null position, meaning the indicator was removed from the document (for example the user deleted that region while waiting), as a signal to skip the insert. The indicator is now removed on every exit path, including render failure.

Resolves #514314 (closed)

References

  • Reporter's re-confirmation (2026-08-31): "upon the first paste action, all existing text is removed and a loading indicator appears, which then hangs indefinitely without any further action."
  • New jest specs fail on master and pass with the fix: select-all paste wiping the document, the indicator stranded at position 0, multi-block paste, and render failure leaving the indicator behind. A gap-cursor case additionally pins pasting at document position 0, the production case the position guard exists for. After the fix, the file passes 44/44 under both Vue 2 and Vue 3.
  • The full spec/frontend/content_editor/ tree plus the markdown editor spec stay green: 126 suites, 1232 tests, both Vue variants.
  • A new end-to-end feature spec example, "replaces the entire content when pasting over a select-all selection" in the rich text editor copy/paste shared examples, drives the real Chrome clipboard. Verified locally: 1 failure at the merge base (nothing pasted), 0 failures at this branch. Draft tier-1 pipelines run no RSpec, so a full pipeline should run before merge.
  • Prior art for the defect class: the paste loader shipped in show a loading indicator on paste (!123202) for #406724 (closed); prevent loading state from showing after undo (!127036) fixed the undo flavor of the same stranded-loader class (#419095 (closed)). Markdown render failures that hit the failure path happen in the wild: #435313 (closed).
  • Related, not resolved here: pasting into a list item splits the item (#512151).

Screenshots or screen recordings

Before After
paste_over_select_all paste_over_select_all

Both shots show the new-issue description editor after the same steps: type 'Text to replace', select all, paste (the clipboard holds rich markdown previously cut from the editor). Before: the typed text is wiped, nothing is inserted, and the dots loader is stranded (it persists after the markdown render request completes). After: the pasted content replaces the document and no loader remains.

Recording

Before (recording) After (recording)
rte_paste_select_all_before rte_paste_select_all_after

The stills' scenario re-captured as three frames: typed text, select all + paste, about 5 seconds later. Before: the paste wipes the document and the dots loader persists across the last two frames. After: the pasted content lands and nothing changes afterwards.

How to set up and validate locally

  1. Open any rich text editor surface, for example the Description field on the new issue page, and switch to rich text editing if in plain text mode.
  2. Type some text, for example "Text to replace".
  3. Copy some markdown as plain text from any app, for example **bold text** and _italic_.
  4. Select all (Cmd+A / Ctrl+A), then paste (Cmd+V / Ctrl+V).
  5. Before the fix: all text disappears, an animated dots loader appears and never goes away, and nothing is inserted (a second paste inserts, often leaving the dots behind). After the fix: the document becomes exactly the pasted content, with no dots and no trailing empty paragraph.
  6. Failure path: in DevTools Network, block the preview_markdown request URL, then paste. Before the fix: error alert appears and the dots stay forever. After the fix: error alert appears and the dots disappear.
  7. Regression checks: paste at a cursor mid-paragraph, paste over a partial selection, and paste and type before the render resolves (content still lands at the indicator's position).

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Paul W

Merge request reports

Loading
Loading