everweij / react-laag

Hooks to build things like tooltips, dropdown menu's and popovers in React

Home Page:https://www.react-laag.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Keep tooltip open while hovering over tooltip (rather than closing when leaving trigger)

linden-dg opened this issue · comments

Is your feature request related to a problem? Please describe.
I've created a navigation sidebar that, when you hover over the icon, displays a tooltip with the label for that route. I've got the tooltip working well, but on user testing, some of my users are trying to click on the tooltip rather than the icon.

Because I'm using useHover [const [isOpen, hoverProps] = useHover({ delayEnter: 100, delayLeave: 200 });] the tooltip closes as the user mouseLeave's off the triggering element and tries to click the tooltip.

Describe the solution you'd like
Some way/logic to stop the delayLeave from triggering if the tooltip is hovered as well. Basically if the element or tooltip is hovered then keep the tooltip open, otherwise trigger delayLeave.

Describe alternatives you've considered
Currently I'm bumping up the delayLeave number to e.g. 500 to keep the tooltip open for longer, but this doesn't look that great from a UX perspective.

I have a similar problem. The transparent part of the arrow overlaps the text and disappears when the tooltip is hovered.

alt text

I fix it like this:

<Arrow
 {...arrowProps}
 style={{
  ...arrowProps.style,
  width:
   arrowProps.layerSide === "left" ||
   arrowProps.layerSide === "right"
    ? '8px'
    : undefined,
  height:
   arrowProps.layerSide === "top" ||
   arrowProps.layerSide === "bottom" ||
   arrowProps.layerSide === "center"
    ? '8px'
    : undefined,
 }}
 size={8}
/>

Is your feature request related to a problem? Please describe.
I've created a navigation sidebar that, when you hover over the icon, displays a tooltip with the label for that route. I've got the tooltip working well, but on user testing, some of my users are trying to click on the tooltip rather than the icon.

Because I'm using useHover [const [isOpen, hoverProps] = useHover({ delayEnter: 100, delayLeave: 200 });] the tooltip closes as the user mouseLeave's off the triggering element and tries to click the tooltip.

Describe the solution you'd like
Some way/logic to stop the delayLeave from triggering if the tooltip is hovered as well. Basically if the element or tooltip is hovered then keep the tooltip open, otherwise trigger delayLeave.

Describe alternatives you've considered
Currently I'm bumping up the delayLeave number to e.g. 500 to keep the tooltip open for longer, but this doesn't look that great from a UX perspective.

Try this:

import React, { useRef } from 'react';
import { Arrow, useHover, useLayer, mergeRefs } from 'react-laag';

type Props = {
    title?: string;
};

const TooltipWrapper: React.FC<Props> = ({ children, title }) => {
    const ref = useRef<HTMLDivElement>(null);
    const [isOver, hoverProps] = useHover({ delayEnter: 100, delayLeave: 200 });

    const { renderLayer, triggerProps, layerProps, arrowProps } = useLayer({
        isOpen: isOver
    });

    const mouseLeaveHandler = (event: React.MouseEvent<HTMLSpanElement, MouseEvent>) => {
        const { relatedTarget } = event;
        if (!(relatedTarget instanceof Element && ref.current?.contains(relatedTarget))) {
            hoverProps.onMouseLeave(event);
        }
    };

    return (
        <>
            {isOver &&
                renderLayer(
                    <div {...layerProps} ref={mergeRefs(layerProps.ref, ref)} onMouseLeave={hoverProps.onMouseLeave}>
                        {title}
                        <Arrow {...arrowProps} />
                    </div>
                )}
            <span {...triggerProps} {...hoverProps} onMouseLeave={mouseLeaveHandler}>
                {children}
            </span>
        </>
    );
};

export default TooltipWrapper;

Seems like such a common use case — would we really have to write that much code to fix it?

Seems like such a common use case — would we really have to write that much code to fix it?

This is just an example of how to work around it before it fixes.

Thanks for submitting this issue!

What I generally do, and here's an example, is to not only spread the hoverProps on the trigger-element, but also on the layer-element itself. So, from the perspective of useHover() it doesn't matter where the 'signals' (enter/leave) originated from, so you could attach the hoverProps (onMouseEnter / onMouseLeave) to any elements you'd like.

// minimal example

function Example() {
  const [isOver, hoverProps] = useHover({ delayLeave: 100 });
  
    const { renderLayer, triggerProps, layerProps } = useLayer({
        isOpen: isOver
    });

    return (
        <>
            {isOver &&
                renderLayer(
                    <div {...layerProps} {...hoverProps}>
                        Layer!
                    </div>
                )}
            <button {...triggerProps} {...hoverProps}>
                Trigger!
            </button>
        </>
    );
}

Does this help / answers your question?

commented

@everweij The problem with this, is this can't be done if I want the trigger to be click and not hover.
I am now facing an issue where I nee the tooltip to support both click and hover, and I find it very troubling (if I were to do this, the click inside the tooltip will close it).