Popovers float over page content and hold supporting information or user controls.
Examples #
Import Syntax #
import Popover from 'mineral-ui/Popover';
Basic Usage #
Popovers wrap the triggering component. Placement is relative to the location of the trigger. Popovers will change position relative to the trigger automatically depending on viewport constraints.
<Popover content={<DemoContent />}> <Button>Open Popover</Button> </Popover>
Title and Subtitle #
Formatted titles render above any other content if provided.
Subtitles are only be rendered if the title
attribute is present.
Title
Subtitle
<DemoLayout> <Popover content={<DemoContent />} placement="right" subtitle="Subtitle" title="Title" isOpen> <Button>Open Popover</Button> </Popover> </DemoLayout>
Placement #
The placement
prop determines the initial placement of the Popover content relative to the trigger.
The Popover will still react to viewport edges and scrolling.
<DemoLayout> <Popover content={<DemoContent />} placement="bottom" isOpen> <Button>Open Popover</Button> </Popover> </DemoLayout>
Overflow #
A Popover can extend beyond its bounding container (the blue area in this example) even if the container has an overflow: hidden
style. See the portal example for even greater control.
<OverflowContainer> <Popover content={<DemoContent />} placement="right" isOpen> <Button>Open Popover</Button> </Popover> </OverflowContainer>
Scrolling Container #
Popover will attempt to stay visible in an overflow: scroll
container.
<ScrollParent> <Popover content={<DemoContent />} placement="left" isOpen> <Button>Open Popover</Button> </Popover> </ScrollParent>
Portal #
Use a portal to render the Popover to the body of the page rather than as a sibling of the trigger. This can be useful to visually "break out" of a container with overflow
or z-index
styles. Note that you may have to adjust the modifiers
to get the exact behavior that you want.
<ScrollParent> <Popover content={<DemoContent />} placement="bottom" usePortal isOpen modifiers={{ preventOverflow: { escapeWithReference: true } }}> <Button>Open Popover</Button> </Popover> </ScrollParent>
Disabled #
Disable the Popover and its trigger.
<Popover content={<DemoContent />} disabled> <Button>Open Popover</Button> </Popover>
Controlled #
Popover controls its own state by default,
and can optionally be managed by the application as a controlled component through the isOpen
attribute.
Callbacks for onOpen
and onClose
are also provided.
class App extends Component { constructor(props) { super(props); this.state = { isOpen: false }; this.onOpen = this.onOpen.bind(this); this.onClose = this.onClose.bind(this); this.togglePopover = this.togglePopover.bind(this); } onOpen(event) { this.setState({ isOpen: true }); } onClose(event) { // Prevent extra call to togglePopover when clicking the controlling button. // Also avoid interactions with other popovers. const demoLayoutNode = this.demoLayout; if ( !event.nativeEvent && demoLayoutNode && demoLayoutNode.contains(event.target) ) { event.stopImmediatePropagation(); } this.setState({ isOpen: false }); } togglePopover(event) { if (this.state.isOpen) { this.onClose(event); } else { this.onOpen(event); } } render() { const label = this.state.isOpen ? 'Close Popover' : 'Open Popover'; return ( <DemoLayout ref={node => { this.demoLayout = node }}> <Popover content={<DemoContent />} isOpen={this.state.isOpen} onOpen={this.onOpen} onClose={this.onClose}> <Button>{label}</Button> </Popover> <Button onClick={this.togglePopover}>{label}</Button> </DemoLayout> ); } }
Bidirectionality #
Popovers support right-to-left (RTL) languages. The placement of
the content as well as the content itself will be reversed when the
direction
theme variable is set to rtl
.
<DemoLayout dir="rtl"> <ThemeProvider theme={{ direction: 'rtl' }}> <Popover content={<DemoContent />} isOpen placement="bottom-start"> <Button>Open Popover</Button> </Popover> </ThemeProvider> </DemoLayout>
Custom Content #
Use the content
render prop to provide custom rendering
control of the content. See our Render Props Guide for additional information, including important considerations and examples.
() => { const content = ({ props }) => { // Your root element must be a Popper component. // import { Popper } from 'react-popper'; const Content = styled('div')(({ theme }) => ({ ...componentStyleReset(theme), backgroundColor: theme.backgroundColor_dangerPrimary, color: theme.color_white, margin: theme.space_inset_sm, padding: theme.space_inset_lg, zIndex: theme.zIndex_100 })); return ( <Popper placement={props.placement}> {(popperProps) => { const contentProps = { ...props, ...popperProps }; return ( <Content {...contentProps} > <DemoContent /> </Content> ); }} </Popper> ); }; return ( <DemoLayout> <Popover content={content} isOpen placement="bottom-start"> <Button>Open Popover</Button> </Popover> </DemoLayout> ); }
Custom Trigger #
Use the children
render prop to provide custom rendering
control of the trigger. See our Render Props Guide for additional information, including important considerations and examples.
() => { // Your render function must return a Popper Reference component. // import { Reference } from 'react-popper'; const CustomTrigger = styled('button')(); return ( <Popover content={<DemoContent />}> { ({ props, state }) => { return ( <Reference> {({ ref: popperRef }) => { const triggerProps = { ...props, ref: (node) => { popperRef(node); props.ref(node); }, role: undefined } return ( <CustomTrigger {...triggerProps}> Popover <span role="img" aria-hidden="true">{state.isOpen ? '▲' : '▼'}</span> </CustomTrigger> ); }} </Reference> ); } } </Popover> ); }
API & Theme #
Popover Props #
The Popover component takes the following React props.
Name | Type | Default | Description |
---|---|---|---|
children | React$Node | RenderFn | required | Trigger for the Popover. Optionally provides custom rendering control. See the custom trigger example and our render props guide. |
content | React$Node | RenderFn | required | Content of the Popover. Optionally provides custom rendering control. See the custom content example and our render props guide. |
defaultIsOpen | boolean | Open the Popover upon initialization. Primarily for use with uncontrolled components. | |
disabled | boolean | Disables the Popover | |
focusTriggerOnClose | boolean | true | Determines whether focus will be set to the trigger when the Popover is closed. |
hasArrow | boolean | true | Include an arrow on the Popover content pointing to the trigger |
id | string | Id of the Popover | |
isOpen | boolean | Determines whether the Popover is open. For use with controlled components. | |
modifiers | Object | Plugins that are used to alter behavior. See PopperJS docs for options. | |
onClose | Called when Popover is closed | ||
onOpen | Called when Popover is opened | ||
placement | 'bottom' | Placement of the Popover | |
positionFixed | boolean | Apply fixed positioning to the content | |
subtitle | React$Node | Subtitle displayed under the title | |
title | React$Node | Title of the Popover | |
usePortal | boolean | Use a Portal to render the Popover to the body rather than as a sibling to the trigger |
Popover Theme Variables #
These variables can be used as hooks to override this component's
style at either a
local
or global
level. The theme
referenced below is whatever theme is available
from props to the instance of this component.
Variable | Value |
---|---|
PopoverArrow_backgroundColor | theme.panel_backgroundColor |
PopoverArrow_borderColor | theme.panel_borderColor |
PopoverContent_backgroundColor | theme.panel_backgroundColor |
PopoverContent_borderColor | theme.panel_borderColor |
PopoverContent_borderRadius | theme.borderRadius_1 |
PopoverContent_boxShadow | theme.boxShadow_2 |
PopoverContent_color | theme.color |
PopoverContent_paddingVertical | theme.space_inset_sm |
PopoverContent_maxWidth | none |
PopoverContent_zIndex | theme.zIndex_100 |
PopoverContentBlock_marginVertical | theme.space_stack_sm |
PopoverContentBlock_paddingHorizontal | theme.space_inset_md |
Usage #
When/How to Use #
Popovers contain content providing clarification for other interface elements. Since popovers are usually triggered with deliberate user actions, they can contain more complex information than a tooltip which appears upon hovering/focusing an element.
- Keep content short. Don't put so much content that the tooltip requires a scrollbar.
- Popovers can be used to implement other custom behaviors or widgets such as color pickers, or other task-specific custom inputs.
- Popovers could be chained together in application logic to create walkthroughs for onboarding.
Best Practices #
Use Popover to provide extra context for selected interface elements.
Mineral UI
Don't place long or extra-wide content in a Popover. If more
context is needed, provide a link to another resource with more detail. If you
must display wide content, add a max-width
value to constrain the layout.