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 {Button} from "@qualcomm-ui/react/button"
import {Popover} from "@qualcomm-ui/react/popover"
import {Portal} from "@qualcomm-ui/react-core/portal"
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:
| Prop | Type | Default |
|---|---|---|
Props applied to the anchor component. | PopoverAnchorProps | |
Props applied to the arrow component. | ||
Props applied to the content component. | ||
Optional description text for the popover. | ||
Props applied to the description component. | ||
Whether to hide the arrow. | boolean | false |
Optional label text for the popover. | ||
Props applied to the label component. | ||
Props applied to the portal component. | { | |
Props applied to the positioner component. |
PopoverAnchorPropsboolean{
container?:
| HTMLElement
| RefObject<HTMLElement>
disabled?: boolean
}
Composite API
<Popover.Root>
| Prop | Type | Default |
|---|---|---|
Whether to automatically set focus on the first focusable
content within the popover when opened. | boolean | true |
Whether to close the popover when the escape key is pressed. | boolean | true |
Whether to close the popover when the user clicks outside the popover. | boolean | true |
The initial open state of the popover when rendered.
Use when you don't need to control the open state of the popover. | boolean | |
The document's text/writing direction. | 'ltr' | 'rtl' | 'ltr' |
The style variant of the popover. | | 'neutral' | 'neutral' |
A root node to correctly resolve document in custom environments. i.e.,
Iframes, Electron. | () => | |
The ids of the popover elements. These will be automatically generated if
omitted. | Partial<{ | |
Whether to synchronize the present change immediately or defer it to the next
frame | boolean | |
The element to focus on when the popover is opened. | () => HTMLElement | |
When true, the component will not be rendered in the DOM until it becomes
visible or active. | boolean | false |
Whether the popover should be modal. When set to true:- 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 | boolean | false |
Function called when the escape key is pressed | ( | |
Function called when the animation ends in the closed state | VoidFunction | |
Function called when the focus is moved outside the component | ( | |
Function called when an interaction happens outside the component | ( | |
Function invoked when the popover opens or closes
| ( | |
Function called when the pointer is pressed down outside the component | ( | |
Function called when this layer is closed due to a parent layer being closed | ( | |
The controlled open state of the popover | boolean | |
Returns the persistent elements that: - should not have pointer-events disabled - should not trigger the dismiss event | Array< | |
Whether the popover is portalled. This will proxy the tabbing behavior
regardless of the DOM position of the popover content. | boolean | true |
The options used to position the popover content. | ||
Whether the node is present (controlled by the user) | boolean | |
On close, restore focus to the element that triggered the open event. | boolean | true |
Whether to allow the initial presence animation. | boolean | false |
When true, the component will be completely removed from the DOM when it
becomes inactive or hidden, rather than just being hidden with CSS. | boolean | false |
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
booleanbooleanbooleanbooleanboolean| Attribute / Property | Value |
|---|---|
data-popover-part | 'root' |
data-popover-part'root'This section describes the elements of the Popover's composite API.
<Popover.Trigger>
| Prop | Type |
|---|---|
React children Render Prop | |
Optional id attribute. | string |
string| Attribute / Property | Value |
|---|---|
data-placement | | 'bottom' |
data-popover-part | 'trigger' |
data-state | | 'open' |
data-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>
| Prop | Type |
|---|---|
string | |
Allows you to replace the component's HTML element with a different tag or component. Learn more | | ReactElement |
string| ReactElement
| ((
props: object,
) => ReactElement)
| Attribute / Property | Value |
|---|---|
data-popover-part | 'positioner' |
style |
data-popover-part'positioner'style<Popover.Content>
| Prop | Type |
|---|---|
id attribute. If
omitted, a unique identifier will be automatically generated for
accessibility. | string |
Allows you to replace the component's HTML element with a different tag or component. Learn more | | ReactElement |
string| ReactElement
| ((
props: object,
) => ReactElement)
| Attribute / Property | Value |
|---|---|
className | 'qui-popover__content' |
data-emphasis | | 'neutral' |
data-expanded | |
data-placement | | 'bottom' |
data-popover-part | 'content' |
data-state | | 'open' |
hidden | boolean |
tabIndex | -1 |
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>
| Prop | Type |
|---|---|
id attribute. If
omitted, a unique identifier will be automatically generated for accessibility. | string |
Allows you to replace the component's HTML element with a different tag or component. Learn more | | ReactElement |
string| ReactElement
| ((
props: object,
) => ReactElement)
| Attribute / Property | Value |
|---|---|
className | 'qui-popover__arrow' |
data-emphasis | | 'neutral' |
data-popover-part | 'arrow' |
style |
className'qui-popover__arrow'data-emphasis| 'neutral'
| 'brand'
data-popover-part'arrow'style<Popover.Label>
| Prop | Type |
|---|---|
id attribute. If
omitted, a unique identifier will be automatically generated for accessibility. | string |
Allows you to replace the component's HTML element with a different tag or component. Learn more | | ReactElement |
string| ReactElement
| ((
props: object,
) => ReactElement)
| Attribute / Property | Value |
|---|---|
data-popover-part | 'label' |
data-popover-part'label'<Popover.Description>
| Prop | Type |
|---|---|
id attribute. If
omitted, a unique identifier will be automatically generated for
accessibility. | string |
Allows you to replace the component's HTML element with a different tag or component. Learn more | | ReactElement |
string| ReactElement
| ((
props: object,
) => ReactElement)
| Attribute / Property | Value |
|---|---|
data-popover-part | 'description' |
data-popover-part'description'Data Structures
PopoverPositioningOptions
| Prop | Type | Default |
|---|---|---|
The minimum padding between the arrow and the floating element's corner. | number | 4 |
CSS selector used to locate the arrow element within the floating element.
Components override this with their own anatomy-namespaced selector
(e.g. [data-menu-part=arrow]). | string | |
The overflow boundary of the reference element | () => | |
Whether the popover should fit the viewport. | boolean | |
Whether to flip the placement when the floating element overflows the boundary. | | boolean | true |
Function that returns the anchor rect | ( | |
The main axis offset or gap between the reference and floating element | number | 8 |
Whether the popover should be hidden when the reference element is detached | boolean | |
Options to activate auto-update listeners | | boolean | true |
The offset of the floating element | { | |
Function called when the placement is computed | ( | |
Function called when the floating element is positioned or not | (data: { | |
The virtual padding around the viewport edges to check for overflow | number | |
Whether the floating element can overlap the reference element | boolean | false |
The initial placement of the floating element | | 'bottom' | 'top' |
Whether to make the floating element same width as the reference element | boolean | |
The secondary axis offset or gap between the reference and floating elements | number | |
Whether the popover should slide when it overflows. | boolean | |
The strategy to use for positioning | | 'absolute' | 'absolute' |
A callback that will be called when the popover needs to calculate its
position. | (data: { |
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>