Skip to main content
Default Gray Amethyst

Combobox

<mo-combobox> | MOCombobox
Since 1.9 stable

A combo box consists of a list and a selection field. The list presents the options that a user can select, and the selection field displays the current selection.

<mo-combobox></mo-combobox>

Simple combobox

Use the options attribute to give the mo-combobox an array of options. The tree structure will be generated based on this array.

<mo-combobox label="Country" placeholder="Type to search" class="combobox-local" clearable> </mo-combobox>

<script>
  const combobox = document.querySelector('.combobox-local');
  const fetchData = async () => {
    try {
      const response = await fetch('/api/countries');
      if (!response.ok) {
        throw new Error('Network response was not ok');
      }
      const data = await response.json();
      return data;
    } catch (error) {
      console.error('There has been a problem with your fetch operation:', error);
    }
  };
  const setOptions = async () => {
    const data = await fetchData();
    combobox.options = data;
  }
  setOptions();
</script>
import { MOCombobox } from '@metsooutotec/modes-web-components/dist/react';

const countries = [{...}];

const App = () => (
  <>
    <MOCombobox
      label="Country"
      placeholder="Type to search"
      clearable
      closeOnSelection
      options={countries}
    ></MOCombobox>
  </>
);

Hierarchical structure

Supplying the mo-combobox with a list of tree items using the treeItems attribute allows for hierarchical a tree structure inside the dropdown.

<mo-combobox
  label="Celestial objects"
  placeholder="Select variable"
  id="tree-box"
  hierarchical-content
  clearable
></mo-combobox>

<script>
  const treeBox = document.querySelector('#tree-box');
  const treeItems = [
    {
      name: 'Galaxies',
      id: '1',
      children: [
        {
          name: 'Elliptical',
          id: '1-1',
          children: [
            { name: 'IC 1101', id: '1-1-1' },
            { name: 'Hercules A', id: '1-1-2' },
            { name: 'A2261-BCG', id: '1-1-3' },
            { name: 'ESO 306-17', id: '1-1-4' },
            { name: 'ESO 444-46', id: '1-1-5' }
          ]
        },
        {
          name: 'Spiral',
          id: '1-2',
          children: [
            { name: "Rubin's Galaxy", id: '1-2-1' },
            { name: 'Comet Galaxy', id: '1-2-2' },
            { name: 'Condor Galaxy', id: '1-2-3' },
            { name: 'Tadpole Galaxy', id: '1-2-4' },
            { name: 'Andromeda Galaxy', id: '1-2-5' }
          ]
        }
      ]
    },
    {
      name: 'Planets',
      id: '2',
      children: [
        {
          name: 'Sub-Earth',
          id: '2-1',
          children: [
            { name: 'Mars', id: '2-1-1' },
            { name: 'Mercury', id: '2-1-2' }
          ]
        },
        {
          name: 'Giant',
          id: '2-2',
          children: [
            { name: 'Jupiter', id: '2-2-1' },
            { name: 'Saturn', id: '2-2-2' },
            { name: 'Uranus', id: '2-2-3' },
            { name: 'Neptune', id: '2-2-4' }
          ]
        },
        {
          name: 'Exoplanet',
          id: '2-3',
          children: [
            {
              name: 'Potentially habitable',
              id: '2-3-1',
              children: [
                { name: 'Alpha Centauri', id: '2-3-1-1' },
                { name: 'Ross 128', id: '2-3-1-2' },
                { name: 'Wolf 1061', id: '2-3-1-3' }
              ]
            },
            { name: 'Epsilon Eridani', id: '2-3-2' },
            { name: 'YZ Ceti', id: '2-3-4' }
          ]
        }
      ]
    }
  ];
  treeBox.treeItems = treeItems;
</script>
import { MOCombobox } from '@metsooutotec/modes-web-components/dist/react';

const treeItems = [
  {
    name: 'Galaxies',
    id: '1',
    children: [
      {
        name: 'Elliptical',
        id: '1-1',
        children: [
          { name: 'IC 1101', id: '1-1-1' },
          { name: 'Hercules A', id: '1-1-2' },
          { name: 'A2261-BCG', id: '1-1-3' },
          { name: 'ESO 306-17', id: '1-1-4' },
          { name: 'ESO 444-46', id: '1-1-5' }
        ]
      },
      {
        name: 'Spiral',
        id: '1-2',
        children: [
          { name: "Rubin's Galaxy", id: '1-2-1' },
          { name: 'Comet Galaxy', id: '1-2-2' },
          { name: 'Condor Galaxy', id: '1-2-3' },
          { name: 'Tadpole Galaxy', id: '1-2-4' },
          { name: 'Andromeda Galaxy', id: '1-2-5' }
        ]
      }
    ]
  },
  {
    name: 'Planets',
    id: '2',
    children: [
      {
        name: 'Sub-Earth',
        id: '2-1',
        children: [
          { name: 'Mars', id: '2-1-1' },
          { name: 'Mercury', id: '2-1-2' }
        ]
      },
      {
        name: 'Giant',
        id: '2-2',
        children: [
          { name: 'Jupiter', id: '2-2-1' },
          { name: 'Saturn', id: '2-2-2' },
          { name: 'Uranus', id: '2-2-3' },
          { name: 'Neptune', id: '2-2-4' }
        ]
      },
      {
        name: 'Exoplanet',
        id: '2-3',
        children: [
          {
            name: 'Potentially habitable',
            id: '2-3-1',
            children: [
              { name: 'Alpha Centauri', id: '2-3-1-1' },
              { name: 'Ross 128', id: '2-3-1-2' },
              { name: 'Wolf 1061', id: '2-3-1-3' }
            ]
          },
          { name: 'Epsilon Eridani', id: '2-3-2' },
          { name: 'YZ Ceti', id: '2-3-4' }
        ]
      }
    ]
  }
];

