Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Tooltip] Tooltip component doesn't work on iOS Safari (14.4) #955

Closed
jordie23 opened this issue Nov 4, 2021 · 18 comments
Closed

[Tooltip] Tooltip component doesn't work on iOS Safari (14.4) #955

jordie23 opened this issue Nov 4, 2021 · 18 comments

Comments

@jordie23
Copy link

jordie23 commented Nov 4, 2021

Bug report

The Tooltip component isn't working on iOS Safari.

Current Behavior

Tapping on a tooltip component doesn't show the content

Expected behavior

Content shows when tapping a Tooltip component

Reproducible example

  1. Load up this page on iOS (14.4 is what I used in simulator and 14.6 on my own phone):

https://www.radix-ui.com/docs/primitives/components/tooltip

  1. Tap the + in the example.

Suggested solution

Content should show on tap events

Your environment

Below is my env, but it's also not working on whatever your website env is.

Software Name(s) Version
Radix Package(s) @radix-ui/react-tooltip 0.1.1
React n/a 17.0.2
Browser Safari iOS 14.4 ad 14.6
Assistive tech n/a
Node n/a v14.17.3
npm/yarn npm 6.14.13
Operating System iOS 14.4
@benoitgrelard
Copy link
Collaborator

This is by design. Tooltips are problematic on touch devices because there is no hover interaction, so if it was on tap it would fight with the general button action, or require 2 taps. That's also why tooltips should just be used generally for extra information that isn't mandatory do use your interface.

@jjenzz
Copy link
Contributor

jjenzz commented Nov 4, 2021

I wonder if it is worth looking into this though. Material UI provide a "touch" option for this which appears to be a longpress: https://mui.com/components/tooltips/#triggers

Perhaps we can do something similar?

@jordie23
Copy link
Author

jordie23 commented Nov 5, 2021

Tooltips aren't usually just superfluous information, they generally important information just hidden away until required. I do typically see the hover ability on desktop replaced with a tap action on mobile.

I mean, here, on your own documentation page you've made the tooltips accessible on mobile devices. Why would you do that if it wasn't important information?

IMG_6009

I'd suggest one or more of the following actions:

  • modify the documentation page to mention the tooltip component doesn't work for and isn't designed for mobile/touch devices
  • add an example on how it could be modified to work with touch devices (as per your own use case ^)
  • provide default support for touch devices, perhaps behind a boolean prop flag? If it's opt-in, the developer would understand the limitations/interference this might have with button actions.

@benoitgrelard
Copy link
Collaborator

I wonder if it is worth looking into this though. Material UI provide a "touch" option for this which appears to be a longpress: https://mui.com/components/tooltips/#triggers
Perhaps we can do something similar?

I personally don't think that makes much sense (for the reasons highlighted below). A tooltip is usually used to provide more info about a trigger that already does something. How would you know you can long press it?

I mean, here, on your own documentation page you've made the tooltips accessible on mobile devices. Why would you do that if it wasn't important information?

That is not a tooltip, that's a popover, the interaction for those is pointer down.
The big difference here is that there isn't 2 functionality attached to the same trigger.
Tooltips work fine with focus or hover, because they are attached to triggers with pointer down actions.

So it looks like perhaps we need to emphasize more in the docs the differences between the patterns?

@jordie23
Copy link
Author

jordie23 commented Nov 8, 2021

That is not a tooltip, that's a popover, the interaction for those is pointer down.
The big difference here is that there isn't 2 functionality attached to the same trigger.
Tooltips work fine with focus or hover, because they are attached to triggers with pointer down actions.

So it looks like perhaps we need to emphasize more in the docs the differences between the patterns?

AHHHH I see. That makes so much more sense now. I guess I didn't think about the popover being used in that way, I figured it was always for more complex use cases. Thanks for the clarification.

I think perhaps a note on the Tooltip page referencing the Popover might help silly people like me who generalise what a Tooltip is?

@jjenzz
Copy link
Contributor

jjenzz commented Nov 8, 2021

A tooltip is usually used to provide more info about a trigger that already does something

True. Plus, I just realised that the WAI ARIA tooltip docs (wip) suggest they're intended for focus/hover only.

I think perhaps a note on the Tooltip page referencing the Popover might help silly people like me who generalise what a Tooltip is?

Agreed. We have spoken about having a "When to use" section for the components as these patterns can appear to overlap, it's all a bit confusing. I'll let the team know that we've had another request for this 👍

EDIT: I created a new issue on the website repo instead radix-ui/website#272

@yangmingshan
Copy link

I found a naive way to make Tooltip working on both pointing device and touch device. In my use case, it's just an info icon like your website, it's not a general button, so there will be no event conflict. Not sure any downside of this solution.

https://codesandbox.io/s/smoosh-forest-553n1q?file=/App.js

