Skip to main content
Default Gray Amethyst

Dropdown

<mo-dropdown> | MODropdown
Since 1.0 stable

Dropdowns expose additional content that “drops down” in a panel.

Dropdowns consist of a trigger and a panel. By default, activating the trigger will expose the panel and interacting outside of the panel will close it.

Dropdown Dropdown Item 1 Dropdown Item 2 Dropdown Item 3 Checked Disabled Prefix Suffix Icon
<mo-dropdown>
  <mo-button slot="trigger" caret>Dropdown</mo-button>
  <mo-menu>
    <mo-menu-item>Dropdown Item 1</mo-menu-item>
    <mo-menu-item>Dropdown Item 2</mo-menu-item>
    <mo-menu-item>Dropdown Item 3</mo-menu-item>
    <mo-divider></mo-divider>
    <mo-menu-item type="checkbox" checked>Checked</mo-menu-item>
    <mo-menu-item disabled>Disabled</mo-menu-item>
    <mo-divider></mo-divider>
    <mo-menu-item>
      Prefix
      <mo-icon slot="prefix" name="home"></mo-icon>
    </mo-menu-item>
    <mo-menu-item>
      Suffix Icon
      <mo-icon slot="suffix" name="settings"></mo-icon>
    </mo-menu-item>
  </mo-menu>
</mo-dropdown>
import {
  MOButton,
  MODivider,
  MODropdown,
  MOIcon,
  MOMenu,
  MOMenuItem
} from '@metsooutotec/modes-web-components/dist/react';

const App = () => (
  <MODropdown>
    <MOButton slot="trigger" caret>
      Dropdown
    </MOButton>
    <MOMenu>
      <MOMenuItem>Dropdown Item 1</MOMenuItem>
      <MOMenuItem>Dropdown Item 2</MOMenuItem>
      <MOMenuItem>Dropdown Item 3</MOMenuItem>
      <MODivider />
      <MOMenuItem checked>Checked</MOMenuItem>
      <MOMenuItem disabled>Disabled</MOMenuItem>
      <MODivider />
      <MOMenuItem>
        Prefix
        <MOIcon slot="prefix" name="home" />
      </MOMenuItem>
      <MOMenuItem>
        Suffix Icon
        <MOIcon slot="suffix" name="settings" />
      </MOMenuItem>
    </MOMenu>
  </MODropdown>
);

Examples

Getting the selected item

When dropdowns are used with menus, you can listen for the mo-select event to determine which menu item was selected. The menu item element will be exposed in event.detail.item. You can set value props to make it easier to identify commands.

<div class="dropdown-selection">
  <mo-dropdown>
    <mo-button slot="trigger" caret>Edit</mo-button>
    <mo-menu>
      <mo-menu-item value="cut">Cut</mo-menu-item>
      <mo-menu-item value="copy">Copy</mo-menu-item>
      <mo-menu-item value="paste">Paste</mo-menu-item>
    </mo-menu>
  </mo-dropdown>
</div>

<script>
  const container = document.querySelector('.dropdown-selection');
  const dropdown = container.querySelector('mo-dropdown');

  dropdown.addEventListener('mo-select', event => {
    const selectedItem = event.detail.item;
    console.log(selectedItem.value);
  });
</script>
import { MOButton, MODropdown, MOMenu, MOMenuItem } from '@metsooutotec/modes-web-components/dist/react';

const App = () => {
  function handleSelect(event) {
    const selectedItem = event.detail.item;
    console.log(selectedItem.value);
  }

  return (
    <MODropdown>
      <MOButton slot="trigger" caret>
        Edit
      </MOButton>
      <MOMenu onMoSelect={handleSelect}>
        <MOMenuItem value="cut">Cut</MOMenuItem>
        <MOMenuItem value="copy">Copy</MOMenuItem>
        <MOMenuItem value="paste">Paste</MOMenuItem>
      </MOMenu>
    </MODropdown>
  );
};

Alternatively, you can listen for the click event on individual menu items. Note that, using this approach, disabled menu items will still emit a click event.

<div class="dropdown-selection-alt">
  <mo-dropdown>
    <mo-button slot="trigger" caret>Edit</mo-button>
    <mo-menu>
      <mo-menu-item value="cut">Cut</mo-menu-item>
      <mo-menu-item value="copy">Copy</mo-menu-item>
      <mo-menu-item value="paste">Paste</mo-menu-item>
    </mo-menu>
  </mo-dropdown>
</div>

