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"><Popoutaria-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
Name | Type | Default | Description | Required? |
---|---|---|---|---|
isOpen | boolean | 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 | |
fullWidth | boolean | false | 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. | |
focusOnContent | boolean | true | 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
} | {} | ||
zIndex | number | 7 | ||
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. | |
appendToBody | boolean | true | 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"><Popoutaria-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"><Popoutaria-haspopup='dialog'content={<Popout.Content tabIndex={0}>Hello!</Popout.Content>}>{({ ref, ariaProps, toggle }) => (<Buttonappearance="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"><Popoutaria-haspopup='dialog'content={<Popout.Content tabIndex={0}>Hello!</Popout.Content>}isOpen={isOpen}setIsOpen={setIsOpen}><Buttonappearance="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"><Popoutaria-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: