Skip to content

fix(226629): tooltips after SPA/Turbo navigation + hotspot no longer shifts its element (226620) - #196

Merged
boehlerlukas merged 1 commit into
masterfrom
fix-226629-226620-tooltips
Oct 28, 2026
Merged

boehlerlukas merged 1 commit into
masterfrom
fix-226629-226620-tooltips

Conversation

@boehlerlukas

Copy link
Copy Markdown
Contributor

Fixes two Hubflo tooltip tickets (#226629, #226620). Hubflo loads sdk.gleap.io/latest and runs on Turbo Drive.

#226629: hotspot only showed after a full reload

Root causes in src/GleapTooltipManager.js:

  • Observer bound to the old <body>. observe(document.body): Turbo Drive swaps in a new <body> on every visit, so the observer kept watching the detached one and never saw the new page. It now observes document.documentElement.
  • URL change only checked the nodes in that mutation batch. If the target rendered before the URL flipped (or nothing changed in the DOM after it), the element was never checked again. Every URL change now re-runs the page rules, scans the page (checkForTooltips), and removes tooltips whose rules no longer match. The URL is also polled every second, as the other managers do.
  • A stale data-gleap-tooltip marker blocked re-linking. Turbo restores cached page snapshots, which still carry our marker and a dead anchor. A re-render that sets the element's text (React textContent) also wipes the hotspot. linkTooltip returned early in both cases. It now re-links unless the element has a working link, and it drops dead popup copies that come back with a restored snapshot.
  • destroy() stopped the observer for good. destroy() runs on every session change, and nothing restarted the observer. load() now calls start() again (it is idempotent). The observer is created directly, without waiting on requestAnimationFrame.
  • Removing an ancestor of a linked element now cleans up its popup and autoUpdate. Hover listeners are removed on cleanup.

#226620: a gap ("tab space") was added before the heading text

The anchor was inserted as the element's first child as a floated inline-block. In a flex/grid heading it became a flex item, so gap was applied. It also made the real first child a sibling, so space-x / * + * margins applied, and it broke :first-child styles. The anchor is now a 0×0 position:absolute box (inline !important), appended as the last child, so the host's layout is untouched.
The hotspot used to be placed at range(selectNodeContents).width + paddingLeft. That measurement included our own anchor and hotspot, which is why the icon sat on top of "permissions" in the customer's first screenshot. It now uses the content's right edge, measured up to our anchor.

Also: an explicit Offset X of 0 was replaced by the 4px default (!tooltip.offsetX). 0 is now honored. The dashboard editor and the server both default to 0.

Verification

Built the SDK, then loaded the old (master) and new bundles in a static page with a mocked Gleap API:

  • Layout (master → this branch): flex heading text shift 16px → 0, space-x first child shift 24px → 0, hotspot vs. text end −12px (overlapping) → 0 with Offset X 0. Hover popup still shows.
  • SPA (this branch): Turbo-style <body> swap → linked within 200 ms; restored snapshot clone → re-linked with 1 anchor and no stale popups; textContent re-render → hotspot back; target rendered before pushState with no later mutation → linked; navigating to a non-matching URL → tooltip removed; a late render on a matching page → linked.
  • npx jest: 34 suites / 586 tests pass.

No release in this PR. It ships with the next JS SDK release (/latest CDN).

🤖 Generated with Claude Code

… shifts its element (226620)

#226629: hotspot tooltips only appeared after a full reload.
- The MutationObserver watched document.body; Turbo Drive swaps in a new
  <body> on every visit, so new pages were never seen. Observe
  document.documentElement instead.
- On a URL change only the nodes added in that same batch were checked,
  so a target rendered before the URL flipped was missed. Re-run the page
  rules and scan the page on every URL change (also polled every second),
  and remove tooltips whose page rules no longer match.
- Elements that still carried our marker (Turbo page snapshot, or a
  re-render that wiped the hotspot) were skipped forever. Re-link them;
  drop dead popup copies from restored snapshots.
- destroy() (run on every session change) stopped the observer for good.
  load() restarts it; no rAF wait needed for documentElement.

#226620: the hotspot anchor was inserted as the element's first child as a
floated inline-block, so flex/grid gaps, sibling margins (space-x) and
:first-child styles pushed the element's content right. The anchor is now
an absolutely positioned 0x0 box appended last, and the hotspot sits at the
content's right edge (our anchor excluded from the measurement), so it no
longer overlaps the text.

Also: an explicit Offset X of 0 was replaced by the 4px default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@boehlerlukas
boehlerlukas merged commit 005566b into master Oct 28, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant