Tooltip
Tooltips appear on demand to explain UI elements, clarify functionality, or provide helpful context without permanently occupying screen space. They automatically position themselves relative to their trigger element.
import {Tooltip} from "@qualcomm-ui/react/tooltip"Overview
The tooltip should be used to provide additional non-essential information. It is linked to an interactive element (the trigger) and will be shown when this element is focused or hovered. Because the tooltip itself can't be focused or tabbed to, it should not contain interactive elements. Only one tooltip can be open at a time.
In general, tooltips should be used sparingly and only contain succinct information. Here are guidelines to help you decide when to use one:
- When using controls that lack visual labels, like icon buttons.
- When defining a term or inline item.
- When additional information may help a user make decisions.
- When providing more context to an element.
Examples
Simple
The simple API provides a standalone component with built-in layout. The trigger is supplied as a prop, and the content of the tooltip is the children.
<Tooltip trigger={<Button emphasis="primary">Hover me</Button>}>
Hello world!
</Tooltip>
Composite
Build with the composite API for granular control. This API requires you to provide each subcomponent, but gives you full control over the structure and layout.
<Tooltip.Root>
<Tooltip.Trigger>
<Button emphasis="primary">Hover me</Button>
</Tooltip.Trigger>
<Portal>
<Tooltip.Positioner>
<Tooltip.Content>
<Tooltip.Arrow>
<Tooltip.ArrowTip />
</Tooltip.Arrow>
Hello world!
</Tooltip.Content>
</Tooltip.Positioner>
</Portal>
</Tooltip.Root>
Placement
The tooltip's position, relative to the trigger element, can be set using the placement option of the positioning prop. Its default value is top.
Change the values in the example below and hover over the select to see the tooltip position change.
import {type ReactElement, useState} from "react"
import {selectCollection} from "@qualcomm-ui/core/select"
import type {Placement} from "@qualcomm-ui/dom/floating-ui"
import {Select} from "@qualcomm-ui/react/select"
import {Tooltip} from "@qualcomm-ui/react/tooltip"
const positions = selectCollection({
items: [
"top-start",
"top",
"top-end",
"right-start",
"right",
"right-end",
"bottom-start",
"bottom",
"bottom-end",
"left-start",
"left",
"left-end",
],
})
export function TooltipPlacementDemo(): ReactElement {
const [placement, setPlacement] = useState<Placement>("top")
return (
<div className="w-48">
<Tooltip
positioning={{placement}}
trigger={
<Select
aria-label="Select a position"
collection={positions}
onValueChange={(value) => setPlacement(value[0] as Placement)}
positioning={{sameWidth: true}}
value={[placement]}
/>
}
>
{placement}
</Tooltip>
</div>
)
}Close On Click / Escape
By default, the tooltip will close when the Escape key is pressed or a click is detected. You can customize this behavior by using the closeOnClick and closeOnEscape props.
<Tooltip
closeOnClick={false}
closeOnEscape={false}
trigger={<Button emphasis="primary">Hover me</Button>}
>
Hello world!
</Tooltip>
Controlled Visibility
Use open and onOpenChange to control the component's visibility manually. These props follow our controlled state pattern.
import {type ReactElement, useState} from "react"
import {Button} from "@qualcomm-ui/react/button"
import {Tooltip} from "@qualcomm-ui/react/tooltip"
export function TooltipControlledStateDemo(): ReactElement {
const [open, setOpen] = useState(false)
return (
<div className="flex w-40 flex-col items-center gap-4">
<Tooltip
onOpenChange={setOpen}
open={open}
trigger={<Button emphasis="primary">Hover me</Button>}
>
Hello world!
</Tooltip>
<output className="font-body-sm text-neutral-primary flex">
The tooltip is {open ? "visible" : "hidden"}
</output>
</div>
)
}Disabled
You can disable the tooltip by using the disabled prop.
import {type ReactElement, useState} from "react"
import {Button} from "@qualcomm-ui/react/button"
import {Tooltip} from "@qualcomm-ui/react/tooltip"
export function TooltipDisabledDemo(): ReactElement {
const [disabled, setDisabled] = useState(true)
return (
<div className="flex flex-col items-center gap-4">
<Tooltip
disabled={disabled}
trigger={<Button emphasis="primary">Hover me</Button>}
>
Hello world!
</Tooltip>
<Button onClick={() => setDisabled(!disabled)} variant="outline">
{disabled ? "Enable" : "Disable"} tooltip
</Button>
</div>
)
}Shortcuts
Shortcuts are enabled for common use cases.
Arrow
If you don't wish to customize the arrow tip, you can omit it and Tooltip.Arrow will render the default one. At that, <Tooltip.Arrow /> is equivalent to:
<Tooltip.Arrow>
<Tooltip.ArrowTip />
</Tooltip.Arrow>API
<Tooltip>
The simple tooltip extends the Tooltip.Root component with the following props:
booleanPortalPropsComposite API
<Tooltip.Root>
booleanboolean'ltr' | 'rtl'
booleantrue, the component's trigger
element will not activate the tooltip.(
open: boolean,
) => void
boolean<Tooltip.Trigger>
stringdata-expandeddata-state| 'open'
| 'closed'
data-tooltip-part'trigger'<Tooltip.Positioner>
<div> element by default.string| ReactElement
| ((
props: object,
) => ReactElement)
<Tooltip.Content>
<div> element by default.string| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-tooltip__content'data-placement| 'bottom'
| 'bottom-end'
| 'bottom-start'
| 'left'
| 'left-end'
| 'left-start'
| 'right'
| 'right-end'
| 'right-start'
| 'top'
| 'top-end'
| 'top-start'
data-state| 'open'
| 'closed'
data-tooltip-part'content'hiddenboolean<Tooltip.Arrow>
<Tooltip.ArrowTip/> by default if no children are provided.| ReactElement
| ((
props: object,
) => ReactElement)
<Tooltip.ArrowTip>
<div> element by default.| ReactElement
| ((
props: object,
) => ReactElement)
TooltipPositioningOptions
numberstring[data-menu-part=arrow]).() =>
| 'clippingAncestors'
| Element
| Array<Element>
| {
height: number
width: number
x: number
y: number
}
boolean| boolean
| Array<
| 'bottom'
| 'bottom-end'
| 'bottom-start'
| 'left'
| 'left-end'
| 'left-start'
| 'right'
| 'right-end'
| 'right-start'
| 'top'
| 'top-end'
| 'top-start'
>
(
element:
| HTMLElement
| VirtualElement,
) => {
height?: number
width?: number
x?: number
y?: number
}
numberboolean| boolean
| {
ancestorResize?: boolean
ancestorScroll?: boolean
animationFrame?: boolean
elementResize?: boolean
layoutShift?: boolean
}
{
crossAxis?: number
mainAxis?: number
}
(
data: ComputePositionReturn,
) => void
(data: {
placed: boolean
}) => void
numberboolean| 'bottom'
| 'bottom-end'
| 'bottom-start'
| 'left'
| 'left-end'
| 'left-start'
| 'right'
| 'right-end'
| 'right-start'
| 'top'
| 'top-end'
| 'top-start'
booleannumberboolean| 'absolute'
| 'fixed'
(data: {
updatePosition: () => Promise<void>
}) => void | Promise<void>