const App = () => (
  <>
    <MOCombobox label="Celestial objects" placeholder="Select variable" clearable treeItems={treeItems}></MOCombobox>
  </>
);

Multiple selection

Setting the attribute selection-mode to multiple will add checkboxes to the mo-tree that will allow multiple nodes to be selected simultaneously.

<mo-combobox
  label="Celestial objects"
  placeholder="Select variable(s)"
  id="tree-box-multiple"
  clearable
  help-text="Multiple nodes can be selected."
  selection-mode="multiple"
></mo-combobox>
<script>
  const treeBox = document.querySelector('#tree-box-multiple');
  const treeItems = [
    {
      id: '1',
      name: 'Galaxies',
      children: [
        {
          name: 'Elliptical',
          id: '1-1',
          children: [
            { name: 'IC 1101', id: '1-1-1', favorite: true },
            { name: 'Hercules A', id: '1-1-2' },
            { name: 'A2261-BCG', id: '1-1-3', favorite: true },
            { name: 'ESO 306-17', id: '1-1-4' },
            { name: 'ESO 444-46', id: '1-1-5' }
          ]
        },
        {
          name: 'Spiral',
          id: '1-2',
          children: [
            { name: "Rubin's Galaxy", id: '1-2-1', favorite: true },
            { name: 'Comet Galaxy', id: '1-2-2' },
            { name: 'Condor Galaxy', id: '1-2-3' },
            { name: 'Tadpole Galaxy', id: '1-2-4' },
            { name: 'Andromeda Galaxy', id: '1-2-5', favorite: true }
          ]
        }
      ]
    },
    {
      name: 'Planets',
      id: '2',
      children: [
        {
          name: 'Sub-Earth',
          id: '2-1',
          children: [
            { name: 'Mars', id: '2-1-1' },
            { name: 'Mercury', id: '2-1-2', favorite: true }
          ]
        },
        {
          name: 'Giant',
          id: '2-2',
          children: [
            { name: 'Jupiter', id: '2-2-1' },
            { name: 'Saturn', id: '2-2-2' },
            { name: 'Uranus', id: '2-2-3', favorite: true },
            { name: 'Neptune', id: '2-2-4' }
          ]
        },
        {
          name: 'Exoplanet',
          id: '2-3',
          children: [
            {
              name: 'Potentially habitable',
              id: '2-3-1',
              children: [
                { name: 'Alpha Centauri', id: '2-3-1-1' },
                { name: 'Ross 128', id: '2-3-1-2', favorite: true },
                { name: 'Wolf 1061', id: '2-3-1-3' }
              ]
            },
            { name: 'Epsilon Eridani', id: '2-3-2' },
            { name: 'YZ Ceti', id: '2-3-4' }
          ]
        }
      ]
    }
  ];
  treeBox.treeItems = treeItems;
</script>
import { MOCombobox } from '@metsooutotec/modes-web-components/dist/react';

const treeItems = [
  {
    name: 'Galaxies',
    id: '1',
    children: [
      {
        name: 'Elliptical',
        id: '1-1',
        children: [
          { name: 'IC 1101', id: '1-1-1' },
          { name: 'Hercules A', id: '1-1-2' },
          { name: 'A2261-BCG', id: '1-1-3' },
          { name: 'ESO 306-17', id: '1-1-4' },
          { name: 'ESO 444-46', id: '1-1-5' }
        ]
      },
      {
        name: 'Spiral',
        id: '1-2',
        children: [
          { name: "Rubin's Galaxy", id: '1-2-1' },
          { name: 'Comet Galaxy', id: '1-2-2' },
          { name: 'Condor Galaxy', id: '1-2-3' },
          { name: 'Tadpole Galaxy', id: '1-2-4' },
          { name: 'Andromeda Galaxy', id: '1-2-5' }
        ]
      }
    ]
  },
  {
    name: 'Planets',
    id: '2',
    children: [
      {
        name: 'Sub-Earth',
        id: '2-1',
        children: [
          { name: 'Mars', id: '2-1-1' },
          { name: 'Mercury', id: '2-1-2' }
        ]
      },
      {
        name: 'Giant',
        id: '2-2',
        children: [
          { name: 'Jupiter', id: '2-2-1' },
          { name: 'Saturn', id: '2-2-2' },
          { name: 'Uranus', id: '2-2-3' },
          { name: 'Neptune', id: '2-2-4' }
        ]
      },
      {
        name: 'Exoplanet',
        id: '2-3',
        children: [
          {
            name: 'Potentially habitable',
            id: '2-3-1',
            children: [
              { name: 'Alpha Centauri', id: '2-3-1-1' },
              { name: 'Ross 128', id: '2-3-1-2' },
              { name: 'Wolf 1061', id: '2-3-1-3' }
            ]
          },
          { name: 'Epsilon Eridani', id: '2-3-2' },
          { name: 'YZ Ceti', id: '2-3-4' }
        ]
      }
    ]
  }
];

const App = () => (
  <>
    <MOCombobox
      label="Celestial objects"
      placeholder="Select variable"
      clearable
      selection-mode="single"
      treeItems={treeItems}
    ></MOCombobox>
  </>
);

Show path first

Enabling the attribute show-path-first offers an alternate way of displaying the paths to variables shown in manual search queries, in the favorites, and in the input value field.

<mo-combobox
  label="Celestial objects"
  placeholder="Select variable"
  id="tree-path"
  favorites
  show-path-first
  clearable
  separator="&gt;"
></mo-combobox>

