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

Relevant links

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
  2. When you want to reduce visual clutter
  3. 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 or ToggleButtons.

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.

Accessibility

When preventSelection is true, the Dropdown will use role="menu", instead of role="listbox" for better screen reader support.

Custom size

You can change the width of the Dropdown component with CSS by using:

.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 using popup style (no title) */
.dnb-dropdown--is-popup .dnb-drawer-list__root {
width: 10rem;
}

Demos

Default dropdown

No value is defined, but a title is given.

const data = [
  // Every data item can, beside "content" - contain what ever
  {
    // (optional) can be what ever
    selectedKey: 'key_0',
    // (optional) is show instead of "content", once selected
    selectedValue: 'Item 1 Value',
    // Item content as a string or array
    content: 'Item 1 Content',
  },
  {
    selectedKey: 'key_1',
    content: ['Item 2 Value', 'Item 2 Content'],
  },
  {
    selectedValue: (
      <NumberFormat.BankAccountNumber alwaysSelectAll>
        11345678962
      </NumberFormat.BankAccountNumber>
    ),
    content: [
      <NumberFormat.BankAccountNumber key="ban" alwaysSelectAll>
        11345678962
      </NumberFormat.BankAccountNumber>,
      'Bank account number',
    ],
  },
  {
    selectedKey: 'key_2',
    selectedValue: 'Item 3 Value',
    content: ['Item 3 Content A', 'Item 3 Content B'],
  },
  {
    selectedKey: 'key_3',
    selectedValue: 'Item 4 Value',
    content: ['Item 4 Content A', <>Custom Component</>],
  },
]
render(
  <Dropdown
    data={data}
    label="Label"
    title="Please select a value"
    onChange={({ data }) => {
      console.log('onChange', data)
    }}
  />
)

Dropdown with different item content 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>,
  ]}
/>

Icon on left side

<Dropdown
  label="Label"
  iconPosition="left"
  data={data}
  value={3}
  skipPortal={true}
  onChange={({ data: selectedDataItem }) => {
    console.log('onChange', selectedDataItem)
  }}
  onOpen={() => {
    console.log('onOpen')
  }}
/>

Dropdown as tertiary variant

<Dropdown
  variant="tertiary"
  direction="bottom"
  independentWidth={true}
  iconPosition="left"
  align="left"
  data={data}
/>

Dropdown in different sizes

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

<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

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"
      iconPosition="left"
      data={data}
    />
    <CustomWidthTwo
      label="Label"
      size="small"
      preventSelection
      title={null}
      data={data}
    />
    <CustomWidthThree
      label="Label"
      size="large"
      align="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
<Dropdown data={data} label="Label" status="Message to the user" />

Findable list

With long list to make it scrollable and searchable

const scrollableData = [
  {
    content: 'A',
  },
  {
    content: 'B',
  },
  {
    selectedValue: (
      <NumberFormat.BankAccountNumber alwaysSelectAll>
        11345678962
      </NumberFormat.BankAccountNumber>
    ),
    content: [
      <NumberFormat.BankAccountNumber key="ban-1" alwaysSelectAll>
        11345678962
      </NumberFormat.BankAccountNumber>,
      'C',
    ],
  },
  {
    selectedValue: (
      <NumberFormat.BankAccountNumber alwaysSelectAll>
        15349648901
      </NumberFormat.BankAccountNumber>
    ),
    content: [
      <NumberFormat.BankAccountNumber key="ban-2" alwaysSelectAll>
        15349648901
      </NumberFormat.BankAccountNumber>,
      'D',
    ],
  },
  {
    content: 'E',
  },
  {
    selectedKey: 'key_1',
    selectedValue: '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 selectedKey: 'key_1'
    label="Label"
  />
)

Disabled dropdown

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

Individual options can also be disabled.

<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"
/>

Disabled tertiary dropdown

<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

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}
    preventSelection
    triggerElement={(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

Groups

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.

<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',
    },
  ]}
/>