@ -1,4 +1,4 @@
import React , { useCallback , useEffect } from 'react' ;
import React , { useCallback , useEffect , useId , useState } from 'react' ;
import { usePopperTooltip } from 'react-popper-tooltip' ;
import { GrafanaTheme2 } from '@grafana/data' ;
@ -24,7 +24,8 @@ export interface TooltipProps {
export const Tooltip = React . forwardRef < HTMLElement , TooltipProps > (
( { children , theme , interactive , show , placement , content } , forwardedRef ) = > {
const [ controlledVisible , setControlledVisible ] = React . useState ( show ) ;
const [ controlledVisible , setControlledVisible ] = useState ( show ) ;
const tooltipId = useId ( ) ;
useEffect ( ( ) = > {
if ( controlledVisible !== false ) {
@ -44,10 +45,9 @@ export const Tooltip = React.forwardRef<HTMLElement, TooltipProps>(
const { getArrowProps , getTooltipProps , setTooltipRef , setTriggerRef , visible , update } = usePopperTooltip ( {
visible : show ? ? controlledVisible ,
placement : placement ,
interactive : interactive ,
placement ,
interactive ,
delayHide : interactive ? 100 : 0 ,
delayShow : 150 ,
offset : [ 0 , 8 ] ,
trigger : [ 'hover' , 'focus' ] ,
onVisibleChange : setControlledVisible ,
@ -69,17 +69,23 @@ export const Tooltip = React.forwardRef<HTMLElement, TooltipProps>(
[ forwardedRef , setTriggerRef ]
) ;
// if the child has an aria-label, this should take precedence over the tooltip content
const childHasAriaLabel = 'aria-label' in children . props ;
return (
< >
{ React . cloneElement ( children , {
ref : handleRef ,
tabIndex : 0 , // tooltip should be keyboard focusable
tabIndex : 0 , // tooltip trigger should be keyboard focusable
'aria-describedby' : ! childHasAriaLabel && visible ? tooltipId : undefined ,
} ) }
{ visible && (
< Portal >
< div
data - testid = { selectors . components . Tooltip . container }
ref = { setTooltipRef }
id = { tooltipId }
role = "tooltip"
{ . . . getTooltipProps ( { className : style.container } ) }
>
< div { ...getArrowProps ( { className : style.arrow } ) } / >