<script>
  const treeBox = document.querySelector('#tree-path');

  const treeItems = [
    {
      name: 'Galaxies',
      id: '1',
      children: [
        {
          name: 'Elliptical',
          id: '1-1',
          children: [
            { name: 'IC 1101', id: '1-1-1', favorite: true },
            { name: 'Hercules A', id: '1-1-2' },
            { name: 'A2261-BCG', id: '1-1-3', favorite: true },
            { name: 'ESO 306-17', id: '1-1-4' },
            { name: 'ESO 444-46', id: '1-1-5' }
          ]
        },
        {
          name: 'Spiral',
          id: '1-2',
          children: [
            { name: "Rubin's Galaxy", id: '1-2-1', favorite: true },
            { name: 'Comet Galaxy', id: '1-2-2' },
            { name: 'Condor Galaxy', id: '1-2-3' },
            { name: 'Tadpole Galaxy', id: '1-2-4' },
            { name: 'Andromeda Galaxy', id: '1-2-5', favorite: true }
          ]
        }
      ]
    },
    {
      name: 'Planets',
      id: '2',
      children: [
        {
          name: 'Sub-Earth',
          id: '2-1',
          children: [
            { name: 'Mars', id: '2-1-1' },
            { name: 'Mercury', id: '2-1-2', favorite: true }
          ]
        },
        {
          name: 'Giant',
          id: '2-2',
          children: [
            { name: 'Jupiter', id: '2-2-1' },
            { name: 'Saturn', id: '2-2-2' },
            { name: 'Uranus', id: '2-2-3', favorite: true },
            { name: 'Neptune', id: '2-2-4' }
          ]
        },
        {
          name: 'Exoplanet',
          id: '2-3',
          children: [
            {
              name: 'Potentially habitable',
              id: '2-3-1',
              children: [
                { name: 'Alpha Centauri', id: '2-3-1-1' },
                { name: 'Ross 128', id: '2-3-1-2', favorite: true },
                { name: 'Wolf 1061', id: '2-3-1-3' }
              ]
            },
            { name: 'Epsilon Eridani', id: '2-3-2' },
            { name: 'YZ Ceti', id: '2-3-4' }
          ]
        }
      ]
    }
  ];
  treeBox.treeItems = treeItems;
</script>
import { MOCombobox } from '@metsooutotec/modes-web-components/dist/react';

const treeItems = [
  {
    name: 'Galaxies',
    id: '1',
    children: [
      {
        name: 'Elliptical',
        id: '1-1',
        children: [
          { name: 'IC 1101', id: '1-1-1', favorite: true },
          { name: 'Hercules A', id: '1-1-2' },
          { name: 'A2261-BCG', id: '1-1-3', favorite: true },
          { name: 'ESO 306-17', id: '1-1-4' },
          { name: 'ESO 444-46', id: '1-1-5' }
        ]
      },
      {
        name: 'Spiral',
        id: '1-2',
        children: [
          { name: "Rubin's Galaxy", id: '1-2-1', favorite: true },
          { name: 'Comet Galaxy', id: '1-2-2' },
          { name: 'Condor Galaxy', id: '1-2-3' },
          { name: 'Tadpole Galaxy', id: '1-2-4' },
          { name: 'Andromeda Galaxy', id: '1-2-5', favorite: true }
        ]
      }
    ]
  },
  {
    name: 'Planets',
    id: '2',
    children: [
      {
        name: 'Sub-Earth',
        id: '2-1',
        children: [
          { name: 'Mars', id: '2-1-1' },
          { name: 'Mercury', id: '2-1-2', favorite: true }
        ]
      },
      {
        name: 'Giant',
        id: '2-2',
        children: [
          { name: 'Jupiter', id: '2-2-1' },
          { name: 'Saturn', id: '2-2-2' },
          { name: 'Uranus', id: '2-2-3', favorite: true },
          { name: 'Neptune', id: '2-2-4' }
        ]
      },
      {
        name: 'Exoplanet',
        id: '2-3',
        children: [
          {
            name: 'Potentially habitable',
            id: '2-3-1',
            children: [
              { name: 'Alpha Centauri', id: '2-3-1-1' },
              { name: 'Ross 128', id: '2-3-1-2', favorite: true },
              { name: 'Wolf 1061', id: '2-3-1-3' }
            ]
          },
          { name: 'Epsilon Eridani', id: '2-3-2' },
          { name: 'YZ Ceti', id: '2-3-4' }
        ]
      }
    ]
  }
];

const App = () => (
  <>
    <MOCombobox
      label="Celestial objects"
      placeholder="Select variable"
      clearable
      treeItems={treeItems}
      separator="&gt;"
    ></MOCombobox>
  </>
);

Controlled and initial selection

You can set the value attribute to a node id for single selection, and to a space-separated string of id’s for the multiple selection (as attribute, HTML), or an array of strings (as property, JavaScript/React).

Note that for multiple selection, only leaf nodes are included in the value.

selected nodes:
<div>
  <mo-combobox
    style="flex: 1 1 auto;"
    label="Celestial objects"
    placeholder="Select variable"
    selection-mode="single"
    value="1-1-2"
    id="tree-controlled"
    help-text="Choose one option"
    clearable
  ></mo-combobox>
  <mo-input
    help-text="Input node id to be selected here."
    id="node-id-input"
    placeholder="Example: 2-3-1-2"
    value="1-1-2"
    label="value"
  ></mo-input>
</div>
<mo-divider></mo-divider>
<div>
  <mo-combobox
    style="flex: 1 1 auto;"
    label="Celestial objects"
    placeholder="Select variable"
    id="tree-controlled-multiple"
    help-text="Multiple options can be selected"
    value="2-1-1 1-1-2"
    selection-mode="multiple"
    clearable
  ></mo-combobox>
  <mo-input
    id="node-id-input-multiple"
    value="2-1-1 1-1-2"
    help-text="Input node id to be selected here."
    placeholder="Example: 2-1-1 1-1-2"
    label="value"
  ></mo-input>
</div>
<pre id="pre">selected nodes:</pre>

