Fix sticky title not updating when draft status changes

What does this MR do and why?

This merge request fixes a bug where the sticky merge request title in the header does not update when you mark a merge request as draft or ready.

When you mark a merge request as draft or ready on the merge request page, the main title updates instantly. However, the small sticky title that stays near the top of the screen while scrolling does not update. It keeps showing the old status, like "Draft: ..." even after you've marked it as ready. You have to refresh the page to see the correct sticky title.

The code now directly updates the sticky title text on the page as soon as you click "Mark as draft" or "Mark as ready". It also fires a custom event at the same moment so that Vue-based sticky headers can update themselves right away, instead of waiting for a background network message that might be slow or not arrive at all. Tests have been updated to verify this behavior works correctly.

References

Related to issue #607897 (closed) where the "Mark as ready" button was causing doubled "Draft:" prefixes. That issue has been fixed. However, in a comment on that issue from @jotolo_gl posted on 2026-08-11 (#607897 (comment 3670092555)), he reported a separate problem with the sticky title not updating, describing it as "another problem with DRAFT feature". This merge request fixes that specific issue he pointed out.

On the Apollo subscription and sticky header rendering

In a comment on the issue (#607897 (comment 3672700677)), @thomasrandolph noted that an Apollo subscription over a websocket should be updating the sticky header and suggested that something might be disconnected there. His observation is accurate, but only for one of two rendering paths that GitLab uses for the sticky title.

Browsers that support the CSS container-type: scroll-state feature (confirmed on current Chrome, which is what this bug was reproduced and fixed against) render the sticky title as a static server-rendered element, styled to appear or disappear based on scroll position via CSS container queries. This path has no JavaScript involvement and no subscription at all, and it is the path affected by the reported bug. The Vue-based fallback component that @thomasrandolph describes, which does rely on an ActionCable subscription to stay in sync, only activates in browsers that lack support for that CSS feature.

This MR's fix addresses both paths: it directly updates the static title's text as soon as the draft/ready toggle fires, which fixes the bug on the CSS-driven path. At the same time, it fires a lightweight custom browser event so the Vue fallback component can update immediately as well, so it no longer depends solely on subscription timing or delivery. Reviewing the subscription wiring itself (the GraphQL trigger, the ActionCable channel, and the cable/Redis config) found no issues with how it's configured, so the approach here sidesteps the subscription reliability question rather than trying to resolve it directly.

Screen recordings:

Issue:

Fix:

How to set up and validate locally

  1. Open any merge request that is marked as draft.
  2. Scroll down so the sticky title bar at the top is visible.
  3. Click "Mark as ready".
  4. Before this fix, the sticky title keeps showing the old "Draft:" text until you refresh the page. After this fix, it updates right away.
Edited by Mahaveer A

Merge request reports

Loading
Loading