@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
1,531 lines (1,412 loc) • 43 kB
Markdown
---
title: 'Dropdown'
description: 'The Dropdown component is a custom-made data selection component.'
version: 10.104.0
generatedAt: 2026-04-17T18:46:09.763Z
checksum: c7eec84a58ba0daa761164a593a20eb563837f79b4497653dd8e136091f8dd1b
---
```tsx
import { Dropdown } from '@dnb/eufemia'
```
The Dropdown component is a fully custom-made component. This allows us to change its form based on context (small screens, touch devices, etc.).
## Relevant links
- [Figma](https://www.figma.com/design/cdtwQD8IJ7pTeE45U148r1/%F0%9F%92%BB-Eufemia---Web?node-id=4243-1494)
- [Source code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-eufemia/src/components/dropdown)
- [Docs code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown)
### When to use
Use a dropdown when you need to provide many options to the user but don't have space to display them all. The hidden options should only appear when the user requests them, reducing visual clutter.
1. When space is limited
1. When you want to reduce visual clutter
1. When it's intuitive for users to request hidden content
### When not to use
1. Do not use a dropdown if you have only a few options that could be shown using [Radio buttons](/uilib/components/radio) or [ToggleButtons](/uilib/components/toggle-button).
**Note:** This pattern can be constructed in various ways to achieve a similar effect—from using the HTML `select` element to custom building with divs, spans, and JavaScript.
## Action menu
The Dropdown component can be used as an **action button** by setting `action_menu={true}`. In mobile view, the title/text will be hidden, showing only the icon, and the DrawerList will open from the bottom of the browser.
The Dropdown component can be used as a **menu button** by setting `more_menu={true}`, which displays the [more](/icons/primary
For both the Action Menu and the Menu Button (and when `prevent_selection` is true), the Dropdown will use `role="menu"` instead of `role="menuitems"` for better screen reader support.
You can change the **width** of the Dropdown component with CSS by using:
```css
.dnb-dropdown {
--dropdown-width: 20rem; /* custom width */
}
```
You can also set the width directly, but then it has to be defined like so (including `min-width`):
```css
/** Because of the included label/status etc. we target the "__shell" */
.dnb-dropdown__shell {
width: 10rem;
}
/** In order to change only the drawer-list width */
.dnb-dropdown .dnb-drawer-list__root {
width: 10rem;
}
/** If more_menu={true} is used */
.dnb-dropdown--is-popup .dnb-drawer-list__root {
width: 10rem;
}
```
No `value` is defined, but a `title` is given.
```tsx
render(
<Wrapper>
<ComponentBox data-visual-test="dropdown-closed">
{() => {
const data = [
// Every data item can, beside "content" - contain what ever
{
// (optional) can be what ever
selected_key: 'key_0',
// (optional) is show instead of "content", once selected
selected_value: 'Item 1 Value',
// Item content as a string or array
content: 'Item 1 Content',
},
{
selected_key: 'key_1',
content: ['Item 2 Value', 'Item 2 Content'],
},
{
selected_value: (
<NumberFormat always_selectall ban>
11345678962
</NumberFormat>
),
content: [
<NumberFormat key="ban" always_selectall ban>
11345678962
</NumberFormat>,
'Bank account number',
],
},
{
selected_key: 'key_2',
selected_value: 'Item 3 Value',
content: ['Item 3 Content A', 'Item 3 Content B'],
},
{
selected_key: 'key_3',
selected_value: 'Item 4 Value',
content: ['Item 4 Content A', <>Custom Component</>],
},
]
return (
<Dropdown
data={data}
label="Label"
title="Please select a value"
on_change={({ data }) => {
console.log('on_change', data)
}}
/>
)
}}
</ComponentBox>
</Wrapper>
)
```
```tsx
render(
<Wrapper>
<ComponentBox
scope={{
visualTestProps,
}}
data-visual-test="dropdown-item-directions"
>
<Dropdown
label="Label"
data={[
['Vertical', 'alignment'],
<>
<P weight="medium">Vertical</P>
<P>alignment</P>
</>,
<Dropdown.HorizontalItem key="item-1">
<P weight="medium" right="x-small">
Horizontal
</P>
<P>alignment</P>
</Dropdown.HorizontalItem>,
]}
{...visualTestProps(globalThis.IS_TEST)}
/>
</ComponentBox>
</Wrapper>
)
```
### Icon on left side
```tsx
render(
<Wrapper>
<ComponentBox
scope={{
data,
}}
data-visual-test="dropdown-left-icon"
>
<Dropdown
label="Label"
icon_position="left"
data={data}
value={3}
skip_portal={true}
on_change={({ data: selectedDataItem }) => {
console.log('on_change', selectedDataItem)
}}
on_show={() => {
console.log('on_show')
}}
/>
</ComponentBox>
</Wrapper>
)
```
The ActionMenu will change its characteristics in mobile view. It will hide the title, and the DrawerList will be placed on the bottom of the page.
```tsx
render(
<Wrapper>
<ComponentBox
scope={{
trash,
download,
}}
data-visual-test="dropdown-action_menu"
>
<Dropdown
title="ActionMenu"
action_menu={true}
align_dropdown="left"
data={() => ({
trash: (
<>
<Icon icon={trash} right />
Move to trash
</>
),
download: (
<>
<Icon icon={download} right />
Download
</>
),
})}
on_change={({ value }) => console.log('action:', value)}
/>
</ComponentBox>
</Wrapper>
)
```
No lasting selection will be made.
```tsx
render(
<Wrapper>
<ComponentBox
scope={{
visualTestProps,
}}
data-visual-test="dropdown-more_menu"
>
<Dropdown
more_menu={true}
size="small"
title="Choose an item"
data={() => [
<Link href="/" key="item-1">
Go to this Link
</Link>,
'Or press on me',
<>Custom component</>,
]}
right="small"
{...visualTestProps(globalThis.IS_TEST)}
/>
<Dropdown
prevent_selection={true}
align_dropdown="right"
size="small"
title={null}
aria-label="Choose an item"
data={() => ({
first: (
<Link href="/" key="item-1">
Go to this Link
</Link>
),
second: 'Or press on me',
third: <>Custom component</>,
})}
right="small"
{...visualTestProps(globalThis.IS_TEST)}
/>
<Dropdown
more_menu={true}
title="Choose an item"
data={[
<Link href="/" key="item-1">
Go to this Link
</Link>,
'Or press on me',
<>Custom component</>,
]}
right="small"
/>
<Dropdown
prevent_selection={true}
align_dropdown="right"
title={null}
aria-label="Choose an item"
data={() => ({
first: (
<Link href="/" key="item-1">
Go to this Link
</Link>
),
second: 'Or press on me',
third: <>Custom component</>,
})}
on_change={({ value }) => {
console.log('on_change', value)
}}
on_select={({ active_item }) => {
console.log('on_select', active_item)
}}
/>
</ComponentBox>
</Wrapper>
)
```
```tsx
render(
<Wrapper>
<ComponentBox
scope={{
data,
}}
data-visual-test="dropdown-tertiary"
>
<Dropdown
variant="tertiary"
direction="bottom"
independent_width={true}
icon_position="left"
align_dropdown="left"
data={data}
/>
</ComponentBox>
</Wrapper>
)
```
```tsx
render(
<Wrapper>
<ComponentBox
scope={{
data,
}}
data-visual-test="dropdown-tertiary-right"
>
<Dropdown
variant="tertiary"
direction="bottom"
independent_width={true}
icon_position="right"
align_dropdown="right"
data={data}
/>
</ComponentBox>
</Wrapper>
)
```
### Custom item events
```tsx
render(
<Wrapper>
<ComponentBox
scope={{
data,
visualTestProps,
}}
data-visual-test="dropdown-action_menu-custom"
>
{() => {
const CustomComponent = () => (
<CustomComponentInner
onTouchStart={preventDefault}
onClick={(e) => {
console.log('Do something different')
preventDefault(e)
}}
>
Custom event handler
</CustomComponentInner>
)
const CustomComponentInner = styled.span`
display: block;
width: 100%;
margin: -1rem -2rem -1rem -1rem;
padding: 1rem 2rem 1rem 1rem;
`
const preventDefault = (e) => {
e.stopPropagation()
e.preventDefault()
}
return (
<Dropdown
action_menu
right
label="Label"
title="Choose an item"
data={() => ({
first: (
<Link href="/" key="item-1">
Go to this Link
</Link>
),
second: 'Or press on me',
third: <CustomComponent key="item-2" />,
})}
on_change={({ value }) => {
console.log('More menu:', value)
}}
suffix={
<HelpButton title="Modal Title">Modal content</HelpButton>
}
{...visualTestProps(globalThis.IS_TEST)}
/>
)
}}
</ComponentBox>
</Wrapper>
)
```
Four sizes are available: `small`, `default`, `medium` and `large`
```tsx
render(
<Wrapper>
<ComponentBox
data-visual-test="dropdown-sizes"
scope={{
data,
}}
>
<Flex.Vertical>
<Dropdown label="Label" size="default" data={() => data} />
<Dropdown label="Label" size="medium" data={() => data} />
<Dropdown label="Label" size="large" data={() => data} />
</Flex.Vertical>
</ComponentBox>
</Wrapper>
)
```
```tsx
render(
<Wrapper>
<ComponentBox
scope={{
data,
}}
>
{() => {
const CustomWidthOne = styled(Dropdown)`
.dnb-dropdown__shell {
width: 10rem;
}
`
const CustomWidthTwo = styled(Dropdown)`
&.dnb-dropdown--is-popup .dnb-drawer-list__root {
width: 12rem;
}
`
const CustomWidthThree = styled(Dropdown)`
/** Change the "__shell" width */
.dnb-dropdown__shell {
width: 10rem;
}
/** Change the "__list" width */
.dnb-drawer-list__root {
width: 20rem;
}
`
const CustomWidthFour = styled(Dropdown)`
width: 60%;
min-width: 224px; /** 14rem (please use pixels on min-width!) */
max-width: 25rem;
/** In case we have a label */
.dnb-form-label + .dnb-dropdown__inner {
width: 100%;
}
`
return (
<Flex.Vertical>
<CustomWidthOne
label="Label"
size="default"
icon_position="left"
data={data}
/>
<CustomWidthTwo
label="Label"
size="small"
more_menu
data={data}
/>
<CustomWidthThree
label="Label"
size="large"
align_dropdown="right"
data={data}
/>
<CustomWidthFour
title="Min and max width"
stretch={true}
data={data}
/>
</Flex.Vertical>
)
}}
</ComponentBox>
</Wrapper>
)
```
And vertical label layout.
```tsx
render(
<Wrapper>
<ComponentBox
data-visual-test="dropdown-status-error"
scope={{
data,
}}
>
<Dropdown
data={data}
label="Label"
label_direction="vertical"
status="Message to the user"
/>
</ComponentBox>
</Wrapper>
)
```
### Findable list
With long list to make it scrollable and searchable
```tsx
render(
<Wrapper>
<ComponentBox>
{() => {
const scrollableData = [
{
content: 'A',
},
{
content: 'B',
},
{
selected_value: (
<NumberFormat always_selectall ban>
11345678962
</NumberFormat>
),
content: [
<NumberFormat key="ban-1" always_selectall ban>
11345678962
</NumberFormat>,
'C',
],
},
{
selected_value: (
<NumberFormat always_selectall ban>
15349648901
</NumberFormat>
),
content: [
<NumberFormat key="ban-2" always_selectall ban>
15349648901
</NumberFormat>,
'D',
],
},
{
content: 'E',
},
{
selected_key: 'key_1',
selected_value: 'Find me by keypress',
content: ['F', 'F', 'F', 'F'],
},
{
content: 'G',
},
{
content: 'H',
},
]
return (
<Dropdown
data={scrollableData}
value="key_1" // use either index (5) or selected_key: 'key_1'
label="Label"
/>
)
}}
</ComponentBox>
</Wrapper>
)
```
```tsx
render(
<Wrapper>
<ComponentBox
scope={{
data,
}}
data-visual-test="dropdown-disabled"
>
<Dropdown disabled data={['Disabled Dropdown']} label="Label" />
</ComponentBox>
</Wrapper>
)
```
Individual options can also be disabled.
```tsx
render(
<Wrapper>
<ComponentBox data-visual-test="dropdown-disabled-options">
<Dropdown
data={[
{
content: 'Item 1 Content',
},
{
content: 'Item 2 Content',
disabled: true,
},
{
content: 'Item 3 Content',
disabled: true,
},
{
content: 'Item 4 Content A',
},
]}
label="Label"
/>
</ComponentBox>
</Wrapper>
)
```
### Disabled tertiary dropdown
```tsx
render(
<Wrapper>
<ComponentBox data-visual-test="dropdown-disabled-tertiary">
<Dropdown
disabled
variant="tertiary"
data={['Disabled Dropdown']}
label="Disabled tertiary dropdown"
/>
</ComponentBox>
</Wrapper>
)
```
### Customized Dropdown
An example of how you can customize the look of your `Dropdown`
```tsx
render(
<Wrapper>
<ComponentBox
scope={{
chevron_right,
newspaper,
chevron_down,
}}
>
{() => {
const styles = {
customTrigger: {
backgroundColor: '#d4ecc5',
color: '#14555a',
border: 'none',
borderRadius: '8px',
padding: '8px 16px',
fontWeight: 600,
},
customMenuItem: {
display: 'flex',
flexFlow: 'row nowrap',
justifyContent: 'space-between',
alignItems: 'center',
},
customMenuItemTitle: {
display: 'flex',
flexFlow: 'column',
gap: '0.5rem',
},
}
const MenuItem = ({ title, content, key }) => (
<span style={styles.customMenuItem} key="item-1">
<span style={styles.customMenuItemTitle}>
{title}
<span>{content}</span>
</span>
<Icon icon={chevron_right} />
</span>
)
const data = {
accounts: (
<MenuItem
key="item-1"
title="Accounts"
content={'Bills, Savings'}
/>
),
loans: (
<MenuItem
key="item-2"
title="Loans"
content={'Mortgage, Car'}
/>
),
cards: (
<MenuItem
key="item-3"
title="Cards"
content={'Visa, Mastercard'}
/>
),
stocks: (
<MenuItem
key="item-4"
title="Stocks"
content={'Nvidia, Apple'}
/>
),
}
return (
<Dropdown
data={data}
action_menu
trigger_element={(props) => (
<button {...props} style={styles.customTrigger}>
<Icon icon={newspaper} /> Custom trigger{' '}
<Icon icon={chevron_down} />
</button>
)}
/>
)
}}
</ComponentBox>
</Wrapper>
)
```
Only to visualize and used for visual testing
```tsx
render(
<Wrapper>
<ComponentBox
data-visual-test="dropdown-list"
scope={{
data,
}}
hideCode
>
<span className="dnb-drawer-list__list">
<ul className="dnb-drawer-list__options">
<li className="dnb-drawer-list__option first-of-type">
<span className="dnb-drawer-list__option__inner">
Brukskonto - Kari Nordmann
</span>
</li>
<li className="dnb-drawer-list__option dnb-drawer-list__option--selected">
<span className="dnb-drawer-list__option__inner">
<span className="dnb-drawer-list__option__item item-nr-1">
<NumberFormat always_selectall key="n-1" ban>
12345678902
</NumberFormat>
</span>
<span className="dnb-drawer-list__option__item">
Sparekonto - Ole Nordmann
</span>
</span>
</li>
<li className="dnb-drawer-list__option">
<span className="dnb-drawer-list__option__inner">
<span className="dnb-drawer-list__option__item item-nr-1">
<NumberFormat always_selectall key="n-2" ban>
11345678962
</NumberFormat>
</span>
<span className="dnb-drawer-list__option__item">
Feriekonto - Kari Nordmann med et kjempelangt etternavnsen
</span>
</span>
</li>
<li className="dnb-drawer-list__option last-of-type">
<span className="dnb-drawer-list__option__inner">
<span className="dnb-drawer-list__option__item item-nr-1">
<NumberFormat always_selectall key="n-3" ban>
15349648901
</NumberFormat>
</span>
<span className="dnb-drawer-list__option__item">
Oppussing - Ole Nordmann
</span>
</span>
</li>
<li className="dnb-drawer-list__triangle" />
</ul>
</span>
</ComponentBox>
</Wrapper>
)
```
```tsx
render(
<Wrapper>
<ComponentBox data-visual-test="dropdown-ellipsis">
<Dropdown
data={['Long text that will overflow with CSS ellipsis']}
value={0}
label="Label"
/>
</ComponentBox>
</Wrapper>
)
```
```tsx
render(
<Wrapper>
<ComponentBox data-visual-test="dropdown-independent_width_left">
<Dropdown
independent_width={true}
icon_position="left"
direction="top"
title="Choose an item"
data={() => [
<Link href="/" key="item-1">
Go to this Link
</Link>,
'Or press on me',
<>Custom component</>,
]}
right="small"
/>
</ComponentBox>
</Wrapper>
)
```
```tsx
render(
<Wrapper>
<ComponentBox data-visual-test="dropdown-independent_width_right">
<Dropdown
independent_width={true}
icon_position="right"
direction="top"
title="Choose an item"
data={() => [
<Link href="/" key="item-1">
Go to this Link
</Link>,
'Or press on me',
<>Custom component</>,
]}
right="small"
/>
</ComponentBox>
</Wrapper>
)
```
If an item has a `groupIndex` property, it will use the groups in the `groups` property. Only the first group can be without title, all other groups must have a title.
```tsx
render(
<Wrapper>
<ComponentBox data-visual-test="dropdown-groups">
<Dropdown
groups={[undefined, 'Pets', 'Cars']}
data={[
{
groupIndex: 0,
content: 'Default 2',
},
{
groupIndex: 0,
content: 'Default 1',
},
{
groupIndex: 1,
content: 'Cat',
},
{
groupIndex: 1,
content: 'Dog',
},
{
groupIndex: 2,
content: 'Jeep',
},
{
groupIndex: 2,
content: 'Van',
},
]}
/>
</ComponentBox>
</Wrapper>
)
```
## Properties
You may check out the [DrawerList Properties](#drawerlist-properties) down below as well as the [Data structure examples](#the-data-property).
```json
{
"props": {
"title": {
"doc": "Give a title to let the users know what they have to do. Defaults to `Valgmeny`.",
"type": "React.ReactNode",
"status": "optional"
},
"value": {
"doc": "Define a preselected data entry (index). More info down below.",
"type": "number",
"status": "optional"
},
"variant": {
"doc": "Defines the kind of dropdown. Possible values are `primary`, `secondary`, `tertiary` and `signal`. Defaults to `secondary`.",
"type": ["primary", "secondary", "tertiary", "signal"],
"status": "optional"
},
"icon": {
"doc": "Icon to be included in the dropdown.",
"type": "React.Node",
"status": "optional"
},
"icon_size": {
"doc": "Change the size of the icon pragmatically.",
"type": "string",
"status": "optional"
},
"icon_position": {
"doc": "Position of the icon inside the dropdown. Set to `left` or `right`. Defaults to `right`.",
"type": ["left", "right"],
"status": "optional"
},
"triangle_position": {
"doc": "Position of arrow / triangle of the drawer. Set to `left` or `right`. Defaults to `right`.",
"type": ["left", "right"],
"status": "optional"
},
"size": {
"doc": "Define the height of the Dropdown. Can be set to `small`, `default`, `medium` and `large`. Defaults to `default`.",
"type": ["small", "default", "medium", "large"],
"status": "optional"
},
"opened": {
"doc": "If set to `true`, the Dropdown will be rendered initially with a visible and accessible data list / options.",
"type": "boolean",
"status": "optional"
},
"open_on_focus": {
"doc": "If set to `true`, the Dropdown will be opened when the users enter the trigger button with a focus action.",
"type": "boolean",
"status": "optional"
},
"prevent_selection": {
"doc": "If set to `true`, no permanent selection will be made. Defaults to `false`.",
"type": "boolean",
"status": "optional"
},
"action_menu": {
"doc": "Same as `prevent_selection`, but the DrawerList will be opened from the bottom of the page for mobile devices. Defaults to `false`.",
"type": "boolean",
"status": "optional"
},
"more_menu": {
"doc": "Same as `prevent_selection`, but the \"selection area\" (given title) will not be visible and the icon `more` (three dots) is used. Defaults to `false`.",
"type": "boolean",
"status": "optional"
},
"align_dropdown": {
"doc": "Use `right` to change the options alignment direction. Makes only sense to use in combination with `prevent_selection` or `more_menu`. Defaults to `left`.",
"type": ["left", "right"],
"status": "optional"
},
"independent_width": {
"doc": "If set to `true`, the Dropdown will handle its width independent to the content width. Defaults to `false`.",
"type": "boolean",
"status": "optional"
},
"skip_portal": {
"doc": "Set to `true` to disable the React Portal behavior. Defaults to `false`.",
"type": "boolean",
"status": "optional"
},
"stretch": {
"doc": "If set to `true`, then the dropdown will be 100% in available `width`.",
"type": "boolean",
"status": "optional"
},
"status": {
"doc": "Text with a status message. The style defaults to an error message. You can use `true` to only get the status color, without a message.",
"type": ["error", "info", "boolean"],
"status": "optional"
},
"status_state": {
"doc": "Defines the state of the status. It's two statuses `[error, info]`. Defaults to `error`.",
"type": ["error", "info"],
"status": "optional"
},
"status_props": {
"doc": "Use an object to define additional FormStatus properties.",
"type": "object",
"status": "optional"
},
"globalStatus": {
"doc": "The [configuration](/uilib/components/global-status/properties/#configuration-object) used for the target [GlobalStatus](/uilib/components/global-status).",
"type": "object",
"status": "optional"
},
"label": {
"doc": "Prepends the Form Label component. If no ID is provided, a random ID is created.",
"type": "React.ReactNode",
"status": "optional"
},
"label_direction": {
"doc": "Use `label_direction=\"vertical\"` to change the label layout direction. Defaults to `horizontal`.",
"type": ["horizontal", "vertical"],
"status": "optional"
},
"label_sr_only": {
"doc": "Use `true` to make the label only readable by screen readers.",
"type": "boolean",
"status": "optional"
},
"suffix": {
"doc": "Text describing the content of the Dropdown more than the label. You can also send in a React component, so it gets wrapped inside the Dropdown component.",
"type": "React.ReactNode",
"status": "optional"
},
"trigger_element": {
"doc": "Lets you provide a custom React element as the trigger HTML element.",
"type": "React.ReactNode",
"status": "optional"
},
"innerRef": {
"doc": "By providing a React.ref you can get the internally used main element (DOM). E.g. `innerRef={myRef}` by using `React.createRef()` or `React.useRef()`.",
"type": "React.RefObject",
"status": "optional"
},
"buttonRef": {
"doc": "By providing a React.ref you can get the internally used button element (DOM). E.g. `buttonRef={myRef}` by using `React.createRef()` or `React.useRef()`.",
"type": "React.RefObject",
"status": "optional"
},
"skeleton": {
"doc": "If set to `true`, an overlaying skeleton with animation will be shown.",
"type": "boolean",
"status": "optional"
},
"[DrawerList](/uilib/components/fragments/drawer-list/properties)": {
"doc": "All DrawerList properties.",
"type": "Various",
"status": "optional"
},
"[Space](/uilib/layout/space/properties)": {
"doc": "Spacing properties like `top` or `bottom` are supported.",
"type": ["string", "object"],
"status": "optional"
}
}
}
```
```json
{
"props": {
"[data](#the-data-property)": {
"doc": "The data we want to fill the list with. [Details on the type of {DATA} can be found below](#the-data-property). The data can be provided as an array or object. Or as a function that returns the data (called when user opens the list).",
"type": ["{DATA}", "() => {DATA}"],
"status": "required"
},
"groups": {
"doc": "An array of group titles for the list items. Only the first group can be `undefined`",
"type": "React.ReactNode[]",
"status": "optional"
},
"value": {
"doc": "Define a preselected `data` entry. In order of priority, `value` can be set to: object key (if `data` is an object), `selectedKey` prop (if `data` is an array), array index (if no `selectedKey`) or content (if `value` is a non-integer string).",
"type": ["string", "number"],
"status": "optional"
},
"default_value": {
"doc": "Define a startup value or handle a re-render without handling the state during the re-render by yourself. Defaults to `null`.",
"type": "number",
"status": "optional"
},
"triangle_position": {
"doc": "Position of the arrow icon/triangle inside the drawer-list. Set to 'left' or 'right'. Defaults to 'left' if not set.",
"type": "string",
"status": "optional"
},
"direction": {
"doc": "Defines the direction of how the drawer-list shows the options list. Can be 'bottom' or 'top'. Defaults to 'auto'.",
"type": "string",
"status": "optional"
},
"label_direction": {
"doc": "The direction of the label. If set to 'horizontal', the label will be positioned horizontally next to the input element. If set to 'vertical', the label will be positioned vertically above the input element.",
"type": "string",
"status": "optional"
},
"prevent_selection": {
"doc": "If set to `true`, the DrawerList will then not make any permanent selection.",
"type": "boolean",
"status": "optional"
},
"focusable": {
"doc": "If set to `true`, the element is then focusable by assertive technologies.",
"type": "boolean",
"status": "optional"
},
"prevent_close": {
"doc": "If set to `true`, the DrawerList will not close on any events.",
"type": "boolean",
"status": "optional"
},
"keep_open": {
"doc": "If set to `true`, the DrawerList will close on outside clicks, but not on selection.",
"type": "boolean",
"status": "optional"
},
"independent_width": {
"doc": "If set to `true`, the DrawerList will handle its width and position independently of the parent/mother element.",
"type": "boolean",
"status": "optional"
},
"fixed_position": {
"doc": "If set to `true`, the DrawerList will be fixed in its scroll position by using CSS `position: fixed;`.",
"type": "boolean",
"status": "optional"
},
"enable_body_lock": {
"doc": "If set to `true`, the HTML body will get locked from scrolling when the Dropdown is open.",
"type": "boolean",
"status": "optional"
},
"skip_keysearch": {
"doc": "If set to `true`, search items by the first key will be ignored.",
"type": "boolean",
"status": "optional"
},
"ignore_events": {
"doc": "If set to `true`, all keyboard and mouse events will be ignored.",
"type": "boolean",
"status": "optional"
},
"align_drawer": {
"doc": "Use 'right' to change the options alignment direction. Makes only sense to use in combination with `prevent_selection` or `more_menu` - or if an independent width is used.",
"type": "string",
"status": "optional"
},
"list_class": {
"doc": "Define an HTML class that will be set on the list, beside `dnb-drawer-list__list`.",
"type": "string",
"status": "optional"
},
"portal_class": {
"doc": "Define an HTML class that will be set on the DOM portal beside `dnb-drawer-list__portal__style`. Can be useful to handle e.g. a custom `z-index` in relation to a header.",
"type": "string",
"status": "optional"
},
"scrollable": {
"doc": "Defines if the options list should be scrollable (the `max-height` is set by default to `50vh`).",
"type": "boolean",
"status": "optional"
},
"no_scroll_animation": {
"doc": "To disable scrolling animation.",
"type": "boolean",
"status": "optional"
},
"no_animation": {
"doc": "To disable appear/disappear (show/hide) animation.",
"type": "boolean",
"status": "optional"
},
"skip_portal": {
"doc": "To disable the React Portal behavior.",
"type": "boolean",
"status": "optional"
},
"min_height": {
"doc": "Defines the minimum height (in `rem`) of the options list.",
"type": "string",
"status": "optional"
},
"max_height": {
"doc": "Defines the maximum height (in `rem`) of the options list.",
"type": "string",
"status": "optional"
},
"page_offset": {
"doc": "Defines the available scrollable height. If scrolling should not change the height of the drawer-list, then set it to `0` (useful if the DrawerList is used in fixed positions on contrast to a scrollable page content).",
"type": "string",
"status": "optional"
},
"observer_element": {
"doc": "Set a HTML element, either as a selector or a DOM element. Can be used to send in an element which will be used to make the direction calculation on.",
"type": "string",
"status": "optional"
},
"cache_hash": {
"doc": "Set a `cache_hash` as a string to enable internal memorizing of the list to enhance rerendering performance. Components like Autocomplete are using this because of the huge data changes due to search and reorder.",
"type": "string",
"status": "optional"
},
"wrapper_element": {
"doc": "Has to be an HTML Element, or a selector for one, ideally a mother element, used to calculate sizes and distances. Also used for the 'click outside' detection. Clicking on the `wrapper_element` will not trigger an outside click.",
"type": ["string", "HTMLElement"],
"status": "optional"
},
"options_render": {
"doc": "Has to be a function, returning the items again. See [example](/uilib/components/fragments/drawer-list#example-usage-of-options_render). This can be used to add additional options above the actual rendered list.",
"type": "function",
"status": "optional"
},
"[Space](/uilib/layout/space/properties)": {
"doc": "Spacing properties like `top` or `bottom` are supported.",
"type": ["string", "object"],
"status": "optional"
}
}
}
```
Should either be an index (integer) of the data array or a key – defined by `selectedKey` (the deprecated `selected_key` should not start with a number) inside an array item.
If `data` is an object, use the object key as the `value` to define the selected item. Can be a string or integer.
## The `data` property
The `data` can be structured in two main ways:
- As an array
- As an object.
An array is preferred as it gives you the most options.
### `data` as an array
```ts
// an array can contain complex items and offers the most control
const data = [
{
content: "Item 1",
},
{
content: <span>Item 2</span>
},
{
content: ["Item 3", "Line 2", <span>Line 3</span>]
},
{
content: ['Main account', '1234 12 12345'],
selected_value: 'Main account (605,22 kr)',
suffix_value: '605,22 kr',
},
{
content: ['Old account', <i>Closed</i>],
disabled: true,
suffix_value: '0,00 kr',
},
]
// If you only use the `content` property, you can use it directly in the array.
// This list is identical to the one above:
const data = [
"Item 1",
<span>Item 2</span>,
["Item 3", "Line 2", <span>Line 3</span>],
{
content: ['Main account', '1234 12 12345'],
selected_value: 'Main account (605,22 kr)',
suffix_value: '605,22 kr',
},
{
content: ['Old account', <i>Closed</i>],
disabled: true,
suffix_value: '0,00 kr',
},
]
const onChange = ({ data, value }) => {
console.log(data) // returns the item as it appears in the array
console.log(value) // returns the index of the item
}
```
Each object in the array have the following properties:
```json
{
"props": {
"content": {
"doc": "Visual content in the list item",
"type": ["string", "React.node", "(string | React.Node)[]"],
"status": "optional"
},
"disabled": {
"doc": "Disables the list item from selection",
"type": "boolean",
"status": "optional"
},
"groupIndex": {
"doc": "What group index in the `groups` prop this item belongs to.",
"type": "number",
"status": "optional"
},
"selectedKey": {
"doc": "If set, can be used instead of array index by the `value` prop",
"type": ["string", "number"],
"status": "optional"
},
"selected_value": {
"doc": "Replaces the standard value output for selected item. Only used in some implementations (Dropdown, Autocomplete).",
"type": ["string", "React.Node"],
"status": "optional"
},
"suffix_value": {
"doc": "Content placed to the right in the list item.",
"type": ["string", "React.node"],
"status": "optional"
},
"selected_key": {
"doc": "Use prop `selectedKey` instead",
"type": ["string", "number"],
"status": "deprecated"
}
}
}
```
A simpler alternative, but with less options
```ts
// Each entry can contain the same type of value as the array's `content` property
const data = {
first: "Item 1",,
second: <span>Item 2</span>,
last: ["Item 3", "Line 2", <span>Line 3</span>],
}
const onChange = ({ data, value }) => {
console.log(data)
// returns a generated object representing the item:
// {
// selectedKey: 'first',
// value: 'first',
// content: 'Item 1',
// type: 'object'
// }
console.log(value) // returns the key ("first", "second", or "last"), instead of an index
}
```
The following is an overview of all the types that the `data` property accepts. (These are not actual names of actual types in the library.)
```ts
// The visual content that is shown in one DrawerList item.
// An array can be used to define multiple lines.
type CONTENT = string | React.Node | (string | React.Node)[]
// An array item
type ARRAY_OBJECT = {
content: CONTENT
disabled?: boolean
selectedKey?: string | number
selected_value?: string | React.Node
suffix_value?: string | React.Node
style?: React.CSSProperties
}
// `data` as an array. A list of "ARRAY_OBJECT" types is preferred,
// but the "CONTENT" type can be useful for simple lists.
type ARRAY = (CONTENT | ARRAY_OBJECT)[]
// `data` as an object. Can only contain the "CONTENT" type.
// Each `key` behaves like the "ARRAY_OBJECT"'s `selectedKey`.
type RECORD = Record<string, CONTENT>
// An object or array that represents the entire DrawerList list.
type DATA = ARRAY | RECORD
// The final type of the `data` property:
let data: DATA | () => DATA
```
There is technically support for sending in a JSON string of the data to the `data` property. But this is an old functionality that we do not really support anymore.
```json
{
"locales": ["da-DK", "en-GB", "nb-NO", "sv-SE"],
"entries": {
"DrawerList.defaultGroupSR": {
"nb-NO": "Standardvalg",
"en-GB": "Default options",
"sv-SE": "Standardval",
"da-DK": "Standardvalg"
},
"DrawerList.missingGroup": {
"nb-NO": "Gruppe",
"en-GB": "Group",
"sv-SE": "Grupp",
"da-DK": "Gruppe"
},
"DrawerList.noGroupSR": {
"nb-NO": "Andre valg",
"en-GB": "Other options",
"sv-SE": "Andra val",
"da-DK": "Andre valg"
},
"Dropdown.title": {
"nb-NO": "Valgmeny",
"en-GB": "Option Menu",
"sv-SE": "Valmeny",
"da-DK": "Valgmenu"
}
}
}
```
```json
{
"props": {
"on_change": {
"doc": "Will be called on state changes made by the user. Returns an object with the new selected `data` item `{ data, event, attributes, value }`.",
"type": "function",
"status": "optional"
},
"on_select": {
"doc": "Will be called once the user focuses or selects an item by a click or keyboard navigation. Returns an object with the new selected `data` item `{ data, event, attributes, value, active_item }`. The **active_item** property is the currently selected item by keyboard navigation.",
"type": "function",
"status": "optional"
},
"on_show": {
"doc": "Will be called once the user presses the dropdown. Returns the data item `{ data, attributes }`.",
"type": "function",
"status": "optional"
},
"on_hide": {
"doc": "Will be called once the user presses the dropdown again, or clicks somewhere else. Returns the data item `{ data, attributes }`.",
"type": "function",
"status": "optional"
}
}
}
```
The difference between `on_change` and `on_select` is:
- `on_change` will be called when the state changes, either with a **click** or **space/enter** keypress confirmation.
- `on_select` differs most when the user is navigating by keyboard. Once the user is pressing e.g. the arrow keys, the selection is changing, but not the state.