Popover
Status: stableAccessibility reviewedtenet-ui@0.4.0
import { Popover } from 'tenet-ui';Source: src/components/Popover/Popover.tsx
Usage guidelines
Section titled “Usage guidelines”When to use: a lightweight floating container anchored to a control — a filter panel, a small form, rich help, an account summary. It’s non-modal: the page behind stays interactive. For a blocking task or confirmation (focus trap, scroll lock, dimmed background) use Dialog; for a plain text hint use Tooltip; for a list of actions use Menu; for picking a value use Select.
- Pass a focusable
trigger(aButton). It’s cloned and wired witharia-haspopup="dialog",aria-expanded,aria-controls, an onClick toggle, and a ref so focus returns to it on close. A non-focusable trigger (a plain<div>) strands keyboard users. - Give an
aria-label— it names therole="dialog"surface (it falls back to “Popover”, which isn’t descriptive). - Focus + keyboard are handled: on open focus moves into the surface (first focusable child, else the container); Escape closes and returns focus to the trigger; click-outside closes; and focus leaving the wrapper (Tab-out) closes it so it can’t strand open behind the page.
- It is non-modal (
aria-modal="false"): no focus trap, no scroll lock, no backgroundaria-hidden. If you need any of those, you wantDialog, not a Popover with hacks bolted on. - Choose
placementto keep the surface on-screen near the trigger (bottom-startdefault, plusbottom-end,top-start,top-end). Keep the content short — a popover is not a page.
Do / Don’t
Section titled “Do / Don’t”- ✅
<Popover aria-label="Filters" trigger={<Button>Filters ▾</Button>}><FilterForm /></Popover> - ❌
<Popover trigger={<div>Open</div>}>…</Popover>— a non-focusable trigger; keyboard users can’t reach it. - ❌ Reaching for a Popover for a destructive confirm that must block the flow — that’s a
Dialog. - ❌ Omitting
aria-label— the dialog is then named only “Popover”.
Examples
Section titled “Examples”2 stories for Popover — open in Storybook
The full matrix (all args, controls, accessibility panel) is in Storybook.
| Prop | Type | Default | Description |
|---|---|---|---|
trigger * | ReactElement | — | The trigger element (e.g. a Button). It is cloned and wired with aria-haspopup="dialog", aria-expanded, aria-controls, an onClick toggle, and a ref so focus can be restored to it on close. |
children * | ReactNode | — | The floating content rendered inside the popover surface. |
placement | 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end'PopoverPlacement | bottom-start | Where the surface is anchored relative to the trigger. |
aria-label | string | Popover | Accessible name for the popover dialog (falls back to "Popover"). |
className | string | — |
* required