And it let me thinking, can we add an option to make Tooltip working on both pointing device and touch device, but it can not be a general button, so we can avoid event conflict? 🤔

@guidominguesnk
Copy link

You are a god

@yangmingshan
Copy link

@benoitgrelard Sorry to ping you, but what's your thoughts?

@benoitgrelard
Copy link
Collaborator

Hey @yangmingshan,

In my use case, it's just an info icon like your website, it's not a general button, so there will be no event conflict. Not sure any downside of this solution.

In that case, a Popover is probably more appropriate then.

And it let me thinking, can we add an option to make Tooltip working on both pointing device and touch device, but it can not be a general button, so we can avoid event conflict? 🤔

No that's not possible, a tooltip is here to display a "tip" regarding a given "tool", there should always be an action behind this, so it's not possible to avoid event conflicts.

@kaptn3
Copy link

kaptn3 commented Feb 3, 2023

@benoitgrelard hi! sorry for ping, but what else I need opens popover on hover event on desktop and on click event on mobile devices, what I must use popover or tooltip?

@benoitgrelard
Copy link
Collaborator

@kaptn3 If you want an interaction that works on mobile (touch) then it would have to be popover. That said, it doesn't open on hover on desktop as that's not the intended behaviour of a popover, so you'd have to override that behaviour if that's what you wanted, I'd warn you against it though as that's not really natural as focus moves into the popover when it opens for accessibility reasons.

@kaptn3
Copy link

kaptn3 commented Feb 6, 2023

@benoitgrelard got it, thank you!

@sksiyer
Copy link

sksiyer commented Feb 6, 2023

@benoitgrelard I think that the convention for mobile is that tooltips appear...
See Bootstrap: https://getbootstrap.com/docs/4.0/components/tooltips
See Carbon: https://carbondesignsystem.com/components/tooltip/code

@benoitgrelard
Copy link
Collaborator

There are no such convention in the existing accessibility guidelines as touch is problematic.

This is because a tooltip is a "tip" for a "tool", that tool is an interactive element (99% of the time should be a button), hence on touch devices if you'd show the tooltip on touch, it would already be too late anyway because the action the tool performs would have been triggered so it simply doesn't make much sense. Other alternatives result in poor UX such as asking the user to press twice (once for the tooltip, and a second for the action).

Hopefully this makes sense why they are ignored on touch.

See here where it specifically only mentions hover and focus: https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/

cmnord added a commit to cmnord/jep that referenced this issue Mar 17, 2023
Instead of using the clue list at the bottom, click on an answered clue to show
a popover with its answer.

Radix popover: https://www.radix-ui.com/docs/primitives/components/popover

Use a popover instead of a tooltip because Radix tooltips do not show up on
mobile (no hover events). See discussion:
radix-ui/primitives#955

To make clue button a popover trigger, pass a ref with React.forwardRef like
so:
- radix-ui/primitives#953
- https://buildui.com/videos/do-your-react-components-compose
cmnord added a commit to cmnord/jep that referenced this issue Mar 17, 2023
Unfortunately Radix tooltips do not work on mobile (no hover events). See
discussion: radix-ui/primitives#955
cmnord added a commit to cmnord/jep that referenced this issue Mar 17, 2023
Instead of using the clue list at the bottom, click on an answered clue to show
a popover with its answer.

Radix popover: https://www.radix-ui.com/docs/primitives/components/popover

Use a popover instead of a tooltip because Radix tooltips do not show up on
mobile (no hover events). See discussion:
radix-ui/primitives#955

To make clue button a popover trigger, pass a ref with React.forwardRef like
so:
- radix-ui/primitives#953
- https://buildui.com/videos/do-your-react-components-compose
cmnord added a commit to cmnord/jep that referenced this issue Mar 17, 2023
Unfortunately Radix tooltips do not work on mobile (no hover events). See
discussion: radix-ui/primitives#955
cmnord added a commit to cmnord/jep that referenced this issue Mar 17, 2023
Unfortunately Radix tooltips do not work on mobile (no hover events). See
discussion: radix-ui/primitives#955
cmnord added a commit to cmnord/jep that referenced this issue Mar 17, 2023
Instead of using the clue list at the bottom, click on an answered clue to show
a popover with its answer.

Radix popover: https://www.radix-ui.com/docs/primitives/components/popover

Use a popover instead of a tooltip because Radix tooltips do not show up on
mobile (no hover events). See discussion:
radix-ui/primitives#955

To make clue button a popover trigger, pass a ref with React.forwardRef like
so:
- radix-ui/primitives#953
- https://buildui.com/videos/do-your-react-components-compose
cmnord added a commit to cmnord/jep that referenced this issue Mar 17, 2023
Unfortunately Radix tooltips do not work on mobile (no hover events). See
discussion: radix-ui/primitives#955
@MaximilianDietel03
Copy link

