Rapid Diffs: Restore "Viewed by me" tooltip on viewed checkbox

Summary

The Rapid Diffs "Viewed" checkbox is missing the "Viewed by me" tooltip that exists in the legacy Vue implementation. The tooltip was implemented during !222771 (merged) but was removed due to a Chrome-specific bug where the tooltip persists after scrolling and causes panel overflow.

This is a feature parity gap with the legacy diffs implementation.

Legacy behavior

In app/assets/javascripts/diffs/components/diff_file_header.vue (line 430), the legacy Viewed checkbox uses v-gl-tooltip.hover.focus.left.html with the text "Viewed by me" and an optional keyboard shortcut hint (<kbd>v</kbd>). The tooltip is defined via the MR_TOGGLE_REVIEW keybinding in app/assets/javascripts/behaviors/shortcuts/keybindings.js.

The legacy implementation does not exhibit the persistence/overflow bug in Chrome.

Root cause analysis

The has-tooltip CSS class (used for server-rendered HAML elements) and v-gl-tooltip (used in Vue components) take different initialization paths to the same underlying GlTooltip -> BTooltip -> BVTooltip stack:

  • v-gl-tooltip: Binds mouseenter/mouseleave and focusin/focusout directly on the element at directive bind time.
  • has-tooltip: Uses document-level event delegation (app/assets/javascripts/tooltips/index.js). On mouseenter or focus, it lazily creates a GlTooltip component instance targeting the element.

When the tooltip is open and the user scrolls, BVTooltip relies on a 100ms setInterval visibility poll (bv-tooltip.js, visibleCheck). This poll checks isVisible(target), which tests whether the element has layout dimensions - NOT whether it's in the viewport. An element scrolled out of view but still in the DOM passes this check, so the tooltip remains visible.

In Chrome specifically (not Firefox), when has-tooltip elements are inside Rapid Diffs <diff-file> web components, the tooltip persists after scrolling. This is observable when keyboard focus opens the tooltip: the tooltip remains visible as the user scrolls away from the checkbox and persists until the focus changes (via additional keyboard input or a click). Hover-triggered tooltips exhibit the same persistence in Chrome, but are more easily dismissed since mouse movement naturally triggers mouseleave.

Additional finding: trigger/triggers key mismatch

In app/assets/javascripts/main.js (line 111), initTooltips is called with trigger: 'hover' (singular key). However, initTooltips in tooltips/index.js (line 63) reads config.triggers (plural). The singular trigger key is silently ignored, and the system falls back to DEFAULT_TRIGGER = 'hover focus'. This means has-tooltip always responds to both hover AND focus events, regardless of the configured intent.

References

  • !222771 (merged) - MR where the tooltip was implemented and then removed
  • !222771 (merged)#note_3096439340 - Review thread documenting the overflow behavior (video attached)
  • app/assets/javascripts/tooltips/index.js - has-tooltip event delegation system
  • app/assets/javascripts/main.js:109-114 - Global tooltip initialization with mismatched key
  • app/assets/javascripts/diffs/components/diff_file_header.vue:428-437 - Legacy tooltip implementation

Proposal

  1. Investigate why Chrome handles the tooltip persistence differently from Firefox in the Rapid Diffs web component context
  2. Fix the trigger/triggers key mismatch in main.js / tooltips/index.js
  3. Restore the "Viewed by me" tooltip with placement: 'left' on the Rapid Diffs checkbox
Edited Feb 26, 2026 by Thomas Randolph
Assignee Loading
Time tracking Loading