<script>
  const treeBox = document.querySelector('#tree-controlled');
  const treeBoxMulti = document.querySelector('#tree-controlled-multiple');
  const input = document.querySelector('#node-id-input');
  const inputMulti = document.querySelector('#node-id-input-multiple');
  const pre = document.querySelector('#pre');
  const treeItems = [
    {
      name: 'Galaxies',
      id: '1',
      children: [
        {
          name: 'Elliptical',
          id: '1-1',
          children: [
            { name: 'IC 1101', id: '1-1-1' },
            { name: 'Hercules A', id: '1-1-2', selected: true },
            { name: 'A2261-BCG', id: '1-1-3' },
            { name: 'ESO 306-17', id: '1-1-4' },
            { name: 'ESO 444-46', id: '1-1-5' }
          ]
        },
        {
          name: 'Spiral',
          id: '1-2',
          children: [
            { name: "Rubin's Galaxy", id: '1-2-1' },
            { name: 'Comet Galaxy', id: '1-2-2' },
            { name: 'Condor Galaxy', id: '1-2-3' },
            { name: 'Tadpole Galaxy', id: '1-2-4' },
            { name: 'Andromeda Galaxy', id: '1-2-5' }
          ]
        }
      ]
    },
    {
      name: 'Planets',
      id: '2',
      children: [
        {
          name: 'Sub-Earth',
          id: '2-1',
          children: [
            { name: 'Mars', id: '2-1-1' },
            { name: 'Mercury', id: '2-1-2' }
          ]
        },
        {
          name: 'Giant',
          id: '2-2',
          children: [
            { name: 'Jupiter', id: '2-2-1' },
            { name: 'Saturn', id: '2-2-2' },
            { name: 'Uranus', id: '2-2-3' },
            { name: 'Neptune', id: '2-2-4' }
          ]
        },
        {
          name: 'Exoplanet',
          id: '2-3',
          children: [
            {
              name: 'Potentially habitable',
              id: '2-3-1',
              children: [
                { name: 'Alpha Centauri', id: '2-3-1-1' },
                { name: 'Ross 128', id: '2-3-1-2' },
                { name: 'Wolf 1061', id: '2-3-1-3' }
              ]
            },
            { name: 'Epsilon Eridani', id: '2-3-2' },
            { name: 'YZ Ceti', id: '2-3-4' }
          ]
        }
      ]
    }
  ];

  treeBox.treeItems = treeItems;
  treeBoxMulti.treeItems = treeItems;
  treeBoxMulti.addEventListener('mo-selection-change', e => {
    pre.textContent = 'selected nodes: ' + treeBoxMulti.value;
  });
  input.addEventListener('mo-input', () => {
    treeBox.value = input.value;
  });
  inputMulti.addEventListener('mo-input', () => {
    treeBoxMulti.value = inputMulti.value;
  });
</script>
import { MOCombobox, MODivider, MOInput, MOButton } from '@metsooutotec/modes-web-components/dist/react';

const treeItems = [
  {
    name: 'Galaxies',
    id: '1',
    children: [
      {
        name: 'Elliptical',
        id: '1-1',
        children: [
          { name: 'IC 1101', id: '1-1-1' },
          { name: 'Hercules A', id: '1-1-2' },
          { name: 'A2261-BCG', id: '1-1-3' },
          { name: 'ESO 306-17', id: '1-1-4' },
          { name: 'ESO 444-46', id: '1-1-5' }
        ]
      },
      {
        name: 'Spiral',
        id: '1-2',
        children: [
          { name: "Rubin's Galaxy", id: '1-2-1' },
          { name: 'Comet Galaxy', id: '1-2-2' },
          { name: 'Condor Galaxy', id: '1-2-3' },
          { name: 'Tadpole Galaxy', id: '1-2-4' },
          { name: 'Andromeda Galaxy', id: '1-2-5' }
        ]
      }
    ]
  },
  {
    name: 'Planets',
    id: '2',
    children: [
      {
        name: 'Sub-Earth',
        id: '2-1',
        children: [
          { name: 'Mars', id: '2-1-1' },
          { name: 'Mercury', id: '2-1-2' }
        ]
      },
      {
        name: 'Giant',
        id: '2-2',
        children: [
          { name: 'Jupiter', id: '2-2-1' },
          { name: 'Saturn', id: '2-2-2' },
          { name: 'Uranus', id: '2-2-3' },
          { name: 'Neptune', id: '2-2-4' }
        ]
      },
      {
        name: 'Exoplanet',
        id: '2-3',
        children: [
          {
            name: 'Potentially habitable',
            id: '2-3-1',
            children: [
              { name: 'Alpha Centauri', id: '2-3-1-1' },
              { name: 'Ross 128', id: '2-3-1-2' },
              { name: 'Wolf 1061', id: '2-3-1-3' }
            ]
          },
          { name: 'Epsilon Eridani', id: '2-3-2' },
          { name: 'YZ Ceti', id: '2-3-4' }
        ]
      }
    ]
  }
];

const inputRef = useRef(null);
const cbRef = useRef(null);

const setNode = () => {
  cbRef.current.selectNode(cbRef.current.getNodeById(inputRef.value));
};

const App = () => (
  <>
    <MOCombobox
      label="Celestial objects"
      placeholder="Select variable"
      clearable
      ref={cbRef}
      treeItems={treeItems}
    ></MOCombobox>
    <MODivider></MODivider>
    <MOInput ref={inputRef} placeholder="Example: 2-3-1-2" label="Node id"></MOInput>
    <br />
    <MOButton onClick={setNode}>Select node</MOButton>
  </>
);

Form submission

If the required attribute is set to true, the combobox will stop form submission if no value has been selected. Check the example below on how to extract the current value on form submission.



Submit
submitted data will be here...
<form class="form" id="req-form">
  <mo-combobox
    name="combobox-simple"
    label="Countries"
    placeholder="Select country"
    id="simple-form"
    required
    close-on-selection
    clearable
  ></mo-combobox>
  <br />
  <mo-combobox
    name="combobox-tree"
    label="Celestial objects"
    placeholder="Select object"
    id="tree-form"
    required
    clearable
  ></mo-combobox>
  <br />
  <mo-button type="submit" id="submit-btn">Submit</mo-button>
  <mo-divider></mo-divider>
  <pre id="data">submitted data will be here...</pre>
