All files / components/Tooltip Tooltip.tsx

83.33% Statements 10/12
72.72% Branches 8/11
75% Functions 3/4
83.33% Lines 10/12

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71                                          4x   14x         14x 5x 5x             14x           14x   14x 14x                 14x                              
import { useState } from "react"
 
export type TooltipProps = {
	/** Tooltip content for example a text. */
	content: React.ReactNode
	/** Content when you hover, tooltip is shown. */
	children: React.ReactNode
	/** Position where tooltip is displayed. */
	position?: "top" | "right" | "bottom" | "left"
	/** Time when the tooltip is displayed. */
	delay?: number
	/** Adjust margin for top and left position when tooltip gets bigger. */
	customMargin?: string
	/** Add custom classname to tooltip wrapper. */
	wrapperClassname?: string
}
 
/**
 * Wrap around Component and show tooltip when hover over it.
 * @example <Tooltip content="Tooltip">Hover over me</Tooltip>
 */
export const Tooltip: React.FC<TooltipProps> = ({ children, content, delay, wrapperClassname = "", position = "top", customMargin = "-40px" }) => {
	let timeout: NodeJS.Timeout
	const [isShown, setIsShown] = useState(false)
 
	/**
	 * Shows Tooltip
	 */
	const showTooltip = () => {
		timeout = setTimeout(() => {
			setIsShown(true)
		}, delay || 0)
	}
 
	/**
	 * Hide Tooltip
	 */
	const hideTooltip = () => {
		clearInterval(timeout)
		setIsShown(false)
	}
 
	// CSS Classes
	const baseClass = "absolute bg-black text-white text-sm rounded-md left-1/2 -translate-x-1/2 whitespace-nowrap z-50 px-4 py-2"
	const triangleBaseClass =
		"before:content-[' '] before:left-1/2 before:h-0 before:w-0 before:absolute before:border-transparent before:border-solid before:border-[8px] before:ml-[-8px]"
	const tooltipClassesPosition = {
		["top"]: "text-center before:top-full before:border-t-black",
		["right"]:
			"left-[calc(100%_+_20px)] top-1/2 translate-x-0 -translate-y-1/2 before:left-[-8px] before:top-1/2 before:translate-x-0 before:-translate-y-1/2 before:border-r-black",
		["bottom"]: "text-center before:bottom-full before:border-b-black",
		["left"]:
			"left-auto right-[calc(100%_+_20px)] top-1/2 translate-x-0 -translate-y-1/2 before:left-auto before:right-[-16px] before:top-1/2 before:translate-x-0 before:-translate-y-1/2 before:border-l-black",
	}
 
	return (
		<div datacy="tooltip-wrapper" className={`relative inline-block ${wrapperClassname}`} onMouseEnter={showTooltip} onMouseLeave={hideTooltip}>
			{children}
			{isShown && (
				<div
					datacy="tooltip"
					style={position == "top" ? { top: customMargin } : position == "bottom" ? { bottom: customMargin } : {}}
					className={`${baseClass} ${triangleBaseClass} ${tooltipClassesPosition[position]}`}
				>
					{content}
				</div>
			)}
		</div>
	)
}