<script>
  const container = document.querySelector('.dropdown-selection-alt');
  const cut = container.querySelector('mo-menu-item[value="cut"]');
  const copy = container.querySelector('mo-menu-item[value="copy"]');
  const paste = container.querySelector('mo-menu-item[value="paste"]');

  cut.addEventListener('click', () => console.log('cut'));
  copy.addEventListener('click', () => console.log('copy'));
  paste.addEventListener('click', () => console.log('paste'));
</script>
import { MOButton, MODropdown, MOMenu, MOMenuItem } from '@metsooutotec/modes-web-components/dist/react';

const App = () => {
  function handleCut() {
    console.log('cut');
  }

  function handleCopy() {
    console.log('copy');
  }

  function handlePaste() {
    console.log('paste');
  }

  return (
    <MODropdown>
      <MOButton slot="trigger" caret>
        Edit
      </MOButton>
      <MOMenu>
        <MOMenuItem onClick={handleCut}>Cut</MOMenuItem>
        <MOMenuItem onClick={handleCopy}>Copy</MOMenuItem>
        <MOMenuItem onClick={handlePaste}>Paste</MOMenuItem>
      </MOMenu>
    </MODropdown>
  );
};

To create a submenu, nest an <mo-menu slot="submenu"> element in a menu item.

Edit Cut Copy Paste Disabled Show toolbar Find Find… Find Next Find Previous Transformations Make uppercase Make lowercase Capitalize
<mo-dropdown>
  <mo-button slot="trigger" caret>Edit</mo-button>
  <mo-menu>
    <mo-menu-item value="cut">Cut</mo-menu-item>
    <mo-menu-item value="copy">Copy</mo-menu-item>
    <mo-menu-item value="paste">Paste</mo-menu-item>
    <mo-menu-item disabled value="disabled">Disabled</mo-menu-item>
    <mo-divider></mo-divider>
    <mo-menu-item type="checkbox" checked value="copy">Show toolbar</mo-menu-item>
    <mo-divider></mo-divider>
    <mo-menu-item>
      Find
      <mo-menu slot="submenu">
        <mo-menu-item value="find">Find…</mo-menu-item>
        <mo-menu-item value="find-previous">Find Next</mo-menu-item>
        <mo-menu-item value="find-next">Find Previous</mo-menu-item>
      </mo-menu>
    </mo-menu-item>
    <mo-menu-item>
      Transformations
      <mo-menu slot="submenu">
        <mo-menu-item value="uppercase">Make uppercase</mo-menu-item>
        <mo-menu-item value="lowercase">Make lowercase</mo-menu-item>
        <mo-menu-item value="capitalize">Capitalize</mo-menu-item>
      </mo-menu>
    </mo-menu-item>
  </mo-menu>
</mo-dropdown>
import MOButton from '@metsooutotec/modes-web-components/dist/react/button';
import MODivider from '@metsooutotec/modes-web-components/dist/react/divider';
import MODropdown from '@metsooutotec/modes-web-components/dist/react/dropdown';
import MOMenu from '@metsooutotec/modes-web-components/dist/react/menu';
import MOMenuItem from '@metsooutotec/modes-web-components/dist/react/menu-item';

const App = () => (
  <>
    <MODropdown>
      <MOButton slot="trigger" caret>Edit</MOButton>

      <MOMenu style="max-width: 200px;">
        <MOMenuItem value="undo">Undo</MOMenuItem>
        <MOMenuItem value="redo">Redo</MOMenuItem>
        <MODivider />
        <MOMenuItem value="cut">Cut</MOMenuItem>
        <MOMenuItem value="copy">Copy</MOMenuItem>
        <MOMenuItem value="paste">Paste</MOMenuItem>
        <MODivider />
        <MOMenuItem>
          Find
          <MOMenu slot="submenu">
            <MOMenuItem value="find">Find…</MOMenuItem>
            <MOMenuItem value="find-previous">Find Next</MOMenuItem>
            <MOMenuItem value="find-next">Find Previous</MOMenuItem>
          </MOMenu>
        </MOMenuItem>
        <MOMenuItem>
          Transformations
          <MOMenu slot="submenu">
            <MOMenuItem value="uppercase">Make uppercase</MOMenuItem>
            <MOMenuItem value="lowercase">Make lowercase</MOMenuItem>
            <MOMenuItem value="capitalize">Capitalize</MOMenuItem>
          </MOMenu>
        </MOMenuItem>
      </MOMenu>
    </MODropdown>
  </>
);

Language selector

You can create a language selector by combining the dropdown with the menu.