</form>

<script>
  const treeBox = document.querySelector('#tree-form');
  const simpleBox = document.querySelector('#simple-form');
  const btn = document.querySelector('#submit-btn');
  const countries = [
    {
      text: 'Finland',
      value: 'finland'
    },
    {
      text: 'Sweden',
      value: 'sweden'
    },
    {
      text: 'Germany',
      value: 'germany'
    },
    {
      text: 'Denmark',
      value: 'denmark'
    },
    {
      text: 'Norway',
      value: 'norway'
    }
  ];
  const treeItems = [
    {
      name: 'Galaxies',
      id: '1',
      children: [
        {
          name: 'Elliptical',
          id: '1-1',
          children: [
            { name: 'IC 1101', id: '1-1-1' },
            { name: 'Hercules A', id: '1-1-2' },
            { name: 'A2261-BCG', id: '1-1-3' },
            { name: 'ESO 306-17', id: '1-1-4' },
            { name: 'ESO 444-46', id: '1-1-5' }
          ]
        },
        {
          name: 'Spiral',
          id: '1-2',
          children: [
            { name: "Rubin's Galaxy", id: '1-2-1' },
            { name: 'Comet Galaxy', id: '1-2-2' },
            { name: 'Condor Galaxy', id: '1-2-3' },
            { name: 'Tadpole Galaxy', id: '1-2-4' },
            { name: 'Andromeda Galaxy', id: '1-2-5' }
          ]
        }
      ]
    },
    {
      name: 'Planets',
      id: '2',
      children: [
        {
          name: 'Sub-Earth',
          id: '2-1',
          children: [
            { name: 'Mars', id: '2-1-1' },
            { name: 'Mercury', id: '2-1-2' }
          ]
        },
        {
          name: 'Giant',
          id: '2-2',
          children: [
            { name: 'Jupiter', id: '2-2-1' },
            { name: 'Saturn', id: '2-2-2' },
            { name: 'Uranus', id: '2-2-3' },
            { name: 'Neptune', id: '2-2-4' }
          ]
        },
        {
          name: 'Exoplanet',
          id: '2-3',
          children: [
            {
              name: 'Potentially habitable',
              id: '2-3-1',
              children: [
                { name: 'Alpha Centauri', id: '2-3-1-1' },
                { name: 'Ross 128', id: '2-3-1-2' },
                { name: 'Wolf 1061', id: '2-3-1-3' }
              ]
            },
            { name: 'Epsilon Eridani', id: '2-3-2' },
            { name: 'YZ Ceti', id: '2-3-4' }
          ]
        }
      ]
    }
  ];
  treeBox.treeItems = treeItems;
  simpleBox.options = countries;
  const form = document.querySelector('#req-form');
  const output = document.querySelector('#data');
  form.addEventListener('submit', e => {
    e.preventDefault();
    const formData = new FormData(form);
    console.log(formData);
    var object = {};
    formData.forEach((value, key) => (object[key] = value));
    var json = JSON.stringify(object, null, 2);
    output.innerHTML = json;
  });
</script>
import { MOCombobox, MODivider, MOButton } from '@metsooutotec/modes-web-components/dist/react';

const countries = [
  {
    text: 'Finland',
    value: 'finland'
  },
  {
    text: 'Sweden',
    value: 'sweden'
  },
  {
    text: 'Germany',
    value: 'germany'
  },
  {
    text: 'Denmark',
    value: 'denmark'
  },
  {
    text: 'Norway',
    value: 'norway'
  }
];

const treeItems = [
  {
    name: 'Galaxies',
    id: '1',
    children: [
      {
        name: 'Elliptical',
        id: '1-1',
        children: [
          { name: 'IC 1101', id: '1-1-1' },
          { name: 'Hercules A', id: '1-1-2' },
          { name: 'A2261-BCG', id: '1-1-3' },
          { name: 'ESO 306-17', id: '1-1-4' },
          { name: 'ESO 444-46', id: '1-1-5' }
        ]
      },
      {
        name: 'Spiral',
        id: '1-2',
        children: [
          { name: "Rubin's Galaxy", id: '1-2-1' },
          { name: 'Comet Galaxy', id: '1-2-2' },
          { name: 'Condor Galaxy', id: '1-2-3' },
          { name: 'Tadpole Galaxy', id: '1-2-4' },
          { name: 'Andromeda Galaxy', id: '1-2-5' }
        ]
      }
    ]
  },
  {
    name: 'Planets',
    id: '2',
    children: [
      {
        name: 'Sub-Earth',
        id: '2-1',
        children: [
          { name: 'Mars', id: '2-1-1' },
          { name: 'Mercury', id: '2-1-2' }
        ]
      },
      {
        name: 'Giant',
        id: '2-2',
        children: [
          { name: 'Jupiter', id: '2-2-1' },
          { name: 'Saturn', id: '2-2-2' },
          { name: 'Uranus', id: '2-2-3' },
          { name: 'Neptune', id: '2-2-4' }
        ]
      },
      {
        name: 'Exoplanet',
        id: '2-3',
        children: [
          {
            name: 'Potentially habitable',
            id: '2-3-1',
            children: [
              { name: 'Alpha Centauri', id: '2-3-1-1' },
              { name: 'Ross 128', id: '2-3-1-2' },
              { name: 'Wolf 1061', id: '2-3-1-3' }
            ]
          },
          { name: 'Epsilon Eridani', id: '2-3-2' },
          { name: 'YZ Ceti', id: '2-3-4' }
        ]
      }
    ]
  }
];

// Form submission logic here...

