Skip to content

Import

import { Dropdown } from '@dnb/eufemia'

Description

The Dropdown component is a fully custom-made component. This allows us to change its form based on context (small screens, touch devices, etc.)

When to use it:

When you need to provide a considerable amount of options to the user and do not have the space to do so. Other reasons may be because the hidden options may clutter the interface and need only be displayed after the user specifically requests it.

  1. when space is an issue (not enough)
  2. when you wish to reduce visual clutter
  3. when it is intuitive for the user to request the hidden content

When not to use it:

  1. do not use this if you have only a few menu options which could otherwise be shown such as Radio buttons or ToggleButtons.

NB: This pattern can be constructed in a number of 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 easily be used as a so-called action button by setting the prop action_menu={true}. In mobile view, the title/text will be hidden, only showing the icon and the DrawerList will open from the browser bottom.

Menu Button

The Dropdown component can easily be used as a so called menu button by setting the prop more_menu={true} which shows then the more icon, appears as dots. You also could use prevent_selection={true} together with an empty title title="" and aria-label="Choose an item".

Accessibility

Both the Action Menu and the Menu Button (and if prevent_selection is true), the Dropdown will use role="menu", instead of role="menuitems" for better screen reader support.

Custom size

Changing the width of the Dropdown component by CSS is easy done by doing:

.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):

/** 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;
}

Demos

Default dropdown

No value is defined, but a title is given.

Code Editor
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</>],
  },
]
render(
  <Dropdown
    data={data}
    label="Label"
    title="Please select a value"
    on_change={({ data }) => {
      console.log('on_change', data)
    }}
  />,
)

Dropdown with different item content directions

Code Editor
<Dropdown
  label="Label"
  data={[
    ['Vertical', 'alignment'],
    <>
      <P modifier="medium">Vertical</P>
      <P>alignment</P>
    </>,
    <Dropdown.HorizontalItem key="item-1">
      <P modifier="medium" right="x-small">
        Horizontal
      </P>
      <P>alignment</P>
    </Dropdown.HorizontalItem>,
  ]}
/>

Icon on left side

Code Editor
<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')
  }}
/>

ActionMenu

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.

Code Editor
<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)}
/>

MoreMenu

No lasting selection will be made.

Code Editor
<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"
/>
<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"
/>
<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)
  }}
/>

Dropdown as tertiary variant

Code Editor
<Dropdown
  variant="tertiary"
  independent_width={true}
  icon_position="left"
  align_dropdown="left"
  data={data}
/>

Custom item events

Code Editor
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()
}
render(
  <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>}
  />,
)

Dropdown in different sizes

Four sizes are available: small, default, medium and large

Code Editor
<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>

Custom width

Code Editor
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%;
  }
`
render(
  <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>,
)

Dropdown with status

And vertical label layout.

Message to the user
Code Editor
<Dropdown
  data={data}
  label="Label"
  label_direction="vertical"
  status="Message to the user"
/>

Findable list

With long list to make it scrollable and searchable

Code Editor
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',
  },
]
render(
  <Dropdown
    data={scrollableData}
    value="key_1" // use either index (5) or selected_key: 'key_1'
    label="Label"
  />,
)

Disabled dropdown

Code Editor
<Dropdown disabled data={['Disabled Dropdown']} label="Label" />

Disabled tertiary dropdown

Code Editor
<Dropdown
  disabled
  variant="tertiary"
  data={['Disabled Dropdown']}
  label="Disabled tertiary dropdown"
/>

Customized Dropdown

An example of how you can customize the look of your Dropdown

Code Editor
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'} />
  ),
}
render(
  <Dropdown
    data={data}
    action_menu
    trigger_element={(props) => (
      <button {...props} style={styles.customTrigger}>
        <Icon icon={newspaper} /> Custom trigger{' '}
        <Icon icon={chevron_down} />
      </button>
    )}
  />,
)

DrawerList opened

Only to visualize and used for visual testing

  • Brukskonto - Kari Nordmann
  • Sparekonto - Ole Nordmann
  • Feriekonto - Kari Nordmann med et kjempelangt etternavnsen
  • Oppussing - Ole Nordmann