base-ui icon indicating copy to clipboard operation
base-ui copied to clipboard

[Tooltip] Component and Hook

Open atomiks opened this issue 10 months ago • 6 comments

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 avatar Apr 03 '24 08:04 atomiks

@atomiks Hilarious to see this in one day 🤣🔥

  1. Tooltip should close then you click whatever is inside Anchor.
  2. We should specify a default delay, something around 750ms.
  3. 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?
  4. 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 this skipDelayDuration 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.
  5. Do we need a global Tooltip Provider? Related to the above point.
  6. 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.
  7. I think anchorGap | offset should be on Content rather than Root.
  8. What is the benefit of the allowTouch prop? Is there an issue with just having tooltips not work on touch devices?

colmtuite avatar Apr 03 '24 09:04 colmtuite

@colmtuite

  1. Good point, will add.
  2. 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).
  3. 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.
  4. 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.
  5. ^
  6. I personally like that it matches the gap prop in flexbox. But yeah, it's called offset in Floating UI - the problem is there are two different types of offsets (side and alignment axis).
  7. That does make more sense
  8. 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.

atomiks avatar Apr 03 '24 09:04 atomiks

  1. Is this a component? Does it exist outside of Tooltip? If it's a component, it's missing from the docs Anatomy section.
  2. sideOffset and alignmentOffset seems like the way forward here? Radix does similar.
  3. 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?

colmtuite avatar Apr 03 '24 10:04 colmtuite

  1. 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.)
  2. I like those! I guess separating side and alignment into separate props instead of Floating UI's placement may work better if we do that? Or is a single placement prop still more ergonomic?

  1. 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?

atomiks avatar Apr 03 '24 10:04 atomiks

Could you extract the transition-related code to another small PR so I can use it in the Dialog?

michaldudak avatar May 07 '24 20:05 michaldudak

Netlify deploy preview

https://deploy-preview-264--base-ui.netlify.app/

Generated by :no_entry_sign: dangerJS against e31058ae5d65641cb4cbe5cbcde3cf4a4114d9a8

mui-bot avatar May 23 '24 03:05 mui-bot

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.

michaldudak avatar May 23 '24 11:05 michaldudak

It seems that the TooltipGroup's delay is not taken into consideration.

Thanks for the spot - fixed

atomiks avatar May 23 '24 13:05 atomiks

I just noticed the tooltip is rendered within two nested portals. Is this intended? image

michaldudak avatar May 23 '24 14:05 michaldudak

I just noticed the tooltip is rendered within two nested portals. Is this intended?

No, mistake when adding the TooltipPopupRoot component. Fixed.

atomiks avatar May 24 '24 04:05 atomiks

A quick feedback:

  1. 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.

  2. 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?

  3. 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.

  4. I guess we should transfer most of the issues of 3. from the Material UI repository to here.

oliviertassinari avatar May 24 '24 09:05 oliviertassinari

@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

atomiks avatar May 24 '24 10:05 atomiks

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 be hover? I think it's the most common expectation, and would also match the the title 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 the side 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?

colmtuite avatar May 24 '24 13:05 colmtuite

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 avatar May 27 '24 05:05 atomiks

@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 :)

mikkelblanne avatar Jun 04 '24 08:06 mikkelblanne

Good catch, thanks!

michaldudak avatar Jun 04 '24 08:06 michaldudak

The docs say Tooltip is still "Planned". Can we use this or not?

Floriferous avatar Jul 08 '24 12:07 Floriferous

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

atomiks avatar Jul 08 '24 13:07 atomiks