Popover
Popover displays contextual information or actions in a floating container that appears relative to a trigger element. Unlike dialogs, popovers are non-modal and allow users to continue interacting with the background content.
import {Popover} from "@qualcomm-ui/react/popover"Examples
Simple
Basic popover using the simple API with a label and child content.
<Popover
label="Label"
trigger={<Button emphasis="primary">Show Popover</Button>}
>
Popover content
</Popover>
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.
<Popover.Root>
<Popover.Anchor>
<Popover.Trigger>
<Button emphasis="primary">Show Popover</Button>
</Popover.Trigger>
</Popover.Anchor>
<Portal>
<Popover.Positioner>
<Popover.Content>
<Popover.Arrow />
<Popover.Label>Label</Popover.Label>
<Popover.Description>Description</Popover.Description>
</Popover.Content>
</Popover.Positioner>
</Portal>
</Popover.Root>
Emphasis
Use emphasis to control the visual style of the popover. Two emphasis options are available: neutral (default) and brand.
import type {ReactElement} from "react"
import type {QdsPopoverEmphasis} from "@qualcomm-ui/qds-core/popover"
import {Portal} from "@qualcomm-ui/react-core/portal"
import {Button} from "@qualcomm-ui/react/button"
import {Popover} from "@qualcomm-ui/react/popover"
const emphasisOptions: QdsPopoverEmphasis[] = ["neutral", "brand"]
export function PopoverEmphasisDemo(): ReactElement {
return (
<div className="flex gap-4">
{emphasisOptions.map((emphasis) => (
<Popover.Root key={emphasis} emphasis={emphasis}>
<Popover.Anchor>
<Popover.Trigger>
<Button emphasis="primary">{emphasis}</Button>
</Popover.Trigger>
</Popover.Anchor>
<Portal>
<Popover.Positioner>
<Popover.Content>
<Popover.Arrow />
<Popover.Label>Label</Popover.Label>
<Popover.Description>
This is a {emphasis} popover.
</Popover.Description>
</Popover.Content>
</Popover.Positioner>
</Portal>
</Popover.Root>
))}
</div>
)
}Accessibility
- The anchor element is automatically associated with the popover:
- When the popover is visible, the anchor element receives the
aria-controlsandaria-expandedattributes. - The anchor element's
aria-haspopupis always set to'true'. - When the
Popover.Labelcomponent is supplied, the overlay panel'saria-labelledbyattribute is set to the id of the label. - When the
Popover.Descriptioncomponent is supplied, the overlay panel'saria-describedbyattribute is set to the id of the label.
NOTE
Don't worry about supplying id's to the label, description or popover. If these aren't provided, they'll be generated automatically.
Explorer
Component Anatomy
Hover to highlight, click to view API
API
<Popover>
The Popover extends the Popover.Root with the following props:
PopoverAnchorPropsboolean{
container?:
| HTMLElement
| RefObject<HTMLElement>
disabled?: boolean
}
Composite API
<Popover.Root>
booleanbooleanbooleanboolean'ltr' | 'rtl'
| 'neutral'
| 'brand'
() =>
| Node
| ShadowRoot
| Document
Partial<{
anchor: string
arrow: string
closeTrigger: string
content: string
description: string
positioner: string
title: string
trigger: string
}>
boolean() => HTMLElement
booleanbooleantrue:- interaction with outside elements will be disabled
- only popover content will be visible to screen readers
- scrolling is blocked
- focus is trapped within the popover
(
event: KeyboardEvent,
) => void
VoidFunction(
event: FocusOutsideEvent,
) => void
(
event: InteractOutsideEvent,
) => void
(
open: boolean,
) => void
- open:The next value of the open state.
(
event: PointerDownOutsideEvent,
) => void
(
event: CustomEvent<{
originalIndex: number
originalLayer: HTMLElement
targetIndex: number
targetLayer: HTMLElement
}>,
) => void
booleanArray<
() => Element
>
- should not have pointer-events disabled
- should not trigger the dismiss event
booleanbooleanbooleanbooleanbooleandata-popover-part'root'This section describes the elements of the Popover's composite API.
<Popover.Trigger>
stringdata-placement| 'bottom'
| 'bottom-end'
| 'bottom-start'
| 'left'
| 'left-end'
| 'left-start'
| 'right'
| 'right-end'
| 'right-start'
| 'top'
| 'top-end'
| 'top-start'
data-popover-part'trigger'data-state| 'open'
| 'closed'
<Popover.Positioner>
string| ReactElement
| ((
props: object,
) => ReactElement)
data-popover-part'positioner'style<Popover.Content>
string| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-popover__content'data-emphasis| 'neutral'
| 'brand'
data-expandeddata-placement| 'bottom'
| 'bottom-end'
| 'bottom-start'
| 'left'
| 'left-end'
| 'left-start'
| 'right'
| 'right-end'
| 'right-start'
| 'top'
| 'top-end'
| 'top-start'
data-popover-part'content'data-state| 'open'
| 'closed'
hiddenbooleantabIndex-1
<Popover.Arrow>
string| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-popover__arrow'data-emphasis| 'neutral'
| 'brand'
data-popover-part'arrow'style<Popover.Label>
string| ReactElement
| ((
props: object,
) => ReactElement)
data-popover-part'label'<Popover.Description>
string| ReactElement
| ((
props: object,
) => ReactElement)
data-popover-part'description'Data Structures
PopoverPositioningOptions
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>