Skip to content

feat(ui): portal all tooltips#17448

Open
paulpopus wants to merge 7 commits into
mainfrom
feat/ui-portal-all-tooltips
Open

feat(ui): portal all tooltips#17448
paulpopus wants to merge 7 commits into
mainfrom
feat/ui-portal-all-tooltips

Conversation

@paulpopus

@paulpopus paulpopus commented Jul 22, 2026

Copy link
Copy Markdown
Member

This PR portals the Tooltip element across buttons and all instances so that it doesn't have any overflow issues within our UI and doesn't get clipped anymore.

Static position clarified as fixing the alignment of the tooltip, does not affect portalling.

Before

image

After

image

Checkboxes and radios

This also ended up fixing other placement issues of tooltips, such as error fields on checkboxes and radio fields:

Before

image

After

image

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

📦 esbuild Bundle Analysis for payload

This analysis was generated by esbuild-bundle-analyzer. 🤖
This PR introduced no changes to the esbuild bundle! 🙌

@JarrodMFlesch

JarrodMFlesch commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

I like the direction here.

I do think we could update the positioning logic though.

Right now, we measure the trigger and rely mostly on window scroll and resize events to keep that measurement updated. That leaves a couple of gaps:

  • If the trigger moves or changes size because the surrounding layout changes, the tooltip can stay positioned against the old rectangle.
  • If the tooltip mounts inside something hidden with display: none, we clear the anchor rectangle. When that content becomes visible, nothing necessarily causes us to measure it again, so the tooltip can stay missing.

We already use @floating-ui/dom for the ReactSelect menu portal in #17304. I’m not suggesting we share or extract the ReactSelect implementation. I do think Tooltip should follow the same general approach instead of maintaining its own manual measurements and event listeners.

For the tooltip, that would mean:

  • Treating the trigger as the reference element and the portaled tooltip as the floating element.
  • Using computePosition with a fixed strategy.
  • Using autoUpdate to respond to scrolling, element resizing, and layout shifts.
  • Using the appropriate middleware, such as offset, flip, shift, size, and potentially arrow.
  • Handling visualViewport changes the same way we do for ReactSelect.
  • Applying the resulting coordinates directly to the tooltip element through a ref instead of storing a new DOMRect in React state.

staticPositioning

I’d keep all tooltips portaled, including ones using staticPositioning.

We currently use that prop for every FieldError and for the sidebar toggle. Its purpose is to disable adaptive placement, not to decide whether the tooltip gets portaled.

We should preserve that behavior and update the PR description and JSDoc so the distinction is clear.

Tests

The cases I’d want covered are:

  • The tooltip escapes an overflow: hidden container.
  • The tooltip updates when its trigger moves or resizes.
  • A tooltip whose trigger was initially hidden appears after the trigger becomes visible.
  • staticPositioning tooltips still portal without changing their placement behavior.

I think centralizing this in Tooltip is a good call. I’d just like us to use the same Floating UI positioning pattern we’ve already established and cover these lifecycle cases before merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants