Components

Popout

Popout is a floating content block triggered via a click on an actionable element.

Popout is primarily used to house additional content and actions without cluttering the surface of a workflow.

It's important to note that the Popout component is not a replacement for more specific components like Tooltip and Menu. Always use a more purpose-built component before turning to Popout, which is intended to be a low-level utility that can be used to build more complex components with popout-like behaviors.

If you need to display a popout on hover or focus, please use the Tooltip component instead.

import { Popout } from '@sproutsocial/racine'
<Box display="flex" alignItems="center" justifyContent="center">
<Popout
aria-haspopup='dialog'
content={
<Popout.Content tabIndex={0}>Hello, I am the popped-out content.</Popout.Content>
}
>
<Button appearance="secondary">Click me</Button>
</Popout>
</Box>

Properties

NameTypeDefaultDescriptionRequired?
isOpenboolean
Is the popout open? This prop is optional. By using it, you will lose some automatic handling of the logic for opening and closing the popout.
setIsOpen(isOpen: boolean, event?: MouseEvent | KeyboardEvent) => void
If you use the isOpen prop you will need to pass a handler to change the state of the popout
content
React.Element<any>
(opts: { hide: () => void }) => React.Element<any>
The content to be shown in the popout. If there is no content, just the children are rendered
children
React.Element<any>
(opts: { ref: (React.ElementRef<any> | HTMLElement) => void, toggle: () => void, show: () => void, hide: () => void, ariaProps: Object, }) => React.Node
The content that the popout should be attached to
placement
'auto'
'top'
'right'
'bottom'
'left'
'top-start'
'right-start'
'bottom-start'
'left-start'
'top-end'
'right-end'
'bottom-end'
'left-end'
'auto'
How the popped-out content should be placed, in relationship to the children
fullWidthbooleanfalse
Should the popped-out content inherit its width from the children? This is useful for typeaheads and other places where a popout needs to match the width of a given element.
focusOnContentbooleantrue
When the popout has opened, should we focus on its content? Focus will be brought inside by looking for elements with [autofocus] first, [tabindex] second and buttons/links/other natively-focusable elements last. Upon closing the popout, focus will be returned to the popout's target. If nothing within your popout's content is focusable, this prop will do nothing.
qa{ [string]: string }{}
zIndexnumber7
popperProps
PopperProps
Override react-popper per the API documentation here: https://github.com/FezVrasta/react-popper#api-documentation
onClose() => void
onOpen() => void
scheduleUpdateRef(() => void) => void() => {}
And optional callback to receive the scheduleUpdate function. Use the function to recalculate the popout position in relation to the contents.
appendToBodybooleantrue
Mount the popout at the bottom of the DOM (using React.Portal) instead of inside the current DOM tree
focusLockProps{| disabled?: boolean, returnFocus?: boolean | {| preventScroll?: boolean |}, persistentFocus?: boolean, autoFocus?: boolean, noFocusGuards?: boolean | 'tail', group?: string, className?: string, onActivation?: (node: HTMLElement) => void, onDeactivation?: (node: HTMLElement) => void, as?: string | React.Element<any>, lockProps?: any, ref?: any, whiteList?: (activeElement: HTMLElement) => boolean, shards?: Array<any>, children?: React.Node, |}{}
Override `FocusLock` props, see the API documentation for more details: https://github.com/theKashey/react-focus-lock#api

Subcomponents

Popout Content

Wrap your Popout's content in Popout.Content to get the correct styling.

<Popout.Content>
This is how the contents of a popout should look.
</Popout.Content>

Recipes

There is a very broad range of applications for the Popout component, and different teams may need to manage a Popout's state differently. Because of this, we offer multiple ways to handle opening and closing a Popout.

The "Magic" click handler

When using this recipe, you just need to provide a single clickable element as the child of Popout. All the logic for handling click events will be applied automatically. Additionally, all necessary aria attributes for a Popout will be applied automatically.

For accessibility reasons, we strongly recommend using a Button for your clickable element. However, if you're unable to do so, aria-role="button" will be applied to your element. When using this method, your Popout will close upon esc keypresses and clicks outside the popped-out content.

<Box display="flex" alignItems="center" justifyContent="center">
<Popout
aria-haspopup='dialog'
content={
<Popout.Content tabIndex={0}>Hello!</Popout.Content>
}
>
<Button appearance="secondary">
Show popout
</Button>
</Popout>
</Box>

Function as child

When you need more control over your Popout's logic, you can use a function as the child of Popout. You may need this if you want to bind to different DOM events or otherwise deviate from the standard Popout behavior.

When using this method, your function is called with this signature:

({ ref, toggle, show, hide, ariaProps }) => <Element />

When compared to “magic” pattern, you'll have to manage more of the Popout logic yourself. Specifically, you need to assign the ref from the function signature to the element you want the popped out content to attach to. You'll also have to assign aria attributes to your click target, and bind any events you want manually.

The callback provides an ariaProps parameter which can be spread onto the element that triggers the display of the popout. The popout will still close automatically on esc or click-away.

<Box display="flex" alignItems="center" justifyContent="center">
<Popout
aria-haspopup='dialog'
content={<Popout.Content tabIndex={0}>Hello!</Popout.Content>}
>
{({ ref, ariaProps, toggle }) => (
<Button
appearance="secondary"
innerRef={ref}
onClick={toggle}
{...ariaProps}
>
Show popout
</Button>
)}
</Popout>
</Box>

Fully controlled

In some cases, you'll want full external control of the Popout. This may be because you're managing the Popout's state in Redux, or migrating from a legacy Popout where it's not practical to change the control logic.

To control the open state of a Popout, pass in two props: isOpen and setIsOpen. isOpen dictates the open state of the Popout. setIsOpen is a callback fired when state changes inside the Popout

() => {
const [isOpen, setIsOpen] = useState(false);
return (
<Box display="flex" alignItems="center" justifyContent="center">
<Popout
aria-haspopup='dialog'
content={<Popout.Content tabIndex={0}>Hello!</Popout.Content>}
isOpen={isOpen}
setIsOpen={setIsOpen}
>
<Button
appearance="secondary"
onClick={() => setIsOpen(!isOpen)}
>
Show popout
</Button>
</Popout>
</Box>
);
};

Closing the Popout from within the Popout's content

If you need to be able to close the popout from within the popped-out content, you can pass a function as the content prop.

When using this method, your function is called with this signature:

({ hide }) => <Element />

<Box display="flex" alignItems="center" justifyContent="center">
<Popout
aria-haspopup='dialog'
content={({ hide }) => (
<Popout.Content tabIndex={0} maxWidth='20em'>
<Text as='div' mb={350} fontSize={300}>
If you need to be able to close the popout from within the popped-out content, you can pass a function as the `content` prop.
</Text>
<Button appearance="secondary" onClick={hide} width={1}>
Close
</Button>
</Popout.Content>
)}
>
<Button appearance="secondary">Show popout</Button>
</Popout>
</Box>

Resources

Popout uses the following libraries for core functionality: