import React, { type ReactNode, useState, forwardRef, type HTMLAttributes, useRef, useEffect, } from 'react' import Icon from '../../atoms/Icon' import IconButton from '../IconButton' /** * Specifies tooltip position. */ export type Side = 'top' | 'right' | 'bottom' | 'left' /** * Specifies tooltip alignment. */ export type Alignment = 'start' | 'center' | 'end' /** * Combines side + alignment (e.g., "top-start"). */ export type Placement = `${Side}-${Alignment}` export interface TooltipProps extends Omit, 'content'> { /** * Text/content of the tooltip. */ content: ReactNode /** * Defines the side or side-alignment (e.g., "top-center", "right-end") of the tooltip. */ placement?: Placement /** * If the tooltip can be closed by a button. */ dismissible?: boolean /** * Called when the dismiss button is clicked. */ onDismiss?: ( ev: | React.KeyboardEvent | React.MouseEvent ) => void /** * Element that activates the tooltip on hover/focus. */ children: ReactNode /** * ID to find this component in testing tools (e.g.: cypress, testing library, and jest). */ testId?: string /** * Maximum width of the tooltip. */ maxWidth?: number /** * ID for the tooltip content to be used with aria-describedby. */ describedById?: string } const Tooltip = forwardRef(function Tooltip( { content, placement = 'top-center', dismissible = false, onDismiss, children, testId = 'fs-tooltip', maxWidth = 300, describedById = 'tooltip-content', ...otherProps }, ref ) { const [open, setOpen] = useState(false) const [dismissed, setDismissed] = useState(false) const dismissButtonRef = useRef(null) const triggerRef = useRef(null) const handleDismiss = ( ev: | React.KeyboardEvent | React.MouseEvent ) => { onDismiss?.(ev) setOpen(false) setDismissed(true) } const toggleOpen = () => { if (dismissed) { setDismissed(false) } setOpen(true) } const handleKeyDown = (event: React.KeyboardEvent) => { if (event.key === 'Escape') { handleDismiss(event) } } useEffect(() => { if (open && dismissible) { dismissButtonRef.current?.focus() } }, [open, dismissible]) return (
setOpen(false)} onFocus={toggleOpen} onBlur={() => setOpen(false)} data-testid={testId} aria-describedby={describedById} onKeyDown={handleKeyDown} tabIndex={0} ref={triggerRef} > {children} {open && !dismissed && (
{content}
{dismissible && ( } aria-label="Dismiss tooltip" data-fs-tooltip-dismiss-button onClick={handleDismiss} ref={dismissButtonRef} /> )} )}
) }) export default Tooltip