<div class="dropdown-language">
  <mo-dropdown>
    <mo-button class="lang-button" variant="secondary" slot="trigger" caret>FI</mo-button>
    <mo-menu>
      <mo-menu-item checked type="checkbox" value="FI">
          Finnish
      </mo-menu-item>
      <mo-menu-item type="checkbox" value="EN">
          English
      </mo-menu-item>
      <mo-menu-item type="checkbox" value="ES">
          Spanish
      </mo-menu-item>
    </mo-menu>
  </mo-dropdown>
  <mo-divider></mo-divider>
  <mo-file-dropzone lang="fi"></mo-file-dropzone>
</div>

<script>
  const container = document.querySelector('.dropdown-language');
  const dropdown = container.querySelector('mo-dropdown');
  const button = container.querySelector('.lang-button');
  const dropzone = container.querySelector('mo-file-dropzone');

  dropdown.addEventListener('mo-select', event => {
    event.preventDefault();
    const selectedItem = event.detail.item;
    dropdown.querySelectorAll('mo-menu-item').forEach(item => (item.checked = false));
    selectedItem.checked = true;
    button.textContent = selectedItem.value;
    dropzone.lang = selectedItem.value.toLowerCase();
  });
</script>

import { MOButton, MODropdown, MOMenu, MOMenuItem } from '@metsooutotec/modes-web-components/dist/react';

const App = () => {
  const [buttonText, setButtonText] = useState('FI');

  function handleSelect(event) {
    const selectedItem = event.detail.item;
    setButtonText(selectedItem.value);
  }

  const css = `
    .flag-container {
      display: flex;
      min-width: 150px;
      justify-content: space-between;
      align-items: center;
    }
  `

  return (
    <div class="dropdown-language">
      <MODropdown>
        <MOButton class="lang-button" variant="secondary" slot="trigger" caret>
          {buttonText}
        </MOButton>
        <MOMenu>
          <MOMenuItem checked type="checkbox" value="FI">
            <span class="flag-container">
              Finnish
            </span>
          </MOMenuItem>
          <MOMenuItem type="checkbox" value="EN">
            <span class="flag-container">
              English
            </span>
          </MOMenuItem>
          <MOMenuItem type="checkbox" value="ES">
            <span class="flag-container">
              Spanish
            </span>
          </MOMenuItem>
        </MOMenu>p
      </MODropdown>
      <MODivider></MODivider>
      <MOFileDropzone lang={buttonText.toLowerCase()}></MOFileDropzone>
    </div>

    <style>{css}</style>
  );
};

Placement

The preferred placement of the dropdown can be set with the placement attribute. Note that the actual position may vary to ensure the panel remains in the viewport.

Edit Cut Copy Paste Find Replace Long title here Cut Copy Paste Find Replace
<mo-dropdown placement="top-start">
  <mo-button slot="trigger" caret>Edit</mo-button>
  <mo-menu>
    <mo-menu-item>Cut</mo-menu-item>
    <mo-menu-item>Copy</mo-menu-item>
    <mo-menu-item>Paste</mo-menu-item>
    <mo-divider></mo-divider>
    <mo-menu-item>Find</mo-menu-item>
    <mo-menu-item>Replace</mo-menu-item>
  </mo-menu>
</mo-dropdown>
<mo-dropdown placement="bottom-end">
  <mo-button slot="trigger" caret>Long title here</mo-button>
  <mo-menu>
    <mo-menu-item>Cut</mo-menu-item>
    <mo-menu-item>Copy</mo-menu-item>
    <mo-menu-item>Paste</mo-menu-item>
    <mo-divider></mo-divider>
    <mo-menu-item>Find</mo-menu-item>
    <mo-menu-item>Replace</mo-menu-item>
  </mo-menu>
</mo-dropdown>
import { MOButton, MODivider, MODropdown, MOMenu, MOMenuItem } from '@metsooutotec/modes-web-components/dist/react';

const App = () => (
  <MODropdown placement="top-start">
    <MOButton slot="trigger" caret>
      Edit
    </MOButton>
    <MOMenu>
      <MOMenuItem>Cut</MOMenuItem>
      <MOMenuItem>Copy</MOMenuItem>
      <MOMenuItem>Paste</MOMenuItem>
      <MODivider />
      <MOMenuItem>Find</MOMenuItem>
      <MOMenuItem>Replace</MOMenuItem>
    </MOMenu>
  </MODropdown>
  <MODropdown placement="bottom-end">
    <MOButton slot="trigger" caret>
      Long title here
    </MOButton>
    <MOMenu>
      <MOMenuItem>Cut</MOMenuItem>
      <MOMenuItem>Copy</MOMenuItem>
      <MOMenuItem>Paste</MOMenuItem>
      <MODivider />
      <MOMenuItem>Find</MOMenuItem>
      <MOMenuItem>Replace</MOMenuItem>
    </MOMenu>
  </MODropdown>
);