const App = () => (
  <>
    <form class="form" id="req-form">
      <MOCombobox
        name="combobox-simple"
        label="Countries"
        placeholder="Select country"
        id="simple-form"
        required
        closeOnSelection
        clearable
      ></MOCombobox>
      <br />
      <MOCombobox
        name="combobox-tree"
        label="Celestial objects"
        placeholder="Select object"
        id="tree-form"
        required
        clearable
      ></MOCombobox>
      <br />
      <MOButton type="submit" id="submit-btn">
        Submit
      </MOButton>
      <MODivider></MODivider>
      <pre id="data">submitted data will be here...</pre>
    </form>
  </>
);

With favorites

Enabling the attribute favorites will allow users to select favorites in the tree, which will be shown as a top category. Each node in the data tree can have an optional favorite property, if set to true that node will be a favorite. This way favorites can easily be saved in the backend on a per-user basis.

<mo-combobox
  label="Celestial objects"
  placeholder="Select variable"
  id="tree-box-favorites"
  prevent-automatic-expanding
  clearable
  favorites
></mo-combobox>

<script>
  const treeBox = document.querySelector('#tree-box-favorites');
  const treeItems = [
    {
      name: 'Galaxies',
      id: '1',
      children: [
        {
          name: 'Elliptical',
          id: '1-1',
          children: [
            { name: 'IC 1101', id: '1-1-1', favorite: true },
            { name: 'Hercules A', id: '1-1-2' },
            { name: 'A2261-BCG', id: '1-1-3', favorite: true },
            { name: 'ESO 306-17', id: '1-1-4' },
            { name: 'ESO 444-46', id: '1-1-5' }
          ]
        },
        {
          name: 'Spiral',
          id: '1-2',
          children: [
            { name: "Rubin's Galaxy", id: '1-2-1', favorite: true },
            { name: 'Comet Galaxy', id: '1-2-2' },
            { name: 'Condor Galaxy', id: '1-2-3' },
            { name: 'Tadpole Galaxy', id: '1-2-4' },
            { name: 'Andromeda Galaxy', id: '1-2-5', favorite: true }
          ]
        }
      ]
    },
    {
      name: 'Planets',
      id: '2',
      children: [
        {
          name: 'Sub-Earth',
          id: '2-1',
          children: [
            { name: 'Mars', id: '2-1-1' },
            { name: 'Mercury', id: '2-1-2', favorite: true }
          ]
        },
        {
          name: 'Giant',
          id: '2-2',
          children: [
            { name: 'Jupiter', id: '2-2-1' },
            { name: 'Saturn', id: '2-2-2' },
            { name: 'Uranus', id: '2-2-3', favorite: true },
            { name: 'Neptune', id: '2-2-4' }
          ]
        },
        {
          name: 'Exoplanet',
          id: '2-3',
          children: [
            {
              name: 'Potentially habitable',
              id: '2-3-1',
              children: [
                { name: 'Alpha Centauri', id: '2-3-1-1' },
                { name: 'Ross 128', id: '2-3-1-2', favorite: true },
                { name: 'Wolf 1061', id: '2-3-1-3' }
              ]
            },
            { name: 'Epsilon Eridani', id: '2-3-2' },
            { name: 'YZ Ceti', id: '2-3-4' }
          ]
        }
      ]
    }
  ];
  treeBox.treeItems = treeItems;
</script>
import { MOCombobox } from '@metsooutotec/modes-web-components/dist/react';

const treeItems = [
  {
    name: 'Galaxies',
    id: '1',
    children: [
      {
        name: 'Elliptical',
        id: '1-1',
        children: [
          { name: 'IC 1101', id: '1-1-1', favorite: true },
          { name: 'Hercules A', id: '1-1-2' },
          { name: 'A2261-BCG', id: '1-1-3', favorite: true },
          { name: 'ESO 306-17', id: '1-1-4' },
          { name: 'ESO 444-46', id: '1-1-5' }
        ]
      },
      {
        name: 'Spiral',
        id: '1-2',
        children: [
          { name: "Rubin's Galaxy", id: '1-2-1', favorite: true },
          { name: 'Comet Galaxy', id: '1-2-2' },
          { name: 'Condor Galaxy', id: '1-2-3' },
          { name: 'Tadpole Galaxy', id: '1-2-4' },
          { name: 'Andromeda Galaxy', id: '1-2-5', favorite: true }
        ]
      }
    ]
  },
  {
    name: 'Planets',
    id: '2',
    children: [
      {
        name: 'Sub-Earth',
        id: '2-1',
        children: [
          { name: 'Mars', id: '2-1-1' },
          { name: 'Mercury', id: '2-1-2', favorite: true }
        ]
      },
      {
        name: 'Giant',
        id: '2-2',
        children: [
          { name: 'Jupiter', id: '2-2-1' },
          { name: 'Saturn', id: '2-2-2' },
          { name: 'Uranus', id: '2-2-3', favorite: true },
          { name: 'Neptune', id: '2-2-4' }
        ]
      },
      {
        name: 'Exoplanet',
        id: '2-3',
        children: [
          {
            name: 'Potentially habitable',
            id: '2-3-1',
            children: [
              { name: 'Alpha Centauri', id: '2-3-1-1' },
              { name: 'Ross 128', id: '2-3-1-2', favorite: true },
              { name: 'Wolf 1061', id: '2-3-1-3' }
            ]
          },
          { name: 'Epsilon Eridani', id: '2-3-2' },
          { name: 'YZ Ceti', id: '2-3-4' }
        ]
      }
    ]
  }
];

const App = () => (
  <>
    <MOCombobox
      label="Celestial objects"
      placeholder="Select variable"
      clearable
      favorites
      treeItems={treeItems}
    ></MOCombobox>
  </>
);

Selectable categories

The attribute selection-mode dictates how the selection is applied when clicking on the tree items inside the tree. By default it is set to leaf, where only leaf nodes are selectable. Setting it to single allows categories to be selectable as well.

<mo-combobox
  label="Celestial objects"
  placeholder="Select variable"
  id="tree-box-selection"
  clearable
  required
  help-text="Categories can be selected as well."
  selection-mode="single"
