A Popover
component is used to display additional content in a small floating window when a specific element clicked or given focus.
import { Popover } from "@vitality-ds/components";
<Popover triggerElement={<Button appearance="primary">Open Popover</Button>} content={ <Stack> <Typography variant="sectionTitle">Section Title</Typography> <Typography variant="body">Body</Typography> </Stack> } />
There are two ways to activate the Popover component, and it can be used in either a controlled or uncontrolled manner.
If you pass a trigger element using the triggerElement
prop, the state of the Popover component will be automatically managed. However, please note that this method requires the trigger element to be an HTML button element that can receive a ref
.
<Popover triggerElement={ <IconButton icon={<AdminNote />} tooltipContent="Open Popover" /> } content={ <Stack> <Typography variant="sectionTitle">Section Title</Typography> <Typography variant="body">Body</Typography> </Stack> } />
When you pass a boolean value to the isOpen
prop, the internal state management of the Popover component will be disabled. This means that the responsibility of opening and closing the popover now falls on the user.
() => { const [isOpen, setIsOpen] = React.useState(false); return ( <> <Popover isOpen={isOpen} triggerElement={ <Button appearance="primary" onClick={() => setIsOpen(!isOpen)}> Toggle Popover </Button> } content={ <Stack> <Typography variant="sectionTitle">Section Title</Typography> <Typography variant="body">Body</Typography> </Stack> } /> </> ); };
Although all popovers are "collision aware", you can still indicate the preference for which side of its trigger it appears, and how to align itself around that side. The collision detection safeguards against popover elements disappearing outside of the viewport and will opt for the opposide side
value. For instance, side="top"
will switch to side="bottom"
if a popover cannot fit above its trigger. These positions will also update as the user scrolls or resizes the window.
When deciding which side or alignment to use, consider what would make the most sense to a user based on the contents of the Popover. For example, displaying search results would likely go to the bottom
side so that the results are shown under the search term, or a piece of additional information (like in an informational popover) may make more sense to the right
side. If you're unsure, you can leave the default.
() => { const [interactedOutside, setInteractedOutside] = React.useState(""); const [side, setSide] = React.useState("top"); const [align, setAlign] = React.useState("start"); return ( <DocsBox css={{ width: "100%", padding: "$xl" }}> <Stack direction="horizontal" align="center" justify="between"> <Stack direction="horizontal"> <FormField type="radioButtons" id="side" label="Side" onChange={(newValue) => setSide(newValue)} value={side} inputProps={{ name: "side", options: [ { label: "left", id: "left", value: "left" }, { label: "right", id: "right", value: "right" }, { label: "top", id: "top", value: "top" }, { label: "bottom", id: "bottom", value: "bottom" }, ], }} /> <FormField type="radioButtons" label="Align" id="align" onChange={(newValue) => setAlign(newValue)} value={align} inputProps={{ name: "align", options: [ { label: "start", id: "start", value: "start" }, { label: "center", id: "center", value: "center" }, { label: "end", id: "end", value: "end" }, ], }} /> </Stack> <Popover isOpen onInteractOutside={(event) => event.preventDefault()} triggerElement={<Button>Trigger element</Button>} side={side} align={align} content={ <Typography variant="body"> I'm on top unless I won't fit! </Typography> } /> </Stack> </DocsBox> ); };
Setting the Popover's align
property will determine the alignment of the popover to its side
. For instance, to display a popover underneath a search field, you'd want the popover placed on the bottom
side of the trigger (the text input) and aligned to the start
(ie. left).
() => { const [interactedOutside, setInteractedOutside] = React.useState(""); return ( <Popover isOpen onInteractOutside={(event) => event.preventDefault()} triggerElement={<TextInput placeholder="Search for a patient..." />} side="bottom" align="start" content={ <Typography variant="body" color="lowContrast"> Search results appear here </Typography> } /> ); };
By default, when used in an uncontrolled manner, the Popover component will automatically close when the user interacts with elements outside of its content (e.g., clicks outside the popover). You can customize this behaviour using the onInteractOutside
prop, you are able to access the event through this function.
() => { const [interactedOutside, setInteractedOutside] = React.useState(""); return ( <Popover onInteractOutside={(event) => event.preventDefault()} triggerElement={<Button appearance="primary">Toggle Popover</Button>} content={ <Stack> <Typography variant="sectionTitle">Section Title</Typography> <Typography variant="body">Body</Typography> </Stack> } /> ); };
The onInteractOutside
prop also enables you to replicate the default behaviour of the Popover component when used in a controlled state, where the popover automatically closes upon outside interaction.
() => { const [isOpen, setIsOpen] = React.useState(false); return ( <> <Popover isOpen={isOpen} onInteractOutside={() => setIsOpen(false)} triggerElement={ <Button appearance="primary" onClick={() => setIsOpen(true)}> Open Popover </Button> } content={ <Stack> <Typography variant="sectionTitle">Section Title</Typography> <Typography variant="body">Body</Typography> </Stack> } /> </> ); };
Description
The preferred alignment against the anchor. May change when collisions occur
Type
"center" | "end" | "start"
Default Value
"center"
Description
The component that pops out when the popover is open.
Type
ReactNode
Description
The controlled open state of the popover. false is closed, true is open.
Type
boolean
Description
The function run when focus exits the popover's container
Type
(event: PointerDownOutsideEvent | FocusOutsideEvent) => void
Description
The preferred side of the anchor to render against when open. Will be reversed when collisions occur.
Type
"bottom" | "left" | "right" | "top"
Default Value
"bottom"
Description
The element that toggles the popover when clicked
Type
ReactNode