Distance

The distance from the panel to the trigger can be customized using the distance attribute. This value is specified in pixels.

Edit Cut Copy Paste Find Replace
<mo-dropdown distance="30">
  <mo-button slot="trigger" caret>Edit</mo-button>
  <mo-menu>
    <mo-menu-item>Cut</mo-menu-item>
    <mo-menu-item>Copy</mo-menu-item>
    <mo-menu-item>Paste</mo-menu-item>
    <mo-divider></mo-divider>
    <mo-menu-item>Find</mo-menu-item>
    <mo-menu-item>Replace</mo-menu-item>
  </mo-menu>
</mo-dropdown>
import { MOButton, MODivider, MODropdown, MOMenu, MOMenuItem } from '@metsooutotec/modes-web-components/dist/react';

const App = () => (
  <MODropdown distance={30}>
    <MOButton slot="trigger" caret>
      Edit
    </MOButton>
    <MOMenu>
      <MOMenuItem>Cut</MOMenuItem>
      <MOMenuItem>Copy</MOMenuItem>
      <MOMenuItem>Paste</MOMenuItem>
      <MODivider />
      <MOMenuItem>Find</MOMenuItem>
      <MOMenuItem>Replace</MOMenuItem>
    </MOMenu>
  </MODropdown>
);

Skidding

The offset of the panel along the trigger can be customized using the skidding attribute. This value is specified in pixels.

Edit Cut Copy Paste Find Replace
<mo-dropdown skidding="30">
  <mo-button slot="trigger" caret>Edit</mo-button>
  <mo-menu>
    <mo-menu-item>Cut</mo-menu-item>
    <mo-menu-item>Copy</mo-menu-item>
    <mo-menu-item>Paste</mo-menu-item>
    <mo-divider></mo-divider>
    <mo-menu-item>Find</mo-menu-item>
    <mo-menu-item>Replace</mo-menu-item>
  </mo-menu>
</mo-dropdown>
import { MOButton, MODivider, MODropdown, MOMenu, MOMenuItem } from '@metsooutotec/modes-web-components/dist/react';

const App = () => (
  <MODropdown skidding={30}>
    <MOButton slot="trigger" caret>
      Edit
    </MOButton>
    <MOMenu>
      <MOMenuItem>Cut</MOMenuItem>
      <MOMenuItem>Copy</MOMenuItem>
      <MOMenuItem>Paste</MOMenuItem>
      <MODivider />
      <MOMenuItem>Find</MOMenuItem>
      <MOMenuItem>Replace</MOMenuItem>
    </MOMenu>
  </MODropdown>
);
Checked Disabled Prefix Suffix Icon
<mo-dropdown>
  <mo-icon-button slot="trigger" name="more"></mo-icon-button>
  <mo-menu>
    <mo-menu-item type="checkbox" checked>Checked</mo-menu-item>
    <mo-menu-item disabled>Disabled</mo-menu-item>
    <mo-divider></mo-divider>
    <mo-menu-item>
      Prefix
      <mo-icon slot="prefix" name="home"></mo-icon>
    </mo-menu-item>
    <mo-menu-item>
      Suffix Icon
      <mo-icon slot="suffix" name="settings"></mo-icon>
    </mo-menu-item>
  </mo-menu>
</mo-dropdown>

Hoisting

Dropdown panels will be clipped if they’re inside a container that has overflow: auto|hidden. The hoist attribute forces the panel to use a fixed positioning strategy, allowing it to break out of the container. In this case, the panel will be positioned relative to its containing block, which is usually the viewport unless an ancestor uses a transform, perspective, or filter. Refer to this page for more details.

<div class="dropdown-hoist">
  <mo-dropdown>
    <mo-button slot="trigger" caret>No Hoist</mo-button>
    <mo-menu>
      <mo-menu-item>Item 1</mo-menu-item>
      <mo-menu-item>Item 2</mo-menu-item>
      <mo-menu-item>Item 3</mo-menu-item>
    </mo-menu>
  </mo-dropdown>

  <mo-dropdown hoist>
    <mo-button slot="trigger" caret>Hoist</mo-button>
    <mo-menu>
      <mo-menu-item>Item 1</mo-menu-item>
      <mo-menu-item>Item 2</mo-menu-item>
      <mo-menu-item>Item 3</mo-menu-item>
    </mo-menu>
  </mo-dropdown>
