base-ui
base-ui copied to clipboard
[Tooltip] Component and Hook
- [x] I have followed (at least) the PR section of the contributing guide.
Closes #32
Preview: https://deploy-preview-264--base-ui.netlify.app/base-ui/react-tooltip/ Transition experiment: https://deploy-preview-264--base-ui.netlify.app/experiments/tooltip/
TODO:
- [x] Tests - a lot of the implementation details are tested by Floating UI already, so we only need to test the wrapping APIs.
- [x] Complete documentation
@atomiks Hilarious to see this in one day 🤣🔥
- Tooltip should close then you click whatever is inside
Anchor
. - We should specify a default delay, something around 750ms.
- I'm not a fan of the object syntax for setting open and close inside the same delay prop. One reason is that it's not intuitive that setting
delay
also sets the close delay. Another is just the awkward ergonomics. I'd rather two separate props. However, it's also worth considering not supporting a close delay at all. Can you think of a reason why one is necessary? - When a tooltip is open, and I very quickly hover another element with a tooltip, the next tooltip should open instantly with no delay, regardless of its delay setting. There should be
ms
threshold for how much time must pass after the first tooltip closes, before you open the second tooltip, for its delay to be removed. Radix calls thisskipDelayDuration
and it's configurable. RA calls it "cooldown period" and it looks like it's not configurable. I'm guessing since we're not using a Tooltip Provider, and each tooltip is responsible only for its own settings, we can't configure this globally across all tooltips? If that's the case, then I think it's probably ok that it's not configurable. - Do we need a global Tooltip Provider? Related to the above point.
-
anchorGap
is confusing to me, since when I read it, I'm not 100% sure what "anchor" or "gap" means. I think "offset" is a more appropriate name. - I think
anchorGap
|offset
should be onContent
rather thanRoot
. - What is the benefit of the
allowTouch
prop? Is there an issue with just having tooltips not work on touch devices?
@colmtuite
- Good point, will add.
- I'm wondering if we should use the "mouse rest" behavior instead of a delay by default? Ideally, you only want to show the tooltip when the cursor is at rest, rather than an arbitrary delay (Floating UI supports both though).
- Fair enough. I'm not sure, the delay being symmetric by default and is how I've always done it and there hasn't really been any complaints, but I can see why you might want it to only affect the open delay, or have separate props.
- This is implemented as
TooltipDelayGroup
in the PR currently. It can be global, or wrap only a specific group, allowing you to change the delay granularly. - ^
- I personally like that it matches the
gap
prop in flexbox. But yeah, it's calledoffset
in Floating UI - the problem is there are two different types of offsets (side and alignment axis). - That does make more sense
- Devs may hide somewhat critical info behind tooltips in some cases, so it should be shown on touch by default imo (at least to get a consistent experience across all devices by default).
Hilarious to see this in one day 🤣🔥
Yeah 😄 as the review comment above says, it's really 2 years of work, compressed into 1 day since I can just reuse all that work I did.
- Is this a component? Does it exist outside of Tooltip? If it's a component, it's missing from the docs Anatomy section.
-
sideOffset
andalignmentOffset
seems like the way forward here? Radix does similar. - How does this work? Like what's the interaction? Touch devices don't support hover (which is how a tooltip is triggered), so I don't see how it would work?
- Yes it's a separate component, not documented currently. It's not really part of the default anatomy since it's a separate feature that wraps it. (Edit: it's now documented.)
- I like those! I guess separating
side
andalignment
into separate props instead of Floating UI'splacement
may work better if we do that? Or is a singleplacement
prop still more ergonomic?
- They tap it and it shows the tooltip; if they tap off it closes.
The main use case where this is necessary for touch input is the "infotip" pattern, where an info icon can be tapped to reveal information. It's not a button tool that performs an action, as the action itself is to show the infotip. For keyboard usage, this pattern may require a click
handler rather than focus
, if it's attached to a button. Not 100% sure about the screen reader/keyboard a11y of this.
Another potential solution is to use long press to show the tooltip on touch devices, rather than a tap. This lets them see the description without triggering the button. Probably somewhat rare?
Could you extract the transition-related code to another small PR so I can use it in the Dialog?
Netlify deploy preview
https://deploy-preview-264--base-ui.netlify.app/
Generated by :no_entry_sign: dangerJS against e31058ae5d65641cb4cbe5cbcde3cf4a4114d9a8
It seems that the TooltipGroup's delay
is not taken into consideration.
<Tooltip.Group delay={10000}>
<Tooltip.Root>
<AnchorButton>Anchor A</AnchorButton>
<TooltipPopup sideOffset={5}>Tooltip A</TooltipPopup>
</Tooltip.Root>
<Tooltip.Root>
<AnchorButton>Anchor B</AnchorButton>
<TooltipPopup sideOffset={5}>Tooltip B</TooltipPopup>
</Tooltip.Root>
</Tooltip.Group>
still opens after a short delay.
It seems that the TooltipGroup's delay is not taken into consideration.
Thanks for the spot - fixed
I just noticed the tooltip is rendered within two nested portals. Is this intended?
I just noticed the tooltip is rendered within two nested portals. Is this intended?
No, mistake when adding the TooltipPopupRoot
component. Fixed.
A quick feedback:
-
Tooltip.Group. Should this be built-in, meaning work without a provider? I get confused seeing this component. I assumed that we would want this behavior for all tooltips on the UI, regardless of their proximity, but because using a group is painful (especially if the app is split between components) then most developers won't use it.
-
Does most of the tests in https://github.com/mui/material-ui/blob/next/packages/mui-material/src/Tooltip/Tooltip.test.js passes? For example, are we missing mobile support?
-
How much of https://github.com/mui/material-ui/issues?q=is%3Aopen+is%3Aissue+label%3A%22component%3A+tooltip%22 did we use to design the component? For example, https://github.com/mui/base-ui/issues/413. I'm not asking in the sens of fixing all of these issues, but about using them to inform the design decisions, so we can later come back to the component and solve the issus with fewer breaking changes. Overall, point 2 feels a lot more important.
-
I guess we should transfer most of the issues of 3. from the Material UI repository to here.
@oliviertassinari
Tooltip.Group. Should this be built-in, meaning work without a provider?
They can use a global provider if they want (the rest of the app remains performant). A provider allow them to change the props for a certain group of tooltips by overriding a "global" one if necessary. Keeping it fully global goes against React's model for the most part.
Does most of the tests in https://github.com/mui/material-ui/blob/next/packages/mui-material/src/Tooltip/Tooltip.test.js passes? For example, are we missing mobile support?
We've completely re-thought about what tooltips should be. It does not work on mobile intentionally as tooltips are an anti-pattern on touch devices. The docs explain this further and what to use instead.
How much of ...
These are common issues we've thought of so far and we've either avoided them or know that they require the user to workaround it explicitly in their code
Tooltip.Group
- Group/Provider should be in Anatomy. imo it should communicated as the default way of using Tooltip for almost all users.
- For the same reason as above, Group/Provider should also be in the hero demo.
Delay
- imo the default
delay
is waaaaayyy too short. I recommend 750ms as the default. - imo
delayType
default should behover
? I think it's the most common expectation, and would also match the thetitle
attr bevaiour.
Docs Content
- I think we should remove Infotip warning message until we merge Popover, or whatever we end up doing for Infotip. I'm not sure how/if we will handle infotips, and don't want to reference components that we don't have yet.
- Remove “They are also useful for things like thumbnail tooltips when hovering over a progress bar when using a mouse.” just to keep the content shorter. imo you're getting the point across sufficiently without this sentence.
- We can shorten the Placement section a good bit.
- Merge the Placement section into a single code block.
- Remove the mention of
Tooltip.Popup
. You can see where theside
prop goes in the code block. - Remove the list of possible values? You can see this in the props table.
- What is the use case for styling the true rendered values of placement?
- Offsets
- Change “Offsets” heading to “Offset”
- Merge these into a single code block.
- Delay
- Merge delay and closeDelay into a single code block?
- Remove the "Default open" docs section? It's in the API Reference and not that important.
- Remove the "Hoverable content" docs section? It's in the API Reference.
- Styling
- Remove the code block?
Group/Provider should be in Anatomy. imo it should communicated as the default way of using Tooltip for almost all users.
It's a bit confusing in Anatomy
since Root
is the one that says "wraps all other components" but now you have this extra one. Given it's optional too, I've added it directly beneath Anatomy
to explain it a bit better.
imo the default delay is waaaaayyy too short. I recommend 750ms as the default. imo delayType default should be hover? I think it's the most common expectation, and would also match the the title attr bevaiour.
The thing with the rest
type is that it can be shorter than you need with hover
. With hover
, the timeout starts as soon as you enter the element, even if your cursor is gliding across it unintentionally, so it needs to be longer. With rest
, the user has stopped and is showing intentionality to use the tool, waiting for any extra info (the tooltip) to appear to help them. I found 200ms
is about the ideal length to show the tooltip as quickly as possible without it appearing under normal clicking circumstances (i.e. the user already knows the tool by heart and doesn't want to see the tooltip appear anymore). I can make it a bit longer, but the point is it doesn't actually need to be so long with this technique. Update: increased to 300ms
@atomiks Great to see this done - minor detail: In the initial description, "Closes" is currently referencing mui/material-ui instead of mui/base-ui #32 :)
Good catch, thanks!
The docs say Tooltip is still "Planned". Can we use this or not?
Hi @Floriferous, the docs that are live refer to an older version of Base UI that is no longer being developed. We are actively working on the new version of Base UI that will be released properly in the upcoming months, which has this component included. You can keep an eye out for the launch on the X account