import React, { useState } from 'react'
import {
autoPlacement,
autoUpdate,
flip,
FloatingPortal,
offset,
type Placement,
safePolygon,
shift,
useDismiss,
useFloating,
useFocus,
useHover,
useInteractions,
useRole,
useTransitionStyles,
} from '@floating-ui/react'
import styles from './_tooltip.module.scss'
import Button from '../Button/Button'
import Icon from '../Icons/Icon'
import { SideDrawer } from '../SideDrawer/SideDrawer'
import { toast } from '../Toast/Toast'
import { useMediaQuery } from '../../hooks/responsiveHooks'
import { c } from '../../translations/LibraryTranslationService'
export type TooltipProps = {
/** The element that you need to hover to show the tooltip. */
tooltipContent?:
| ((props: { setVisible: (value: boolean) => void }) => React.JSX.Element)
| React.ReactNode
/** The content inside of the tooltip. */
children: React.ReactNode
/** The position of the tooltip. */
position?: Placement | 'auto'
/** Optional className for the tooltip. */
className?: string
/** Optional prop to restrict the max width of the tooltip. */
maxWidth?: string
/** Optional prop to show the `SideDrawer` when in a mobile view. */
useSideDrawerForMobile?: boolean
/** Optional prop to show custom header content on `SideDrawer` for mobile view. Only applicable in case of `useSideDrawerForMobile` is set to true. */
sideDrawerHeader?: string
/** Optional prop to show copy icon to copy the tooltip content. */
copyToClipboard?: boolean
/** Optional prop to set the color of the arrow. */
/**
* @deprecated This prop is no longer supported.
**/
arrowColor?: 'medium-purple' | 'red' | 'green' | 'blue' | 'white'
/** Optional prop to remove padding */
noPadding?: boolean
/** Optional prop to disable the tooltip. */
disabled?: boolean
/** Optional prop to disable click event propagation on the tooltip. */
enableClickThrough?: boolean
/** Optional prop to add a test id to the Tooltip for QA testing */
qaTestId?: string
}
const Tooltip = ({
tooltipContent,
children,
position = 'right',
className = '',
maxWidth = '300px',
useSideDrawerForMobile = false,
sideDrawerHeader = '',
copyToClipboard = false,
noPadding = false,
disabled = false,
enableClickThrough = false,
qaTestId = 'tooltip',
}: TooltipProps): React.JSX.Element => {
const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false)
const screenLargerThanMd = useMediaQuery({ type: 'min', breakpoint: 'md' })
const [isTooltipOpen, setIsTooltipOpen] = useState(false)
const isCopyToClipboardValid =
copyToClipboard && typeof tooltipContent === 'string'
const {
refs: { setReference, setFloating },
floatingStyles,
context,
} = useFloating({
open: isTooltipOpen && !disabled,
onOpenChange: setIsTooltipOpen,
strategy: 'fixed',
...(position !== 'auto' ? { placement: position } : {}),
middleware: [
offset(8),
...(position === 'auto' ? [autoPlacement()] : [flip()]), // autoPlacement() is used to automatically place the tooltip in the best position and it internally uses flip()
shift({ padding: 4 }),
],
whileElementsMounted: autoUpdate,
})
const role = useRole(context, { role: 'tooltip' })
const hover = useHover(context, {
move: false,
delay: { open: 100, close: 0 },
handleClose:
// Makes the Tooltip interactive
safePolygon({
requireIntent: false,
blockPointerEvents: true,
buffer: 1,
}),
})
const focus = useFocus(context)
const dismiss = useDismiss(context, {
ancestorScroll: true, // close Tooltip on scrolling
})
const { isMounted, styles: transitionStyles } = useTransitionStyles(context, {
initial: {
opacity: 0,
transform: 'scale(0.8)',
},
duration: 200, // transition duration when opening and closing the Tooltip
})
const { getReferenceProps, getFloatingProps } = useInteractions([
hover,
focus,
dismiss,
role,
])
if (useSideDrawerForMobile && !screenLargerThanMd) {
return (
<>