</div>

<style>
  .dropdown-hoist {
    position: relative;
    border: solid 2px var(--mo-panel-border-color);
    padding: var(--mo-spacing-medium);
    overflow: hidden;
  }
</style>
import {
  MOButton,
  MODivider,
  MODropdown,
  MOIcon,
  MOMenu,
  MOMenuItem
} from '@metsooutotec/modes-web-components/dist/react';

const css = `
  .dropdown-hoist {
    border: solid 2px var(--mo-panel-border-color);
    padding: var(--mo-spacing-medium);
    overflow: hidden;
  }
`;

const App = () => (
  <>
    <div className="dropdown-hoist">
      <MODropdown>
        <MOButton slot="trigger" caret>
          No Hoist
        </MOButton>
        <MOMenu>
          <MOMenuItem>Item 1</MOMenuItem>
          <MOMenuItem>Item 2</MOMenuItem>
          <MOMenuItem>Item 3</MOMenuItem>
        </MOMenu>
      </MODropdown>

      <MODropdown hoist>
        <MOButton slot="trigger" caret>
          Hoist
        </MOButton>
        <MOMenu>
          <MOMenuItem>Item 1</MOMenuItem>
          <MOMenuItem>Item 2</MOMenuItem>
          <MOMenuItem>Item 3</MOMenuItem>
        </MOMenu>
      </MODropdown>
    </div>

    <style>{css}</style>
  </>
);

Importing

If you’re using the autoloader or the traditional loader, you can ignore this section. Otherwise, feel free to use any of the following snippets to cherry pick this component.

Bundler React Script

To import this component using a bundler:

import '@metsooutotec/modes-web-components/dist/components/dropdown/dropdown.js';

To import this component as a React component:

import MODropdown from '@metsooutotec/modes-web-components/dist/react/dropdown/';

To import this component using a script tag:

<script type="module" src="https://modes-web.metso.com/dist/components/cdn/components/dropdown/dropdown.js"></script>

Slots

Name Description
(default) The dropdown’s main content.
trigger The dropdown’s trigger, usually a <mo-button> element.

Learn more about using slots.

Properties

Name Description Reflects Type Default
open Indicates whether or not the dropdown is open. You can toggle this attribute to show and hide the dropdown, or you can use the show() and hide() methods and this attribute will reflect the dropdown’s open state. boolean false
placement The preferred placement of the dropdown panel. Note that the actual placement may vary as needed to keep the panel inside of the viewport. 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'right' | 'right-start' | 'right-end' | 'left' | 'left-start' | 'left-end' 'bottom-start'
disabled Disables the dropdown so the panel will not open. boolean false
stayOpenOnSelect
stay-open-on-select
By default, the dropdown is closed when an item is selected. This attribute will keep it open instead. Useful for dropdowns that allow for multiple interactions. boolean false
containingElement The dropdown will close when the user interacts outside of this element (e.g. clicking). Useful for composing other components that use a dropdown internally. HTMLElement | undefined -
distance The distance in pixels from which to offset the panel away from its trigger. number 2
skidding The distance in pixels from which to offset the panel along its trigger. number 0
disableTriggerKeyboardEvents Disabled default spacebar keyboard interaction. boolean false
hoist Enable this option to prevent the panel from being clipped when the component is placed inside a container with overflow: auto|scroll. Hoisting uses a fixed positioning strategy that works in many, but not all, scenarios. boolean false
updateComplete A read-only promise that resolves when the component has finished updating.

Learn more about attributes and properties.

Events

Name React Event Description Event Detail
mo-show onMoShow Emitted when the dropdown opens. -
mo-after-show onMoAfterShow Emitted after the dropdown opens and all animations are complete. -
mo-hide onMoHide Emitted when the dropdown closes. -
mo-after-hide onMoAfterHide Emitted after the dropdown closes and all animations are complete. -

Learn more about events.

Methods

Name Description Arguments
show() Shows the dropdown panel. -
hide() Hides the dropdown panel -
reposition() Instructs the dropdown menu to reposition. Useful when the position or size of the trigger changes when the menu is activated. -

Learn more about methods.

Parts

Name Description
base The component’s base wrapper.
trigger The container that wraps the trigger.
panel The panel that gets shown when the dropdown is open.

Learn more about customizing CSS parts.

Animations

Name Description
dropdown.show The animation to use when showing the dropdown.
dropdown.hide The animation to use when hiding the dropdown.

Learn more about customizing animations.

Dependencies

This component automatically imports the following dependencies.