npm i @accessible/popover
An accessible, "batteries included", popover component for React.
- Several placement options You can render the popover anywhere! Top, top-left, bottom, center, inside, outside, literally anywhere!
- Containment policies The popover is configured to contain itself inside the window using a containment policy. It's also optional, so you can turn it off.
- Auto-repositioning Use the props
repositionOnScroll
orrepositionOnResize
to reposition the popover automatically when the scroll position or size of the window changes. - Style-agnostic You can use this component with the styling library of your choice. It
works with CSS-in-JS, SASS, plain CSS, plain
style
objects, anything! - Portal-friendly The popover will render into React portals of your choice when configured to do so.
- a11y/aria-compliant This component works with screen readers out of the box and manages focus for you.
Check out the example on CodeSandbox
import {Popover, Target, Trigger} from '@accessible/popover'
const Component = () => (
<Popover repositionOnScroll repositionOnResize>
<Target placement="bottomLeft">
<div className="my-popover">Hello world</div>
</Target>
<Trigger on="hover">
<a href="/profile/me">
<img src="avatar.jpg" />
</a>
</Trigger>
</Popover>
)
Component | Description |
---|---|
<Popover> |
This component creates the context for your popover target and trigger and contains some configuration options. |
<Target> |
This component wraps any React element and turns it into a popover target. |
<Trigger> |
This component wraps any React element and turns it into a popover trigger. |
<Close> |
This is a convenience component that wraps any React element and adds an onClick handler to close the popover. |
Hook | Description |
---|---|
usePopover() |
This hook provides the value of the popover's PopoverContextValue object. |
useControls() |
This hook provides access to the popover's open , close , toggle , and reposition functions. |
usePlacement() |
This hook provides access to the popover's rendered placement. |
useIsOpen() |
This hook provides access to the popover's isOpen value. |
This component creates the context for your popover target and trigger and contains some configuration options.
Prop | Type | Default | Required? | Description |
---|---|---|---|---|
defaultOpen | boolean |
false |
No | This sets the default open state of the popover. By default the popover is closed. |
open | boolean |
undefined |
No | You can control the open/closed state of the popover with this prop. When it isn't undefined, this value will take precedence over any calls to open() , close() , or toggle() . |
onChange | (isOpen: boolean) => void |
undefined |
No | This callback will be invoked each time the open state changes. |
repositionOnResize | boolean |
false |
No | Setting this to true will update the position of the popover when the window's dimensions change and the popover is currently open. |
repositionOnScroll | boolean |
false |
No | Setting this to true will update the position of the popover when the window's scroll position changes and the popover is currently open. |
containPolicy | ContainPolicy |
"flip" |
No | This tells the popover what to do when it overflows outside the dimensions of the window. By default it will flip its position on both the x and y axis to attempt to remain within the bounds of the window. See ContainPolicy for more options. |
id | string |
undefined |
No | By default this component creates a unique id for you, as it is required for certain aria attributes. Supplying an id here overrides the auto id feature. |
Policy | Description |
---|---|
"flip" |
This will attempt to flip its position on both the x and y axis to attempt to remain within the bounds of the window. |
"flipX" |
This will attempt to flip its position on only the x axis to attempt to remain within the bounds of the window. |
"flipY" |
This will attempt to flip its position on only the y axis to attempt to remain within the bounds of the window. |
function |
You can decide what to do with the popover on your own by providing a callback with the signature (placement: string, triggerRect: ClientRect, popoverRect: ClientRect) => Placement | PlacementResult where Placement is a string returning an alternative placement and PlacementResult is an object shaped {placement: Placement, style: CSSProperties} |
This component wraps any React element and turns it into a popover target.
Prop | Type | Default | Required? | Description |
---|---|---|---|---|
placement | Placement |
"bottom" |
No | This tells the target where it should render relative to its triggering element. |
portal | boolean | string |
false |
No | When true this will render the popover into a React portal with the id #portals . You can render it into any portal by providing its query selector here, e.g. #foobar , [data-portal=true] , or .foobar . |
closeOnEscape | boolean |
true |
No | By default the popover will close when the Escape key is pressed. You can turn this off by providing false here. |
closedClass | string |
undefined |
No | This class name will be applied to the child element when the popover is closed . |
openClass | string |
"popover--open" |
No | This class name will be applied to the child element when the popover is open . |
closedStyle | React.CSSProperties |
undefined |
No | These styles will be applied to the child element when the popover is closed in addition to the default styles that set the target's visibility. |
openStyle | React.CSSProperties |
undefined |
No | These styles name will be applied to the child element when the popover is open in addition to the default styles that set the target's visibility. |
children | React.ReactElement |
undefined |
Yes | The child is cloned by this component and has aria attributes injected into its props as well as the events defined above. |
These are the default placements allowed by the popover relative to its triggering element
<Target placement="innerTopLeft">
<div className="menu">Menu</div>
</Target>
// <div
// class="menu"
// aria-hidden="true"
// aria-modal="false"
// id="popover--foobar"
// role="dialog"
// style="position: fixed; visibility: hidden; right: 1024px; top: 0px;"
// >
// Menu
// </div>
This component wraps any React element and turns it into a popover trigger.
Prop | Type | Default | Required? | Description |
---|---|---|---|---|
on | "hover" | "click" | "focus" |
undefined |
Yes | "hover" causes the popover to open on mouseenter and close on mouseleave . "click" causes the popover to toggle its visibility each click event. "focus" causes the popover to open when the child element is focused while nothing happens on blur. |
closedClass | string |
undefined |
No | This class name will be applied to the child element when the popover is closed . |
openClass | string |
undefined |
No | This class name will be applied to the child element when the popover is open . |
closedStyle | React.CSSProperties |
undefined |
No | These styles will be applied to the child element when the popover is closed . |
openStyle | React.CSSProperties |
undefined |
No | These styles name will be applied to the child element when the popover is open . |
children | React.ReactElement |
undefined |
Yes | The child is cloned by this component and has aria attributes injected into its props as well as the events defined above. |
<Trigger on="click">
<button className="my-button">Popover me!</button>
</Trigger>
// <button
// class="my-button"
// aria-controls="popover--12"
// aria-haspopup="dialog"
// aria-expanded="false"
// >
// Popover me!
// </button>
This is a convenience component that wraps any React element and adds an onClick handler to close the popover.
Prop | Type | Default | Required? | Description |
---|---|---|---|---|
children | React.ReactElement |
undefined |
Yes | The child is cloned by this component and has aria attributes injected into its props as well as the events defined above. |
<Close>
<button className="my-button">Close me</button>
</Close>
// <button
// class="my-button"
// aria-controls="popover--12"
// aria-haspopup="dialog"
// aria-expanded="false"
// >
// Close me
// </button>
This hook provides the value of the popover's PopoverContextValue object
const Component = () => {
const {open, close, toggle, isOpen} = usePopover()
return <button onClick={toggle}>Toggle the popover</button>
}
interface PopoverContextValue {
// `true` when the popover is open and visible
// `false` when closed
isOpen: boolean
// opens the popover
open: () => void
// closes the popover
close: () => void
// toggles the popover between open/closed states
toggle: () => void
// calling this forces the popover to reposition
// itself to the specified placement
reposition: (nextPlacement: Placement) => void
// the ID of the popover target
id: string
// the style applied to the popover target
style: React.CSSProperties
// the rendered placement of the popover
placement: Placement
// sets the ref for the popover target
targetRef: React.MutableRefObject<HTMLElement | null>
// sets the ref for the triggering element
triggerRef: React.MutableRefObject<HTMLElement | null>
// this describes the events that cause the popover
// to open
triggeredBy: string | null
// sets the `triggeredBy` variable above
setTriggeredBy: (trigger: string) => void
}
This hook provides access to the popover's rendered placement
const Component = () => {
const placement = usePlacement()
return (
<Target placement="top">
<div className="my-popover">
<span className={`arrow--${placement}`} />
</div>
</Target>
)
}
This hook provides access to the popover's open
, close
, toggle
, and reposition
functions
const Component = () => {
const {open, close, toggle} = useControls()
return (
<Target>
<div className="my-popover">
<button onClick={close}>Close me</button>
</div>
</Target>
)
}
This hook provides access to the popover's isOpen
value
const Component = () => {
const isOpen = useIsOpen()
return (
<Target>
<div className="my-popover">Am I open? {isOpen ? 'Yes' : 'No'}</div>
</Target>
)
}
MIT