Usage
import { Dropdown } from '@loomhq/lens'
<Dropdown
triggerCallback={buttonProps => <div tabIndex="0" {...buttonProps}>Simple trigger</div>}
options={[
{
title: 'Share',
icon: <SvgShare />,
onClick: () => (),
disabled: false,
},
]}
/>
Screen readers should accurately 'read' the text contents of the custom node you return from the callback you pass to trigger
as the label for the Dropdown element. However, you can explicitly pass a label string to ariaMenuName
to explicitly label the element in case screen readers are not behaving as expected.
<Arrange gap="large">
<Dropdown
ariaMenuName="Button with label override"
triggerCallback={buttonProps => {
return (
<Arrange tabIndex="0" {...buttonProps}>
<Text fontWeight="bold">This label is invisible to screen readers</Text>
<Icon icon={<SvgChevronSmallDown />} />
</Arrange>
)
}}
options={[
{ title: 'Share' },
{ title: 'Archive' },
{ title: 'Disabled', disabled: true },
]}
/>
</Arrange>
Trigger
triggerCallback
A callback function that returns a custom node. The Dropdown component will pass the necessary ARIA attributes to allow screen readers to properly 'read' the custom node returned by your callback.
Providing this prop will override any custom node passed to trigger
.
trigger (deprecated - use triggerCallback instead)
Trigger can be any custom node. If the trigger
is not a button make sure to add a tabindex attribute to it.
Note on deprecation: This approach is not a11y compliant, as screen readers cannot properly 'read' the dropdown options. Using a screen reader, you can test the buttons below. Only the first should have its menu items read.
<Arrange gap="large">
<Dropdown
triggerCallback={buttonProps => {
return (
<Arrange tabIndex="0" {...buttonProps}>
<Text fontWeight="bold">ARIA compliant trigger</Text>
<Icon icon={<SvgChevronSmallDown />} />
</Arrange>
)
}}
options={[
{ title: 'Share' },
{ title: 'Archive' },
{ title: 'Disabled', disabled: true },
]}
/>
<Dropdown
trigger={<div tabIndex="0">Simple trigger</div>}
options={[
{ title: 'Share', onClick: () => alert('hi'), },
{ title: 'Archive' },
{ title: 'Disabled', disabled: true },
]}
/>
<Dropdown
trigger={
<Arrange tabIndex="0">
<Text fontWeight="bold">Trigger with icon</Text>
<Icon icon={<SvgChevronSmallDown />} />
</Arrange>
}
options={[
{ title: 'Share' },
{ title: 'Archive' },
{ title: 'Disabled', disabled: true },
]}
/>
<Dropdown
trigger={<Button>Button trigger</Button>}
options={[
{ title: 'Share' },
{ title: 'Archive' },
{ title: 'Disabled', disabled: true },
]}
/>
<Dropdown
trigger={<IconButton altText="dropdown" icon={<SvgChevronDown />} />}
options={[
{ title: 'Share' },
{ title: 'Archive' },
{ title: 'Disabled', disabled: true },
]}
/>
</Arrange>
Menu can be positoned on the left or right side relative to the trigger.
<Arrange gap="medium" columns={['1fr', '1fr']}>
<Dropdown
triggerCallback={buttonProps => <Button {...buttonProps}>Trigger on left</Button>}
menuPosition="left"
options={[
{ title: 'Share' },
{ title: 'Archive' },
]}
/>
<Dropdown
triggerCallback={buttonProps => <Button {...buttonProps}>Trigger on right</Button>}
menuPosition="right"
options={[
{ title: 'Share' },
{ title: 'Archive' },
]}
/>
<Dropdown
triggerCallback={buttonProps => <Button {...buttonProps}>Trigger on top left</Button>}
menuPosition="topLeft"
options={[
{ title: 'Share' },
{ title: 'Archive' },
]}
/>
<Dropdown
triggerCallback={buttonProps => <Button {...buttonProps}>Trigger on top right</Button>}
menuPosition="topRight"
options={[
{ title: 'Share' },
{ title: 'Archive' },
]}
/>
<Dropdown
triggerCallback={buttonProps => <Button {...buttonProps}>Trigger on left side</Button>}
triggerOffset={8}
menuPosition="leftSide"
options={[
{ title: 'Share' },
{ title: 'Archive' },
]}
/>
<Dropdown
triggerCallback={buttonProps => <Button {...buttonProps}>Trigger on right side</Button>}
triggerOffset={-78}
menuPosition="rightSide"
options={[
{ title: 'Share' },
{ title: 'Archive' },
]}
/>
</Arrange>
With icons
<Dropdown
triggerCallback={buttonProps => <Button {...buttonProps}>Trigger</Button>}
options={[
{ title: 'Share ' + demoText.long, icon: <SvgShare /> },
{ title: 'Archive', icon: <SvgArchive /> },
{ title: 'Disabled', icon: <SvgTrash />, disabled: true }
]}
/>
See the Icon set.
With divider
<Dropdown
triggerCallback={buttonProps => <Button {...buttonProps}>Trigger</Button>}
options={[
{ title: 'Share ' },
{ title: 'Archive', },
{ title: 'Download', hasDivider: true }
]}
/>
Min and max width
<Arrange gap="medium">
<Dropdown
triggerCallback={buttonProps => <Button {...buttonProps}>With min width</Button>}
menuMinWidth={10}
options={[
{ title: 'A' },
{ title: 'B' },
{ title: 'C' }
]}
/>
<Dropdown
triggerCallback={buttonProps => <Button {...buttonProps}>With max width</Button>}
menuMaxWidth={56}
options={[
{ title: demoText.long },
{ title: demoText.short },
{ title: demoText.title }
]}
/>
</Arrange>
Max height
<Arrange gap="medium">
<Dropdown
menuMaxHeight={24}
triggerCallback={buttonProps => <Button {...buttonProps}>Trigger</Button>}
options={demoText.ordinals.map((ordinal) => {
return { title: ordinal }
})}
/>
<Dropdown
triggerCallback={buttonProps => <Button {...buttonProps}>Trigger</Button>}
options={demoText.ordinals.map((ordinal) => {
return { title: ordinal }
})}
/>
</Arrange>
Container
Specify where it's going to be rendered in the DOM, this is especially useful when working with Shadow DOM.
<Dropdown
container={() => document.querySelector("#renderContainer")}
triggerCallback={buttonProps => <Button {...buttonProps}>Trigger</Button>}
options={[
{ title: 'A' },
{ title: 'B' },
{ title: 'C' }
]}
/>
onOuterClick callback
<Dropdown
triggerCallback={buttonProps => <Button {...buttonProps}>console.log on change</Button>}
onOuterClick={() => console.log('onOuterClick')}
options={[
{ title: 'A' },
{ title: 'B' },
]}
/>
onOpenChange callback
Callback that provides the isOpen
value when dropdown opens or closes
<Dropdown
triggerCallback={buttonProps => <Button {...buttonProps}>console.log on change</Button>}
onOpenChange={newState => console.log(newState)}
options={[
{ title: 'A' },
{ title: 'B' },
]}
/>
Props
menuPosition
| 'left'
| 'right'
| 'topRight'
| 'topLeft'
| 'leftSide'
| 'rightSide'
'left'
menuMinWidth
number | string
24
menuMaxWidth
number | string
48
menuMaxHeight
number | string
container
HTMLElement | (() => HTMLElement) | string
onOpenChange
(isOpen: boolean) => void