></mo-combobox>
<script>
  const treeBox = document.querySelector('#tree-box-selection');
  const treeItems = [
    {
      name: 'Galaxies',
      id: '1',
      children: [
        {
          name: 'Elliptical',
          id: '1-1',
          children: [
            { name: 'IC 1101', id: '1-1-1' },
            { name: 'Hercules A', id: '1-1-2' },
            { name: 'A2261-BCG', id: '1-1-3' },
            { name: 'ESO 306-17', id: '1-1-4' },
            { name: 'ESO 444-46', id: '1-1-5' }
          ]
        },
        {
          name: 'Spiral',
          id: '1-2',
          children: [
            { name: "Rubin's Galaxy", id: '1-2-1' },
            { name: 'Comet Galaxy', id: '1-2-2' },
            { name: 'Condor Galaxy', id: '1-2-3' },
            { name: 'Tadpole Galaxy', id: '1-2-4' },
            { name: 'Andromeda Galaxy', id: '1-2-5' }
          ]
        }
      ]
    },
    {
      name: 'Planets',
      id: '2',
      children: [
        {
          name: 'Sub-Earth',
          id: '2-1',
          children: [
            { name: 'Mars', id: '2-1-1' },
            { name: 'Mercury', id: '2-1-2' }
          ]
        },
        {
          name: 'Giant',
          id: '2-2',
          children: [
            { name: 'Jupiter', id: '2-2-1' },
            { name: 'Saturn', id: '2-2-2' },
            { name: 'Uranus', id: '2-2-3' },
            { name: 'Neptune', id: '2-2-4' }
          ]
        },
        {
          name: 'Exoplanet',
          id: '2-3',
          children: [
            {
              name: 'Potentially habitable',
              id: '2-3-1',
              children: [
                { name: 'Alpha Centauri', id: '2-3-1-1' },
                { name: 'Ross 128', id: '2-3-1-2' },
                { name: 'Wolf 1061', id: '2-3-1-3' }
              ]
            },
            { name: 'Epsilon Eridani', id: '2-3-2' },
            { name: 'YZ Ceti', id: '2-3-4' }
          ]
        }
      ]
    }
  ];
  treeBox.treeItems = treeItems;
</script>
import { MOCombobox } from '@metsooutotec/modes-web-components/dist/react';

const treeItems = [
  {
    name: 'Galaxies',
    id: '1',
    children: [
      {
        name: 'Elliptical',
        id: '1-1',
        children: [
          { name: 'IC 1101', id: '1-1-1' },
          { name: 'Hercules A', id: '1-1-2' },
          { name: 'A2261-BCG', id: '1-1-3' },
          { name: 'ESO 306-17', id: '1-1-4' },
          { name: 'ESO 444-46', id: '1-1-5' }
        ]
      },
      {
        name: 'Spiral',
        id: '1-2',
        children: [
          { name: "Rubin's Galaxy", id: '1-2-1' },
          { name: 'Comet Galaxy', id: '1-2-2' },
          { name: 'Condor Galaxy', id: '1-2-3' },
          { name: 'Tadpole Galaxy', id: '1-2-4' },
          { name: 'Andromeda Galaxy', id: '1-2-5' }
        ]
      }
    ]
  },
  {
    name: 'Planets',
    id: '2',
    children: [
      {
        name: 'Sub-Earth',
        id: '2-1',
        children: [
          { name: 'Mars', id: '2-1-1' },
          { name: 'Mercury', id: '2-1-2' }
        ]
      },
      {
        name: 'Giant',
        id: '2-2',
        children: [
          { name: 'Jupiter', id: '2-2-1' },
          { name: 'Saturn', id: '2-2-2' },
          { name: 'Uranus', id: '2-2-3' },
          { name: 'Neptune', id: '2-2-4' }
        ]
      },
      {
        name: 'Exoplanet',
        id: '2-3',
        children: [
          {
            name: 'Potentially habitable',
            id: '2-3-1',
            children: [
              { name: 'Alpha Centauri', id: '2-3-1-1' },
              { name: 'Ross 128', id: '2-3-1-2' },
              { name: 'Wolf 1061', id: '2-3-1-3' }
            ]
          },
          { name: 'Epsilon Eridani', id: '2-3-2' },
          { name: 'YZ Ceti', id: '2-3-4' }
        ]
      }
    ]
  }
];

const App = () => (
  <>
    <MOCombobox
      label="Celestial objects"
      placeholder="Select variable"
      clearable
      required
      selection-mode="single"
      treeItems={treeItems}
    ></MOCombobox>
  </>
);

Virtualization (stress test)

The mo-combobox can handle massive trees by only loading items in to the DOM once they are visible and needed. This behavior can be enabled by using the virtualization attribute.

This example has 37 = 2 187 nodes.
<mo-combobox
  label="Celestial objects"
  placeholder="Select variable"
  id="tree-box-tons"
  virtualization
  clearable
></mo-combobox>
This example has 3<sup>7</sup> = 2 187 nodes.
<script>
  const treeBox = document.querySelector('#tree-box-tons');
  function generateItems(depth) {
    function getRandomInt(max) {
      return Math.ceil(Math.random() * max);
    }
    function generateId() {
      return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
        (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16)
      );
    }

    const items = [];
    if (depth < 7) {
      for (let i = 0; i < 3; i++) {
        items.push({
          name: `item ${getRandomInt(1000)}`,
          id: generateId(),
          children: generateItems(depth + 1)
        });
      }
    }
    return items;
  }
  const trees = generateItems(0);
  treeBox.treeItems = trees;
</script>
import { MOCombobox } from '@metsooutotec/modes-web-components/dist/react';

function generateItems(depth) {
  function getRandomInt(max) {
    return Math.ceil(Math.random() * max);
  }
  function generateId() {
    return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
      (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16)
    );
  }

  const items = [];
  if (depth < 7) {
    for (let i = 0; i < 3; i++) {
      items.push({
        name: `item ${getRandomInt(1000)}`,
        id: generateId(),
        children: generateItems(depth + 1)
      });
    }
  }
  return items;
}
const trees = generateItems(0);

