Popup
A popup displays brief content in an overlay.Motion in Early Access
Default
This is the simplest form of a popup. The popup opens from a trigger element.
import { useState } from 'react';
import { jsx } from '@compiled/react';
import Button from '@atlaskit/button/default/button';
import { cssMap } from '@atlaskit/css';
import { Popup } from '@atlaskit/popup/popup';
import { Box } from '@atlaskit/primitives/compiled/box';
import { token } from '@atlaskit/tokens';
const contentStyles = cssMap({
root: {
paddingInlineStart: token('space.200'),
paddingInlineEnd: token('space.200'),
paddingBlockStart: token('space.200'),
paddingBlockEnd: token('space.200'),
},
});
const PopupDefaultExample = (): JSX.Element => {
const [isOpen, setIsOpen] = useState(false);
return (
<Popup
shouldRenderToParent
isOpen={isOpen}
onClose={() => setIsOpen(false)}
placement="bottom-start"
content={() => <Box xcss={contentStyles.root}>Content</Box>}
trigger={(triggerProps) => (
<Button
{...triggerProps}
appearance="primary"
isSelected={isOpen}
onClick={() => setIsOpen(!isOpen)}
>
{isOpen ? 'Close' : 'Open'} popup{' '}
</Button>
)}
/>
);
};
export default PopupDefaultExample;Placement
Use the placement prop to set a preferred position (auto, top, right, left, or bottom).
The popup will move automatically if it's near the edge of the screen.
Using the auto placement will place the popup on the side with the most space available.
import { useState } from 'react';
import { css, jsx } from '@compiled/react';
import Button from '@atlaskit/button/default/button';
import { cssMap } from '@atlaskit/css';
import { type Placement, placements } from '@atlaskit/popper/main';
import { Popup } from '@atlaskit/popup/popup';
import { Box } from '@atlaskit/primitives/compiled/box';
import { token } from '@atlaskit/tokens';
const placementGridPositions = cssMap({
'top-start': {
gridColumn: 2,
gridRow: 1,
},
top: {
gridColumn: 3,
gridRow: 1,
},
'top-end': {
gridColumn: 4,
gridRow: 1,
},
'bottom-start': {
gridColumn: 2,
gridRow: 5,
},
bottom: {
gridColumn: 3,
gridRow: 5,
},
'bottom-end': {
gridColumn: 4,
gridRow: 5,
},
'right-start': {
gridColumn: 5,
gridRow: 2,
},
right: {
gridColumn: 5,
gridRow: 3,
},
'right-end': {
gridColumn: 5,
gridRow: 4,
},
'left-start': {
gridColumn: 1,
gridRow: 2,
},
left: {
gridColumn: 1,
gridRow: 3,
},
'left-end': {
gridColumn: 1,
gridRow: 4,
},
'auto-start': {
gridColumn: 3,
gridRow: 2,
},
auto: {
gridColumn: 3,
gridRow: 3,
},
'auto-end': {
gridColumn: 3,
gridRow: 4,
},
});
const contentStyles = css({
maxWidth: '220px',
paddingBlockEnd: token('space.200'),
paddingBlockStart: token('space.200'),
paddingInlineEnd: token('space.200'),
paddingInlineStart: token('space.200'),
});
const buttonGridStyles = css({
display: 'grid',
gap: token('space.100'),
gridTemplate: 'repeat(5, 1fr) / repeat(5, 1fr)',
justifyItems: 'stretch',
});
const PopupPlacementExample = (): JSX.Element => {
const [openPlacement, setOpenPlacement] = useState<Placement | null>(null);
return (
<div css={buttonGridStyles}>
{placements.map((placement) => {
const isOpen = openPlacement === placement;
return (
<Popup
shouldRenderToParent
key={placement}
placement={placement}
isOpen={isOpen}
onClose={() => {
setOpenPlacement((value) => (value === placement ? null : value));
}}
content={() => (
<div css={contentStyles}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut aliquam massa ac risus
scelerisque, in iaculis magna semper. Phasellus sagittis congue elit, non suscipit
nulla rhoncus vitae.
</div>
)}
trigger={(triggerProps) => (
<Box xcss={placementGridPositions[placement]}>
<Button
{...triggerProps}
shouldFitContainer
isSelected={isOpen}
onClick={() => setOpenPlacement(isOpen ? null : placement)}
>
{placement}
</Button>
</Box>
)}
/>
);
})}
</div>
);
};
export default PopupPlacementExample;Multiple
You can use popups multiple times on the same page.
import { type FC, useState } from 'react';
import { jsx } from '@compiled/react';
import ButtonGroup from '@atlaskit/button/button-group';
import Button from '@atlaskit/button/default/button';
import { cssMap } from '@atlaskit/css';
import { Popup } from '@atlaskit/popup/popup';
import { Box } from '@atlaskit/primitives/compiled/box';
import { token } from '@atlaskit/tokens';
const contentStyles = cssMap({
root: {
paddingInlineStart: token('space.200'),
paddingInlineEnd: token('space.200'),
paddingBlockStart: token('space.200'),
paddingBlockEnd: token('space.200'),
},
});
type PopupExampleProps = {
index: number;
};
const PopupExample: FC<PopupExampleProps> = ({ index }) => {
const [isOpen, setIsOpen] = useState(false);
return (
<Popup
shouldRenderToParent
isOpen={isOpen}
onClose={() => setIsOpen(false)}
content={() => <Box xcss={contentStyles.root}>Content</Box>}
trigger={(triggerProps) => (
<Button {...triggerProps} isSelected={isOpen} onClick={() => setIsOpen(!isOpen)}>
{isOpen ? 'Close' : 'Open'} popup {index + 1}
</Button>
)}
placement="bottom-start"
/>
);
};
const PopupMultipleExample = (): JSX.Element => (
<ButtonGroup label="Open required popup">
{Array.from(Array(3)).map((_, index) => (
<PopupExample index={index} />
))}
</ButtonGroup>
);
export default PopupMultipleExample;Nested
Nesting popups inside other popups works out of the box. However, be mindful that nested popups quickly become confusing, inaccessible, and difficult to navigate. If you’re nesting a lot of content, it’s time to move your experience to a new page instead.
This example has additional code to prevent the parent popup closing when interacting with the child popup.
import { useState } from 'react';
import { jsx } from '@compiled/react';
import Button from '@atlaskit/button/default/button';
import { cssMap } from '@atlaskit/css';
import ArrowRight from '@atlaskit/icon/core/arrow-right';
import MenuIcon from '@atlaskit/icon/core/menu';
import ButtonItem from '@atlaskit/menu/button-item';
import Section from '@atlaskit/menu/section';
import { Popup } from '@atlaskit/popup/popup';
import { Box } from '@atlaskit/primitives/compiled/box';
import { Stack } from '@atlaskit/primitives/compiled/stack';
const nestedPopupStyles = cssMap({
root: {
maxWidth: '800px',
minWidth: '320px',
},
});
const NestedPopup = () => {
const [isOpen, setIsOpen] = useState(false);
return (
<Box onClick={(e: React.MouseEvent) => e.stopPropagation()}>
<Stack xcss={nestedPopupStyles.root}>
<Section title="Projects">
<ButtonItem>Create project</ButtonItem>
<ButtonItem>View all projects</ButtonItem>
</Section>
<Section hasSeparator>
<Popup
isOpen={isOpen}
placement="right-start"
shouldRenderToParent
onClose={() => setIsOpen(false)}
content={() => <NestedPopup />}
trigger={(triggerProps) => (
<ButtonItem
{...triggerProps}
isSelected={isOpen}
onClick={() => setIsOpen(true)}
iconAfter={<ArrowRight label="" />}
>
More actions
</ButtonItem>
)}
/>
</Section>
</Stack>
</Box>
);
};
const PopupNestedExample = (): JSX.Element => {
const [isOpen, setIsOpen] = useState(false);
return (
<Popup
isOpen={isOpen}
onClose={() => setIsOpen(false)}
content={() => <NestedPopup />}
placement="bottom-start"
shouldRenderToParent
trigger={(triggerProps) => (
<Button
{...triggerProps}
isSelected={isOpen}
iconBefore={MenuIcon}
onClick={() => setIsOpen(!isOpen)}
>
Menu
</Button>
)}
/>
);
};
export default PopupNestedExample;Content updates
When changing the content inside the popup, call the update() function to reposition the popup.
Make sure to call this in the same callback or update step to ensure everything happens in the same animation frame.
import { useEffect, useState } from 'react';
import { jsx } from '@compiled/react';
import Button from '@atlaskit/button/default/button';
import { cssMap } from '@atlaskit/css';
import noop from '@atlaskit/ds-lib/noop';
import Heading from '@atlaskit/heading/heading';
import { Popup } from '@atlaskit/popup/popup';
import { Box } from '@atlaskit/primitives/compiled/box';
import { Stack } from '@atlaskit/primitives/compiled/stack';
import { Text } from '@atlaskit/primitives/compiled/text';
import Toggle from '@atlaskit/toggle/toggle';
import { token } from '@atlaskit/tokens';
import { data } from './data';
const wrapperStyles = cssMap({
root: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
},
});
const contentStyles = cssMap({
root: {
maxWidth: '350px',
paddingInlineStart: token('space.200'),
paddingInlineEnd: token('space.200'),
paddingBlockStart: token('space.200'),
paddingBlockEnd: token('space.200'),
textAlign: 'center',
},
});
const Values = ({ onUpdate }: { onUpdate: () => void }) => {
const [textIndex, setTextIndex] = useState(0);
useEffect(() => {
const intervalId = setInterval(() => {
setTextIndex((prevIndex) => (prevIndex + 1) % data.length);
onUpdate();
}, 15000);
return () => clearInterval(intervalId);
}, [onUpdate]);
return (
<Box xcss={contentStyles.root} aria-live="assertive" aria-atomic="true">
<Stack space="space.100">
<Heading size="large">{data[textIndex].title}</Heading>
<Box as="blockquote">
<Text>{data[textIndex].description}</Text>
</Box>
</Stack>
</Box>
);
};
const PopupContentUpdateExample = (): JSX.Element => {
const [isOpen, setIsOpen] = useState(false);
const [isUpdateOn, setIsUpdateOn] = useState(true);
return (
<Box xcss={wrapperStyles.root}>
<Popup
shouldRenderToParent
isOpen={isOpen}
onClose={() => setIsOpen(false)}
content={(props) => <Values onUpdate={isUpdateOn ? props.update : noop} />}
placement="right"
trigger={(triggerProps) => (
<Button
{...triggerProps}
appearance="primary"
isSelected={isOpen}
onClick={() => setIsOpen(!isOpen)}
>
Open popup
</Button>
)}
/>
<Box>
<Text>Updates {isUpdateOn ? 'on' : 'off'}</Text>
<Toggle
size="large"
label="Updates toggle switch controls"
isChecked={isUpdateOn}
onChange={(e) => setIsUpdateOn(e.currentTarget.checked)}
/>
</Box>
</Box>
);
};
export default PopupContentUpdateExample;Customization
You can customize the popup using a component override with the popupComponent prop.
Make sure to forward the ref to the underlying DOM node, (opens new window), or the positioning of the popup won't work correctly.
import { forwardRef, useState } from 'react';
import { css, jsx } from '@compiled/react';
import IconButton from '@atlaskit/button/icon/button';
import ShowMoreHorizontalIcon from '@atlaskit/icon/core/show-more-horizontal';
import { Popup } from '@atlaskit/popup/popup';
import type { PopupComponentProps } from '@atlaskit/popup/types';
import { Box } from '@atlaskit/primitives/compiled/box';
import { token } from '@atlaskit/tokens';
const containerStyles = css({
zIndex: 400,
backgroundColor: token('color.background.brand.bold'),
borderRadius: token('radius.small'),
color: token('color.text.inverse'),
paddingBlockEnd: token('space.200'),
paddingBlockStart: token('space.200'),
paddingInlineEnd: token('space.200'),
paddingInlineStart: token('space.200'),
});
const CustomPopupContainer: React.ForwardRefExoticComponent<
React.PropsWithoutRef<PopupComponentProps> & React.RefAttributes<HTMLDivElement>
> = forwardRef<HTMLDivElement, PopupComponentProps>(
({ children, 'data-testid': testId, xcss: _xcss, ...props }, ref) => (
<div
css={containerStyles}
test-id={testId}
{...props}
className={props.className}
ref={ref}
>
{children}
</div>
),
);
const PopupCustomExample = (): JSX.Element => {
const [isOpen, setIsOpen] = useState(false);
return (
<Popup
shouldRenderToParent
isOpen={isOpen}
testId="popup-custom-example"
onClose={() => setIsOpen(false)}
placement="bottom-start"
popupComponent={CustomPopupContainer}
content={() => <Box>Customized popup</Box>}
trigger={(triggerProps) => (
<IconButton
{...triggerProps}
isSelected={isOpen}
onClick={() => setIsOpen(!isOpen)}
icon={ShowMoreHorizontalIcon}
label="More"
/>
)}
/>
);
};
export default PopupCustomExample;Focus
Set focus
You can focus a specific element when opening a dialog popup. When role is "dialog" or
"alertdialog", the content render props provide a ref setInitialFocusRef to be set on the
desired element. Until platform-dst-top-layer is cleaned up, a role gated on that flag
(fg('platform-dst-top-layer') ? 'dialog' : undefined) also gets it. Other roles do not get it: a
menu or listbox has a fixed focus rule, and a popup with no role must not take focus. Give the
dialog a label or a titleId. Only set focus to an element at the start of the popup, otherwise
people who use assistive technology may miss the content.
The specified element needs to be available in the document as soon as the popup is opened, otherwise it will not be focused upon. This means it will not work for content that renders into the popup content after it has already opened, such as async or lazy loaded content. In those cases, you will need to manually set focus on the content once it is rendered.
Select a button to focus
import { Fragment, useState } from 'react';
import { jsx } from '@compiled/react';
import ButtonGroup from '@atlaskit/button/button-group';
import Button from '@atlaskit/button/default/button';
import { cssMap } from '@atlaskit/css';
import { Popup } from '@atlaskit/popup/popup';
import { Box } from '@atlaskit/primitives/compiled/box';
import RadioGroup from '@atlaskit/radio/radio-group';
import { token } from '@atlaskit/tokens';
const radioValues = [
{ name: 'None', value: '0', label: 'None' },
{ name: 'Button 1', value: '1', label: 'Button 1' },
{ name: 'Button 2', value: '2', label: 'Button 2' },
{ name: 'Button 3', value: '3', label: 'Button 3' },
];
const contentStyles = cssMap({
root: {
paddingBlockEnd: token('space.200'),
paddingBlockStart: token('space.200'),
paddingInlineEnd: token('space.200'),
paddingInlineStart: token('space.200'),
},
});
const PopupFocusExample = (): JSX.Element => {
const [isOpen, setIsOpen] = useState(false);
const [buttonToFocus, setButtonToFocus] = useState('0');
return (
<Fragment>
<p>
<strong>Select a button to focus</strong>
</p>
<RadioGroup
onChange={({ currentTarget: { value } }) => setButtonToFocus(value)}
defaultValue={radioValues[0].value}
options={radioValues}
/>
<Popup
shouldRenderToParent
isOpen={isOpen}
onClose={() => setIsOpen(false)}
content={({ setInitialFocusRef }) => {
return (
<Box xcss={contentStyles.root}>
<ButtonGroup label="Show selected focus">
{radioValues.map(
({ value, label }) =>
value !== '0' && (
<Button
key={value}
ref={value === buttonToFocus ? setInitialFocusRef : undefined}
>
{label}
</Button>
),
)}
</ButtonGroup>
</Box>
);
}}
trigger={(triggerProps) => (
<Button
{...triggerProps}
appearance="primary"
isSelected={isOpen}
onClick={() => setIsOpen(!isOpen)}
>
{isOpen ? 'Close' : 'Open'} popup
</Button>
)}
placement="bottom-start"
role="dialog"
label="Choose the initial focus"
/>
</Fragment>
);
};
export default PopupFocusExample;Disable autofocus
If an element is focused, setting the autofocus prop to false will ensure it keeps focus when
the popup is opened.
With the platform-dst-top-layer feature flag on, autoFocus has no effect and focus follows
role. The dialog, alertdialog, menu and listbox roles move focus into the popup when it
opens. To keep focus where it is, do not use one of these roles. One exception: if a focused
combobox or text field controls the popup, focus stays on that field.
import { useState } from 'react';
import { jsx } from '@compiled/react';
import Button from '@atlaskit/button/default/button';
import { cssMap } from '@atlaskit/css';
import { Popup } from '@atlaskit/popup/popup';
import { Box } from '@atlaskit/primitives/compiled/box';
import Textfield from '@atlaskit/textfield/text-field';
import { token } from '@atlaskit/tokens';
const wrapperStyles = cssMap({
root: {
display: 'grid',
alignItems: 'center',
gap: token('space.200'),
gridTemplateColumns: '1fr auto',
},
});
const contentStyles = cssMap({
root: {
maxWidth: '200px',
paddingInlineStart: token('space.200'),
paddingInlineEnd: token('space.200'),
paddingBlockStart: token('space.200'),
paddingBlockEnd: token('space.200'),
},
});
const PopupDisableAutofocusExample = (): JSX.Element => {
const [isOpen, setIsOpen] = useState(false);
return (
<Box xcss={wrapperStyles.root}>
{/* eslint-disable-next-line @atlaskit/design-system/no-placeholder */}
<Textfield placeholder="This should stay focused when the popup opens" />
<Popup
shouldRenderToParent
autoFocus={false}
isOpen={isOpen}
onClose={() => setIsOpen(false)}
content={() => (
<Box xcss={contentStyles.root}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut aliquam massa ac risus
scelerisque, in iaculis magna semper. Phasellus sagittis congue elit, non suscipit nulla
rhoncus vitae.
</Box>
)}
trigger={(triggerProps) => (
<Button
{...triggerProps}
appearance="primary"
isSelected={isOpen}
onClick={() => setIsOpen(!isOpen)}
>
{isOpen ? 'Close' : 'Open'} popup
</Button>
)}
placement="bottom"
/>
</Box>
);
};
export default PopupDisableAutofocusExample;Surface detection
The current surface CSS variable is set to the
surface color of the popup. You can use the utility.elevation.surface.current design token to
style children with the current surface color.
import { useState } from 'react';
import { css, jsx } from '@compiled/react';
import Button from '@atlaskit/button/default/button';
import { Popup } from '@atlaskit/popup/popup';
import { Flex } from '@atlaskit/primitives/compiled/flex';
import { token } from '@atlaskit/tokens';
const contentStyles = css({
maxWidth: '220px',
backgroundColor: token('utility.elevation.surface.current'),
paddingBlockEnd: token('space.200'),
paddingBlockStart: token('space.200'),
paddingInlineEnd: token('space.200'),
paddingInlineStart: token('space.200'),
});
const SurfaceAwareBox = () => {
return (
<div css={contentStyles}>
A surface aware box - the background color depends on the surface it's placed on.
</div>
);
};
const PopupSurfaceDetectionExample = (): JSX.Element => {
const [isOpen, setIsOpen] = useState(false);
return (
<Flex gap="space.200">
<Popup
shouldRenderToParent
isOpen={isOpen}
onClose={() => setIsOpen(false)}
placement="bottom-start"
content={SurfaceAwareBox}
trigger={(triggerProps) => (
<Button
{...triggerProps}
appearance="primary"
isSelected={isOpen}
onClick={() => setIsOpen(!isOpen)}
>
{isOpen ? 'Close' : 'Open'} popup
</Button>
)}
/>
<SurfaceAwareBox />
</Flex>
);
};
export default PopupSurfaceDetectionExample;Content wrapping
By default, the popup content is rendered inside React.Portal. Use shouldRenderToParent prop to
render the content directly after the trigger element.
import { useState } from 'react';
import { jsx } from '@compiled/react';
import Button from '@atlaskit/button/default/button';
import { cssMap } from '@atlaskit/css';
import { Popup } from '@atlaskit/popup/popup';
import { Box } from '@atlaskit/primitives/compiled/box';
import { token } from '@atlaskit/tokens';
const contentStyles = cssMap({
root: {
paddingInlineStart: token('space.200'),
paddingInlineEnd: token('space.200'),
paddingBlockStart: token('space.200'),
paddingBlockEnd: token('space.200'),
},
});
const PopupContentWithoutPortalExample = (): JSX.Element => {
const [isOpen, setIsOpen] = useState(false);
return (
<Popup
isOpen={isOpen}
onClose={() => setIsOpen(false)}
placement="bottom-start"
content={() => <Box xcss={contentStyles.root}>Content</Box>}
shouldRenderToParent
trigger={(triggerProps) => (
<Button
{...triggerProps}
appearance="primary"
isSelected={isOpen}
onClick={() => setIsOpen(!isOpen)}
>
{isOpen ? 'Close' : 'Open'} popup{' '}
</Button>
)}
/>
);
};
export default PopupContentWithoutPortalExample;Role
Use the role prop to set a role for the popup content. We do not forbid passing any of the aria
roles, but we strongly recommend using only menu or dialog. When the role="dialog" property is
passed, one of the following properties must also be added: label or titleId.
import { type KeyboardEvent, useEffect, useRef, useState } from 'react';
import { jsx } from '@compiled/react';
import Button from '@atlaskit/button/default/button';
import { cssMap } from '@atlaskit/css';
import { Popup } from '@atlaskit/popup/popup';
import { Box } from '@atlaskit/primitives/compiled/box';
import { Stack } from '@atlaskit/primitives/compiled/stack';
import { token } from '@atlaskit/tokens';
const contentStyles = cssMap({
root: {
paddingInlineStart: token('space.200'),
paddingInlineEnd: token('space.200'),
paddingBlockStart: token('space.200'),
paddingBlockEnd: token('space.200'),
},
});
const PopupWithRoleExample = (): JSX.Element => {
const [isDialogOpen, setIsDialogOpen] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false);
const menuItemRefs = useRef<Array<HTMLButtonElement | null>>([]);
const menuTriggerRef = useRef<HTMLButtonElement | null>(null);
const closeMenu = () => {
setIsMenuOpen(false);
menuTriggerRef.current?.focus();
};
useEffect(() => {
if (isMenuOpen) {
menuItemRefs.current[0]?.focus();
}
}, [isMenuOpen]);
const handleMenuItemKeyDown = (index: number) => (event: KeyboardEvent<HTMLButtonElement>) => {
const menuItemCount = menuItemRefs.current.length;
let nextIndex: number | undefined;
switch (event.key) {
case 'ArrowDown':
nextIndex = (index + 1) % menuItemCount;
break;
case 'ArrowUp':
nextIndex = (index - 1 + menuItemCount) % menuItemCount;
break;
case 'Home':
nextIndex = 0;
break;
case 'End':
nextIndex = menuItemCount - 1;
break;
}
if (nextIndex !== undefined) {
event.preventDefault();
menuItemRefs.current[nextIndex]?.focus();
}
};
return (
<Stack alignInline="start" space="space.200">
<Popup
shouldRenderToParent
isOpen={isDialogOpen}
onClose={() => setIsDialogOpen(false)}
placement="bottom-start"
content={() => <Box xcss={contentStyles.root}>Content</Box>}
role="dialog"
label="Popup with role dialog example"
trigger={(triggerProps) => (
<Button
{...triggerProps}
appearance="primary"
isSelected={isDialogOpen}
onClick={() => setIsDialogOpen(!isDialogOpen)}
>
{isDialogOpen ? 'Close' : 'Open'} dialog popup
</Button>
)}
/>
<Popup
// Prefer DropdownMenu for menus. Use Popup only for more complex interactions,
// and ensure it meets applicable accessibility guidelines and expected menu behavior to prevent regressions.
autoFocus={false}
shouldRenderToParent
isOpen={isMenuOpen}
onClose={(event) => {
if (
event?.type === 'keydown' &&
'key' in event &&
(event.key === 'Escape' || event.key === 'Esc')
) {
menuTriggerRef.current?.focus();
}
setIsMenuOpen(false);
}}
placement="bottom-start"
content={() => (
<Stack alignInline="start" space="space.0">
{['Edit', 'Delete'].map((item, index) => (
<Button
key={item}
ref={(element) => {
menuItemRefs.current[index] = element;
}}
onClick={closeMenu}
onKeyDown={handleMenuItemKeyDown(index)}
role="menuitem"
>
{item}
</Button>
))}
</Stack>
)}
role="menu"
label="Popup with role menu example"
trigger={(triggerProps) => (
<Button
{...triggerProps}
appearance="primary"
isSelected={isMenuOpen}
onClick={(event) => {
menuTriggerRef.current = event.currentTarget;
setIsMenuOpen(!isMenuOpen);
}}
>
{isMenuOpen ? 'Close' : 'Open'} menu popup
</Button>
)}
/>
</Stack>
);
};
export default PopupWithRoleExample;Use DropdownMenu whenever possible: this example is not a blessed, production-ready menu pattern.
If a complex interaction requires a Popup with role="menu", the consumer must implement the menu
behavior. Give each action the appropriate menu-item role, move focus to a menu item when the menu
opens, support Arrow Up, Arrow Down, Home, and End navigation, and return focus to the trigger when
an action or Escape closes the menu. Preserve each menu item's native Enter and Space activation
behavior. Validate the complete interaction with the assistive technologies and browser environments
that the product supports. For the complete role and interaction requirements, refer to the
WAI-ARIA menu role, (opens new window) and the
ARIA Authoring Practices menu and menubar pattern, (opens new window).
Full width
Use shouldFitContainer to fit the popup width to its parent's width. When set to true, the
trigger and popup elements will be wrapped in a div with position: relative. The popup will be
rendered as a sibling to the trigger element, and will be full width.
import { useState } from 'react';
import { jsx } from '@compiled/react';
import Button from '@atlaskit/button/default/button';
import { cssMap } from '@atlaskit/css';
import { Popup } from '@atlaskit/popup/popup';
import { Box } from '@atlaskit/primitives/compiled/box';
import { token } from '@atlaskit/tokens';
const contentStyles = cssMap({
root: {
paddingInlineStart: token('space.200'),
paddingInlineEnd: token('space.200'),
paddingBlockStart: token('space.200'),
paddingBlockEnd: token('space.200'),
},
});
const PopupFullWidth = (): JSX.Element => {
const [isOpen, setIsOpen] = useState(false);
return (
<Popup
shouldRenderToParent
content={() => <Box xcss={contentStyles.root}>Content</Box>}
isOpen={isOpen}
onClose={() => setIsOpen(false)}
placement="bottom-start"
shouldFitContainer
trigger={(triggerProps) => (
<Button
{...triggerProps}
appearance="primary"
isSelected={isOpen}
onClick={() => setIsOpen(!isOpen)}
shouldFitContainer
>
{isOpen ? 'Close' : 'Open'} popup{' '}
</Button>
)}
/>
);
};
export default PopupFullWidth;