I've came across this issue as well and ended up modifying the Popover component to fit my requirements. When using the exported components, the Popover is touchable and hoverable.

import * as React from 'react'
import { FC, useContext, useState } from 'react'

import * as PopoverPrimitive from '@radix-ui/react-popover'

const PopoverOnOpenChangeContext = React.createContext({
  onOpenChange: (open: boolean) => {
    /* empty */
  },
  timeoutId: { current: null } as React.MutableRefObject<ReturnType<typeof setTimeout> | null>,
})

const Popover: FC<React.ComponentProps<typeof PopoverPrimitive.Root>> = ({
  open,
  onOpenChange,
  ...props
}) => {
  const [defaultOpen, defaultOnOpenChange] = useState(false)
  const timeoutId = React.useRef(null)

  return (
    <PopoverOnOpenChangeContext.Provider
      value={{
        onOpenChange: onOpenChange ?? defaultOnOpenChange,
        timeoutId,
      }}
    >
      <PopoverPrimitive.Root
        {...props}
        open={open ?? defaultOpen}
        onOpenChange={onOpenChange ?? defaultOnOpenChange}
      />
    </PopoverOnOpenChangeContext.Provider>
  )
}

const PopoverTrigger: React.FC<any> = (props) => {
  const { onOpenChange, timeoutId } = useContext(PopoverOnOpenChangeContext)

  return (
    <PopoverPrimitive.Trigger
      {...props}
      onMouseEnter={() => {
        timeoutId.current && clearTimeout(timeoutId.current)
        onOpenChange(true)
      }}
      onMouseLeave={() => {
        const _timeoutId = setTimeout(() => onOpenChange(false), 300)
        timeoutId.current = _timeoutId
      }}
    />
  )
}

const PopoverContent = React.forwardRef<
  React.ElementRef<typeof PopoverPrimitive.Content>,
  React.PropsWithoutRef<React.ComponentProps<typeof PopoverPrimitive.Content>>
>(({ align = 'center', sideOffset = 4, ...props }, ref) => {
  const { onOpenChange, timeoutId } = useContext(PopoverOnOpenChangeContext)

  return (
    <PopoverPrimitive.Portal>
      <PopoverPrimitive.Content
        ref={ref}
        align={align}
        sideOffset={sideOffset}
        onMouseEnter={() => {
          timeoutId.current && clearTimeout(timeoutId.current)
        }}
        onMouseLeave={() => {
          const _timeoutId = setTimeout(() => onOpenChange(false), 300)
          timeoutId.current = _timeoutId
        }}
        {...props}
      />
    </PopoverPrimitive.Portal>
  )
})
PopoverContent.displayName = PopoverPrimitive.Content.displayName

export { Popover, PopoverContent, PopoverTrigger }

@matstyler
Copy link

Simple implementation for both - mobile and desktop:

export default function Tooltip({ alwaysOpen, children }: Props): ReactElement {
    const [open, setOpen] = useState(false)

    return (
        <RadixTooltip.Root
            open={alwaysOpen || open}
            delayDuration={0}
            onOpenChange={setOpen}
        >
            <div onClick={() => setOpen(true)}>{children}</div>
        </RadixTooltip.Root>
    )
}

@kainanaina
Copy link

To add to solution above, there was still an issue where on mobile I needed to click twice to get the tooltip, because onOpenChange was running before click handler on initial click somehow. The "solution" was to add timeout state update to onFocus on Trigger element.

Here is the final code:

const [open, setOpen] = useState(false);

  return (
    <RadixTooltip.Provider>
      <RadixTooltip.Root open={open} onOpenChange={setOpen}>
        <RadixTooltip.Trigger
          onClick={() => setOpen((prevOpen) => !prevOpen)}
          onFocus={() => setTimeout(() => setOpen(true), 0)} // timeout needed to run this after onOpenChange to prevent bug on mobile
          onBlur={() => setOpen(false)}
        >
          {children}
        </RadixTooltip.Trigger>
        <RadixTooltip.Portal>
          <RadixTooltip.Content>
            // tooltip content
            <RadixTooltip.Arrow />
          </RadixTooltip.Content>
        </RadixTooltip.Portal>
      </RadixTooltip.Root>
    </RadixTooltip.Provider>
  );

robinandeer added a commit to HedvigInsurance/racoon that referenced this issue Nov 30, 2023
<!--
PR title: GRW-123 / Feature / Awesome new thing
-->

## Describe your changes

- Open Tooltip on click

<!--
What changes are made?
If there are many changes, a list might be a good format.
If it makes sense, add screenshots and/or screen recordings here.
-->

## Justify why they are needed

- Fix issue on mobile where the tooltip would not open when tapping on the icon

- Reference: radix-ui/primitives#955 (comment)

## Checklist before requesting a review

- [ ] I have performed a self-review of my code
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

No branches or pull requests

10 participants