const App = () => (
  <>
    <MOCombobox label="Celestial objects" placeholder="Select variable" clearable virtualization treeItems={trees}></MOCombobox>
    This example has 3<sup>10</sup> = 59 094 nodes.
  </>
);

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/combobox/combobox.js';

To import this component as a React component:

import MOCombobox from '@metsooutotec/modes-web-components/dist/react/combobox/';

To import this component using a script tag:

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

Properties

Name Description Reflects Type Default
input The input element. MOInput -
hiddenInput The hidden input element. HTMLInputElement -
dropdown The dropdown element. MODropdown -
menu The menu element. MOMenu -
tree The tree element. MOTree -
treeItemNodes List of all tree item nodes. MOTreeItem[] -
suggestions Options filtered based on search query. { text: string; value: string }[] | undefined undefined
filteredTreeItems Tree items that have been filtered based on search query. TreeNode[] | undefined undefined
favoriteNodes List of all tree items that have been favorited and will be rendered in their own group. TreeNode[] []
selectedNode The currently selected node (in tree structure). TreeNode | undefined undefined
selectedNodes The currently selected nodes (in tree structure). Use with selectionMode = “multiple” TreeNode[] []
options All the different options for the simple combobox. { text: string; value: string }[] []
size The combobox’s size. 'small' | 'medium' | 'large' 'medium'
favorites Allows selection of favoriteNodes in the tree view. Should only be used when treeItems is defined. boolean false
selectionMode
selection-mode
The selection mode of the items in the tree. Defaults to leaf (only leafs are selectable, i.e., not groups). 'single' | 'leaf' | 'multiple' 'leaf'
label The combobox’s label. string ''
name The combobox’s name attribute. string -
searchFn The search function that is used when filtering the combobox through manual input. (term: string, node: TreeNode) => boolean -
visibleValue The combobox’s visible value attribute. string ''
value The current value of the combobox, submitted as a name/value pair with form data. When multiple is enabled, the value attribute will be a space-delimited list of values based on the options selected, and the value property will be an array. For this reason, values must not contain spaces. string | string[] ''
maxTagsVisible
max-tags-visible
The maximum number of tags to show when selectionMode === multiple. After the maximum, ”+n” will be shown to indicate the number of additional items that are selected. Set to -1 to remove the limit. number 3
maxNodesShown
max-nodes-shown
The maximum number of tags to show when selectionMode === multiple. After the maximum, ”+n” will be shown to indicate the number of additional items that are selected. Set to -1 to remove the limit. number 20
helpText
help-text
The combobox’s help text. string ''
error Renders the field in an error state boolean false
success Renders the field in a success state boolean false
errorText
error-text
Error text to show in place of help text when input is invalid. string ''
successText
success-text
Success text to show in place of help text when input is valid. string ''
separator The combobox’s help text. string '/'
clearable Adds a clear button when the input is populated. 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. boolean false
showPathFirst
show-path-first
Enable this option to show the path first, before the variable name, in manual search and favorite listings. boolean false
closeOnSelection
close-on-selection
Indicates whether the combobox dropdown should close automatically when an item is selected. boolean false
placeholder The input’s placeholder text. string -
autofocus The input’s autofocus attribute. boolean -
disabled Disables the combobox component. boolean false
required The combobox’s required attribute. boolean false
invalid This will be true when the control is in an invalid state. Validity is determined by the required prop. boolean false
minChars
min-chars
The minimum chars that will triggered the combobox suggestions. number 0
emptyMessage
empty-message
Message displayed when no result found. string 'No matches found.'
loadingMessage
empty-message
Message displayed when no result found. string 'Loading more items…'
virtualization When enabled, the tree items are added to the DOM when their parent is opened. boolean false
preventAutomaticExpanding
prevent-automatic-expanding
Prevents the tree nodes closing and expanding automatically based on selection. boolean false
getChip A function that customizes the chips to be rendered when multiple=true. The first argument is the option, the second is the current chip’s index. The function should return either a Lit TemplateResult or a string containing trusted HTML of the symbol to render at the specified value. (node: TreeNode, index: number) => TemplateResult | string | HTMLElement -
validity Gets the validity state object - -
validationMessage Gets the validation message - -
chips Returns an array of chips representing the selected nodes. - -
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-selection-change onMoSelectionChange Emitted when an item gets selected or deselected { selection: this.selectedItems }
mo-select onMoSelect Emitted when the input’s value changes. -
mo-clear onMoClear Emitted when the clear button is activated. -
mo-input onMoInput Emitted when the input receives input. -
mo-focus onMoFocus Emitted when the input gains focus. -
mo-blur onMoBlur Emitted when the input loses focus. -
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
checkValidity() Checks for validity but does not show a validation message. Returns true when valid and false when invalid. -
getForm() Gets the associated form, if one exists. -
reportValidity() Checks for validity and shows the browser’s validation message if the control is invalid. -
setCustomValidity() Sets a custom validation message. If message is not empty, the field will be considered invalid. message: string
toggleDropdown() Toggles the visibility of the dropdown. -
reset() Resets the combobox to its initial state. -
findNodeIndex() Finds the index path of a given node within a tree. currentNode: TreeNode, nodeToFind: TreeNode
getNodeById() Retrieves a node from the tree by its ID. id: string
handleTreeChange() Handles the change event of the tree component. event: TreeChangeEvent

Learn more about methods.

Custom Properties

Name Description Default
--max-items-to-show Dictates max-height of the dropdown together with --item-height.

Learn more about customizing CSS custom properties.

Parts

Name Description
base The component’s base wrapper.
label The input label.
dropdown The dropdown control.
input The input control.
tags The tags to indicate multiple selection.
help-text The input help text.

Learn more about customizing CSS parts.

Dependencies

This component automatically imports the following dependencies.