Skip Navigation
React Magma

Combobox

A Combobox is similar to a Select component, in that it is used for collecting user provided information from a list of options. However, a combobox also allows users to type in the field, either to create their own option or to see typeahead suggestions.

Basic Usage

For simple Select functionality, please see the Select component.

import React from 'react';
import { Combobox } from 'react-magma-dom';
export function Example() {
return (
<Combobox
id="comboboxId"
labelText="Combobox"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
placeholder="Hello"
/>
);
}

Initial State

Set initial state using the initialSelectedItem, initialSelectedItems, and initialHighlightedIndex props.

When using any of the initial* props be aware that passing in the controlled version of that prop will overwrite the initial version.

Ex: selectedItem takes precedence over initialSelectedItem
import React from 'react';
import { Combobox } from 'react-magma-dom';
export function Example() {
return (
<>
<Combobox
id="initialSelectedByItemSelectId"
labelText="Initial selected by item"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
initialSelectedItem={{ label: 'Green', value: 'green' }}
/>
<Combobox
id="initialHighlightedIndexSelectId"
labelText="Initial highlighted index"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
initialHighlightedIndex={1}
/>
</>
);
}

Specific State Changes

You can track changes made to the internal state of the select using the following functions: onHighlightedIndexChange, onIsOpenChange, onSelectedItemChange, and onStateChange. Each of these functions will have a changes object as a parameter that includes the changes made to state from each action as well as a type that describes the action.

onStateChange is best used when using the select in a controlled state because it is called on every state change.

import React from 'react';
import { Combobox } from 'react-magma-dom';
export function Example() {
const [highlightedIndexChangeCount, updateHighlightedIndexChangeCount] =
React.useState<number>(0);
const [isOpenChangeCount, updateIsOpenChangeCount] =
React.useState<number>(0);
const [selectedItem, updateSelectedItem] = React.useState(undefined);
function onHighlightedIndexChange(changes) {
updateHighlightedIndexChangeCount(highlightedIndexChangeCount + 1);
}
function onIsOpenChange(changes) {
updateIsOpenChangeCount(isOpenChangeCount + 1);
}
function onSelectedItemChange(changes) {
updateSelectedItem(changes.selectedItem);
}
return (
<>
<strong>Selected Item: </strong>
<pre>{JSON.stringify(selectedItem, null, 2)}</pre>
<p>
<strong>Highlighted Index Change Count: </strong>
{highlightedIndexChangeCount}
</p>
<p>
<strong>Is Open Change Count: </strong>
{isOpenChangeCount}
</p>
<Combobox
id="individualStateChangesId"
labelText="Individual state changes"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
onHighlightedIndexChange={onHighlightedIndexChange}
onIsOpenChange={onIsOpenChange}
onSelectedItemChange={onSelectedItemChange}
/>
</>
);
}

Generic State Change

Using onStateChange and controlling every piece of state will result in losing most of the functionality that has been built in to this component. Only use this feature as a last resort.

onStateChange is called when any internal state is changed. Therefore, it is best used when using the combobox in a controlled state.

The onStateChange function passes a changes object that includes properties that have changed since the last state change. The changes object also includes a type property that describes the action taken to change the state. You can see the full list of the types in the stateChangeTypes section.

import React from 'react';
import { Combobox, ComboboxStateChangeTypes } from 'react-magma-dom';
export function Example() {
const [changes, updateChanges] = React.useState({});
const [highlightedIndex, updateHighlightedIndex] = React.useState<number>(-1);
const [isOpen, updateIsOpen] = React.useState<boolean>(false);
const [selectedItem] = React.useState({});
function onStateChange(newChanges) {
const { type } = newChanges;
updateChanges(newChanges);
switch (type) {
case ComboboxStateChangeTypes.ToggleButtonClick:
updateIsOpen(!isOpen);
break;
case ComboboxStateChangeTypes.InputKeyDownArrowDown:
if (newChanges.isOpen) {
updateIsOpen(newChanges.isOpen);
}
updateHighlightedIndex(newChanges.highlightedIndex);
break;
case ComboboxStateChangeTypes.ItemMouseMove:
case ComboboxStateChangeTypes.FunctionSetHighlightedIndex:
case ComboboxStateChangeTypes.InputKeyDownArrowUp:
updateHighlightedIndex(newChanges.highlightedIndex);
break;
case ComboboxStateChangeTypes.InputKeyDownEscape:
case ComboboxStateChangeTypes.InputBlur:
updateIsOpen(false);
updateHighlightedIndex(newChanges.highlightedIndex);
break;
case ComboboxStateChangeTypes.ItemClick:
case ComboboxStateChangeTypes.InputKeyDownEnter:
updateHighlightedIndex(newChanges.highlightedIndex);
updateIsOpen(newChanges.isOpen);
updateSelectedItem(newChanges.selectedItem);
break;
default:
break;
}
}
return (
<>
<pre>{JSON.stringify(changes, null, 2)}</pre>
<strong>Is Open: </strong>
{isOpen.toString()}
<Combobox
id="stateChangesId"
labelText="State changes"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
onStateChange={onStateChange}
highlightedIndex={highlightedIndex}
isOpen={isOpen}
selectedItem={selectedItem}
/>
</>
);
}

Controlled Items

If you would like to control your items and any updates to them on creation you can pass in a function to the onItemCreated prop. If no newItemTransform function is passed in, the created item will be the string value of what was created.

NOTE: Your created items will not be added to the items list internally. You will have to pass the updated items array to the items prop.

import React from 'react';
import { Combobox } from 'react-magma-dom';
export function Example() {
const defaultItems = [
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
];
const [items, updateItems] = React.useState(defaultItems);
function newItemTransform(item) {
return {
label: item.value,
value: item.value.toLowerCase(),
};
}
function resetItems() {
updateItems([...defaultItems]);
}
function onItemCreated(item) {
updateItems([...items, item]);
}
return (
<>
<button onClick={resetItems}>Reset Items</button>
<strong>Items: </strong>
<pre>{JSON.stringify(items, null, 2)}</pre>
<Combobox
id="controlledItemsId"
labelText="Controlled items"
items={items}
newItemTransform={newItemTransform}
onItemCreated={onItemCreated}
/>
</>
);
}

Disabled

import React from 'react';
import { Combobox } from 'react-magma-dom';
export function Example() {
return (
<Combobox
id="disabledSelectId"
disabled
labelText="Disabled"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
/>
);
}

Clearable

The optional isClearable prop allows the user to clear the field once a selection has been made.

When using the isClearable prop you can choose a default selected item to be set once the combobox is cleared using the defaultSelectedItem prop.

import React from 'react';
import { Combobox } from 'react-magma-dom';
export function Example() {
return (
<>
<Combobox
id="clearableSelectId"
labelText="Clearable"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
initialSelectedItem={{ label: 'Green', value: 'green' }}
isClearable
/>
<Combobox
id="clearableWithDefaultsId"
labelText="Clearable with defaults"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
isClearable
defaultSelectedItem={{ label: 'Blue', value: 'blue' }}
/>
</>
);
}

Error Message

If a select has an errorMessage, the select will be styled to highlight it's error state and the error message will appear below the field. If an error message is present, it will replace the helper text. Can be a node or a string.

import React from 'react';
import { Combobox } from 'react-magma-dom';
export function Example() {
return (
<Combobox
id="errorMessage"
labelText="Error message"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
errorMessage="Please select a color"
/>
);
}

Helper Message

The helperMessage appears underneath the select field. It will not appear if an error message is present. Can be a node or a string.

import React from 'react';
import { Combobox } from 'react-magma-dom';
export function Example() {
return (
<Combobox
id="helperMessage"
labelText="Helper message"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
helperMessage="Helper text goes here"
/>
);
}

Placeholder

The placeholder text of an input can be set using the placeholder prop. Placeholder text should be used to provide supplemental information about the input field. It should not be relied upon to take the place of the label text.

import React from 'react';
import { Combobox } from 'react-magma-dom';
export function Example() {
return (
<Combobox
labelText="Placeholder text"
items={['Red', 'Blue', 'Green']}
placeholder="Enter or select a color"
/>
);
}

Accessibility

The getA11yStatusMessage prop is a passed in function that allows for customization of the screen-reader accessible message whenever the following props are changed: items, highlightedIndex, inputValue or isOpen.

The getA11ySelectionMessage prop is a passed in function that allows for customization of the screen-reader accessible message whenever an item has been selected.

For both the getA11yStatusMessage and getA11ySelectionMessage functions there is an object passed with internal state data.

import React from 'react';
import { Combobox } from 'react-magma-dom';
export function Example() {
return (
<Combobox
id="newA11yStatusMessageId"
labelText="New accessibility status message"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
getA11yStatusMessage={({ highlightedItem }) =>
`custom message saying that ${
highlightedItem ? highlightedItem.label : 'nothing'
} is highlighted`
}
getA11ySelectionMessage={({ selectedItem }) =>
`custom message saying that ${
selectedItem ? selectedItem.label : 'nothing'
} is now selected`
}
/>
);
}

Disable Create Item

The disableCreateItem prop is an optional boolean and can be used to prevent the user from creating their own items. This is commonly used to provide a typeahead dropdown without allowing custom options.

import React from 'react';
import { Combobox } from 'react-magma-dom';
export function Example() {
return (
<Combobox
disableCreateItem
id="disableCreateItemId"
labelText="Disable create item"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
/>
);
}

Multi Combobox

Use hasPersistentMenu prop on multi comboboxes to keep the list of items open after a selection.

import React from 'react';
import { Combobox } from 'react-magma-dom';
export function Example() {
const [selectedItems, updateSelectedItems] = React.useState([]);
function handleSelectedItemsChange(changes) {
updateSelectedItems(changes.selectedItems);
}
function handleRemoveSelectedItem(removedItem) {
updateSelectedItems(selectedItems.filter(item => item !== removedItem));
}
return (
<>
<Combobox
isMulti
hasPersistentMenu
id="multiComboboxId"
labelText="Multi combobox"
initialSelectedItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
]}
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
{ label: 'Orange', value: 'orange' },
{ label: 'Aqua', value: 'aqua' },
{ label: 'Gold', value: 'gold' },
{ label: 'Periwinkle', value: 'periwinkle' },
{ label: 'Lavender', value: 'lavender' },
{ label: 'Marigold', value: 'marigold' },
{ label: 'Yellow', value: 'yellow' },
{ label: 'Purple', value: 'purple' },
{ label: 'Dusty Rose', value: 'dusty_rose' },
{ label: 'Burnt Sienna', value: 'burnt_sienna' },
]}
/>
<Combobox
isMulti
hasPersistentMenu
id="multiComboboxId2"
labelText="Multi combobox"
initialSelectedItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
{ label: 'Orange', value: 'orange' },
{ label: 'Aqua', value: 'aqua' },
{ label: 'Gold', value: 'gold' },
{ label: 'Periwinkle', value: 'periwinkle' },
{ label: 'Lavender', value: 'lavender' },
{ label: 'Marigold', value: 'marigold' },
{ label: 'Yellow', value: 'yellow' },
{ label: 'Purple', value: 'purple' },
{ label: 'Dusty Rose', value: 'dusty_rose' },
{ label: 'Burnt Sienna', value: 'burnt_sienna' },
]}
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
{ label: 'Orange', value: 'orange' },
{ label: 'Aqua', value: 'aqua' },
{ label: 'Gold', value: 'gold' },
{ label: 'Periwinkle', value: 'periwinkle' },
{ label: 'Lavender', value: 'lavender' },
{ label: 'Marigold', value: 'marigold' },
{ label: 'Yellow', value: 'yellow' },
{ label: 'Purple', value: 'purple' },
{ label: 'Dusty Rose', value: 'dusty_rose' },
{ label: 'Burnt Sienna', value: 'burnt_sienna' },
]}
/>
<Combobox
errorMessage="Please correct this error"
id="multiComboboxControlledId"
isMulti
hasPersistentMenu
labelText="Multi combobox controlled"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
selectedItems={selectedItems}
onSelectedItemsChange={handleSelectedItemsChange}
onRemoveSelectedItem={handleRemoveSelectedItem}
/>
</>
);
}

Events

import React from 'react';
import { Combobox } from 'react-magma-dom';
export function Example() {
const [currentComboboxEvent, updateCurrentComboboxEvent] = React.useState<
string | undefined
>(undefined);
function handleComboboxInputBlur(event) {
updateCurrentComboboxEvent('Blur');
}
function handleComboboxInputFocus(event) {
updateCurrentComboboxEvent('Focus');
}
function handleComboboxInputKeyPress(event) {
updateCurrentComboboxEvent('KeyPress');
}
return (
<>
<p>
<strong>{currentComboboxEvent || 'No'} event was triggered</strong>
</p>
<Combobox
id="comboboxFocusEventId"
labelText="Combobox focus events"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
onInputBlur={handleComboboxInputBlur}
onInputFocus={handleComboboxInputFocus}
onInputKeyPress={handleComboboxInputKeyPress}
/>
</>
);
}

Async

import React from 'react';
import { Combobox } from 'react-magma-dom';
export function Example() {
const [isLoading, updateIsLoading] = React.useState<boolean>(false);
function loadItems() {
return new Promise(resolve =>
resolve([
{ label: 'Yellow', value: 'yellow' },
{ label: 'Pink', value: 'pink' },
{ label: 'Periwinkle', value: 'periwinkle' },
])
);
}
function handleInputValueChange(changes, setInputItems) {
const { inputValue } = changes;
if (!inputValue) return;
updateIsLoading(true);
setTimeout(() => {
updateIsLoading(false);
loadItems().then((items: any) => {
setInputItems(
items.filter(item =>
item.label.toLowerCase().startsWith(inputValue.toLowerCase())
)
);
});
}, 1000);
}
return (
<Combobox
id="asyncId"
labelText="Async"
isLoading={isLoading}
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
onInputValueChange={handleInputValueChange}
/>
);
}

Typeahead

When using Combobox for typeahead with a large items list, use the isTypeahead prop to allow the selected item to not be part of the original items list. In addition, when isTypeahead and isLoading are both true, the loading indicator will appear on the list of items instead of the input.

import React from 'react';
import { Combobox } from 'react-magma-dom';
export function Example() {
const dataSet = [
'Aardvark',
'Aardwolf',
'Abyssinian',
'Addax',
'Adelie Penguin',
'Affenpinscher',
'Afghan Hound',
'African Bullfrog',
'African Bush Elephant',
'African Civet',
'African Clawed Frog',
'African Forest Elephant',
'African Grey Parrot',
'African Palm Civet',
'African Penguin',
'African Tree Toad',
'African Wild Dog',
'Aidi',
'Ainu',
'Airedale Terrier',
'Airedoodle',
'Akbash',
'Akita',
'Akita Shepherd',
'Alabai',
'Alaskan Husky',
'Alaskan Klee Kai',
'Alaskan Malamute',
'Alaskan Shepherd',
'Albacore Tuna',
'Albatross',
'Aldabra Giant Tortoise',
'Alligator',
'Alligator Gar',
'Alpaca',
'Alpine Dachsbracke',
'Alpine Goat',
'Alusky',
'Amazon Parrot',
'Amazon River Dolphin (Pink Dolphin)',
'Ambrosia Beetle',
'American Alsatian',
'American Bulldog',
'American Cocker Spaniel',
'American Cockroach',
'American Coonhound',
'American Eskimo Dog',
'American Foxhound',
'American Hairless Terrier',
'American Leopard Hound',
'American Pit Bull Terrier',
'American Pygmy Goat',
'American Robin',
'American Staffordshire Terrier',
'American Toad',
'American Water Spaniel',
'Amur Leopard',
'Anatolian Shepherd Dog',
'Anchovies',
'Angelfish',
'Anglerfish',
'Angora Goat',
'Ant',
'Antarctic scale worm',
'Anteater',
'Antelope',
'Appenzeller Dog',
'Apple Head Chihuahua',
'Arapaima',
'Arctic Fox',
'Arctic Hare',
'Arctic Wolf',
'Arizona Bark Scorpion',
'Armadillo',
'Armyworm',
'Asian Elephant',
'Asian Giant Hornet',
'Asian Palm Civet',
'Asiatic Black Bear',
'Aurochs',
'Aussiedoodle',
'Aussiedor',
'Australian Bulldog',
'Australian Cattle Dog',
'Australian Gecko',
'Australian Kelpie Dog',
'Australian Labradoodle',
'Australian Mist',
'Australian Retriever',
'Australian Shepherd',
'Australian Terrier',
'Avocet',
'Axolotl',
];
const [suggestedItems, setSuggestedItems] = React.useState([]);
const [, updateSelectedItems] = React.useState([]);
const [isLoading, setIsLoading] = React.useState(true);
const [inputQuery, setInputQuery] = React.useState('');
function handleSelectedItemsChange(changes) {
updateSelectedItems(changes.selectedItems);
}
function onInputKeyPress(event) {
setInputQuery(event.target.value + event.key);
}
function onInputValueChange(event) {
setInputQuery(event.inputValue);
}
React.useEffect(() => {
setTimeout(() => {
setIsLoading(true);
}, 100);
setTimeout(() => {
const matches = dataSet.filter(item => {
return item.toLowerCase().includes(inputQuery.toLowerCase());
});
const newSuggestedItems = matches.slice(0, 5).map(item => {
return { label: item, value: item };
});
setTimeout(() => {
setSuggestedItems(newSuggestedItems);
setIsLoading(false);
}, 100);
}, 1000);
}, [inputQuery]);
return (
<Combobox
isTypeahead
isClearable
isMulti
disableCreateItem
items={suggestedItems}
isLoading={isLoading}
labelText="Typeahead Example - Animals"
onInputKeyPress={onInputKeyPress}
onInputValueChange={onInputValueChange}
onSelectedItemsChange={handleSelectedItemsChange}
/>
);
}

Inverse

The isInverse prop is an optional boolean used when the component is to be displayed on a dark background.

import React from 'react';
import { Card, CardBody, Combobox } from 'react-magma-dom';
export function Example() {
return (
<Card isInverse>
<CardBody>
<Combobox
id="comboboxInverseId"
isInverse
labelText="Combobox"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
/>
<Combobox
errorMessage="Please correct this error"
isMulti
id="comboboxMultiInverseId"
isInverse
labelText="Multi combobox"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
initialSelectedItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
/>
</CardBody>
</Card>
);
}

Custom Items List Height

The items list menu has a default max height that can be changed in the theme or using the itemListMaxHeight prop.

import React from 'react';
import { Combobox } from 'react-magma-dom';
export function Example() {
return (
<Combobox
labelText="Combobox small item list menu"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
itemListMaxHeight="50px"
/>
);
}

Label Position

The labelPosition prop can be used to set the position of the text label relative to the form field. It accepts either top or left, with top as the default. Left-aligned lables are not recommended for use in standard forms; instead they are designed for smaller spaces where vertical space is limited, such as in a toolbar.

import React from 'react';
import { Combobox, LabelPosition } from 'react-magma-dom';
export function Example() {
return (
<Combobox
labelPosition={LabelPosition.left}
labelText="Left-aligned label"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
/>
);
}

Custom Styles

Custom styles can be passed into the Combobox component. The containerStyle property will apply to the container. Additional labelStyle, inputStyle and messageStyle properties are available to style the respective elements. Please use discretion when adding custom styles.

import React from 'react';
import { Combobox } from 'react-magma-dom';
export function Example() {
const [selectedItem, updateSelectedItem] = React.useState('');
function onSelectedItemChange(changes) {
updateSelectedItem(changes.selectedItem);
}
return (
<>
<Combobox
containerStyle={{ marginBottom: '32px' }}
helperMessage="Helper message"
inputStyle={{ border: '2px dotted green', width: '200px' }}
items={['Red', 'Blue', 'Green']}
labelStyle={{ fontStyle: 'italic' }}
labelText="Basic with custom styles"
messageStyle={{ border: '1px solid blue' }}
selectedItem={selectedItem}
onSelectedItemChange={onSelectedItemChange}
/>
<Combobox
containerStyle={{ marginBottom: '32px' }}
helperMessage="Helper message"
inputStyle={{ border: '2px dotted green', width: '200px' }}
isMulti
items={['Red', 'Blue', 'Green']}
labelStyle={{ fontStyle: 'italic' }}
labelText="Multi with custom styles"
messageStyle={{ border: '1px solid blue' }}
selectedItem={selectedItem}
onSelectedItemChange={onSelectedItemChange}
/>
</>
);
}

Custom Items

By default each component accepts an array of items with either a string or an object with the shape {item: string; label: string;} for each item. If you need to pass in a custom shape for your items you can pass in an additional prop named itemToString which is a function that returns the string representation of your item which will be applied as the label.

WARNING Your function must include a null check.

If you are using a custom shape for your items in a typescript project refer to our example of using your custom type.

import React from 'react';
import { Combobox } from 'react-magma-dom';
import { v4 as uuidv4 } from 'uuid';
export function Example() {
const [controlledSelectedItem, updateControlledSelectedItem] =
React.useState('');
const [controlledItems, updateControlledItems] = React.useState([
{ id: 0, name: 'red', representation: 'Red', hex: '#FF0000' },
{ id: 1, name: 'blue', representation: 'Blue', hex: '#0000FF' },
{ id: 2, name: 'green', representation: 'Green', hex: '#008000' },
]);
function itemToString(item): string {
return item
? `${item.representation}${item.hex ? ` (${item.hex})` : ''}`
: '';
}
function newItemTransform(item) {
const { value } = item;
return {
id: uuidv4(),
name: value,
hex: item.hex,
representation: value.charAt(0).toUpperCase() + value.slice(1),
};
}
function handleSelectedItemChange(changes) {
updateControlledSelectedItem(changes.selectedItem);
}
function handleItemCreated(newItem) {
updateControlledSelectedItem(newItem);
updateControlledItems(oldItems => [...oldItems, newItem]);
}
return (
<>
<Combobox
labelText="Combobox with custom items"
defaultItems={[
{ id: 10, name: 'red', representation: 'Red', hex: '#FF0000' },
{ id: 11, name: 'blue', representation: 'Blue', hex: '#0000FF' },
{ id: 12, name: 'green', representation: 'Green', hex: '#008000' },
]}
newItemTransform={newItemTransform}
itemToString={itemToString}
/>
{controlledSelectedItem && (
<>
<p>
<strong>Controlled Selected Item</strong>
</p>
<pre>{JSON.stringify(controlledSelectedItem, null, 2)}</pre>
</>
)}
<Combobox
labelText="Controlled combobox with custom items"
items={controlledItems}
newItemTransform={newItemTransform}
itemToString={itemToString}
onItemCreated={handleItemCreated}
onSelectedItemChange={handleSelectedItemChange}
selectedItem={controlledSelectedItem}
/>
</>
);
}

Custom Items Typescript

When using a custom shape for your items in a typescript project you will need to provide the Combobox element with the shape of the items you are passing in.

import React from 'react';
import { Combobox } from 'react-magma-dom';
import { v4 as uuidv4 } from 'uuid';
interface CustomComboboxItem {
id: number;
actual: string;
representation: string;
hex?: string;
}
export function Example() {
const customItems: CustomComboboxItem[] = [
{
id: 21,
actual: 'red',
representation: 'Red',
hex: '#FF0000',
},
{
id: 22,
actual: 'blue',
representation: 'Blue',
hex: '#0000FF',
},
{
id: 23,
actual: 'green',
representation: 'Green',
hex: '#008000',
},
];
function itemToString(item: CustomComboboxItem): string {
return item ? `${item.representation} (${item.hex})` : '';
}
function newItemTransform(item: {
label: string;
value: string;
}): CustomComboboxItem {
const { value } = item;
return {
id: uuidv4(),
actual: value,
representation: value.charAt(0).toUpperCase() + value.slice(1),
};
}
return (
<>
<Combobox<CustomComboboxItem>
id="customItemToStringTypescriptId"
labelText="Custom items with typescript"
defaultItems={customItems}
itemToString={itemToString}
newItemTransform={newItemTransform}
/>
</>
);
}

Custom Components

Out of the box, the Combobox renders a styled li for items and uses React Magma-styled iconography for the button used to clear the selection, the caret for the dropdown, and the loading animation. However, these can be overridden by providing custom components to the components prop, in an object containing an Item, a ClearIndicator, a DropdownIndicator and a LoadingIndicator.

Custom Item Component

By default each item will be rendered with the itemToString value in a styled li. If you would like to add more information or would just like to be able to fully customize the look of your items you can pass in an Item component that uses the props that we use internally.

Be aware. You must pass a ref to your custom item component to make sure the functionality of the entire Combobox continues to work.
import React from 'react';
import styled from '@emotion/styled';
import { Combobox } from 'react-magma-dom';
export function Example() {
const CustomStyledItem = styled.li(props => ({
alignSelf: 'center',
background: props.isFocused ? props.theme.colors.neutral200 : 'transparent',
lineHeight: '24px',
margin: '0',
padding: '8px 16px',
}));
const ContainerSpan = styled.span(props => ({
display: 'flex',
alignItems: 'center',
}));
const Hex = styled.span(props => ({
background: props.color,
border: `2px solid ${props.theme.colors.neutral}`,
borderRadius: props.theme.borderRadius,
display: 'inline-flex',
height: '16px',
marginRight: '4px',
width: '16px',
}));
const Description = styled.div(props => ({
fontSize: '12px',
color: props.theme.colors.neutral500,
}));
function itemToString(item) {
return item ? item.representation : '';
}
const CustomItem = props => {
const { itemRef, itemString, item, ...other } = props;
return (
<CustomStyledItem {...other} ref={itemRef}>
<ContainerSpan>
<Hex color={item.hex} theme={props.theme} />
{itemString}
</ContainerSpan>
<Description theme={props.theme}>{item.description}</Description>
</CustomStyledItem>
);
};
return (
<Combobox
components={{ Item: CustomItem }}
defaultItems={[
{
id: 30,
name: 'red',
representation: 'Red',
hex: '#FF0000',
description: 'The color of roses',
},
{
id: 31,
name: 'blue',
representation: 'Blue',
hex: '#0000FF',
description: 'The color of blueberries',
},
{
id: 32,
name: 'green',
representation: 'Green',
hex: '#008000',
description: 'The color of grass',
},
]}
disableCreateItem
itemToString={itemToString}
labelText="Combobox with custom item render"
/>
);
}

Other Custom Components

import React from 'react';
import { Combobox } from 'react-magma-dom';
export function Example() {
const [isLoading, updateIsLoading] = React.useState<boolean>(false);
function loadItems() {
return new Promise(resolve =>
resolve([
{ label: 'Yellow', value: 'yellow' },
{ label: 'Pink', value: 'pink' },
{ label: 'Periwinkle', value: 'periwinkle' },
])
);
}
function handleInputValueChange(changes, setInputItems) {
const { inputValue } = changes;
if (!inputValue) return;
updateIsLoading(true);
setTimeout(() => {
updateIsLoading(false);
loadItems().then((items: any) => {
setInputItems(
items.filter(item =>
item.label.toLowerCase().startsWith(inputValue.toLowerCase())
)
);
});
}, 1000);
}
return (
<Combobox
id="customComponentsId"
labelText="Custom components"
isLoading={isLoading}
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
isClearable
onInputValueChange={handleInputValueChange}
components={{
ClearIndicator: props => <button onClick={props.onClick}>Clear</button>,
DropdownIndicator: React.forwardRef((props, ref) => (
<button ref={ref} onClick={props.onClick}>
Dropdown
</button>
)),
LoadingIndicator: props => <>Loading</>,
}}
/>
);
}

Internationalization

Some of the internationalization overrides use placeholders to insert selected values in to the message. Placeholders are specific keywords surrounded by curly braces.

  • {labelText} will be replaced with the comboboxes labelText.
  • {selectedItem} will be replaced by the current itemToString representation of the selectedItem of the combobox.
  • {inputValue} will be replaced by the current inputValue of the combobox input.

Full example of internationalization override options

import React from 'react';
import { Combobox, I18nContext, defaultI18n } from 'react-magma-dom';
export function Example() {
return (
<I18nContext.Provider
value={{
...defaultI18n,
combobox: {
clearIndicatorAriaLabel:
'click to reset selection for {labelText}. {selectedItem} is currently selected',
createLabel: 'Custom Create "{inputValue}"',
},
multiCombobox: {
selectedItemButtonAriaLabel:
'click to reset the selected item {selectedItem}',
},
}}
>
<Combobox
labelText="Internationalization"
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
isClearable
initialSelectedItem={{ label: 'Red', value: 'red' }}
/>
<Combobox
labelText="Multi internationalization"
isMulti
defaultItems={[
{ label: 'Red', value: 'red' },
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
]}
initialSelectedItems={[{ label: 'Red', value: 'red' }]}
/>
</I18nContext.Provider>
);
}

State Change Types

The type property in the changes object that is returned from the onStateChange function corresponds to a stateChangeTypes property. The list of all the possible types for a combobox or a multi-combobox are listed below.

In the development environment these types equate to strings
(eg: ComboboxStateChangeTypes.InputKeyDownArrowDown = '__input_keydown_arrow_down__'). However, in the production environment the types equate to numbers
(eg:ComboboxStateChangeTypes.InputKeyDownArrowDown = 0).

Combobox

  • ComboboxStateChangeTypes.InputKeyDownArrowDown
  • ComboboxStateChangeTypes.InputKeyDownArrowUp
  • ComboboxStateChangeTypes.InputKeyDownEscape
  • ComboboxStateChangeTypes.InputKeyDownHome
  • ComboboxStateChangeTypes.InputKeyDownEnd
  • ComboboxStateChangeTypes.InputKeyDownEnter
  • ComboboxStateChangeTypes.InputChange
  • ComboboxStateChangeTypes.InputBlur
  • ComboboxStateChangeTypes.MenuMouseLeave
  • ComboboxStateChangeTypes.ItemMouseMove
  • ComboboxStateChangeTypes.ItemClick
  • ComboboxStateChangeTypes.ToggleButtonClick
  • ComboboxStateChangeTypes.FunctionToggleMenu
  • ComboboxStateChangeTypes.FunctionOpenMenu
  • ComboboxStateChangeTypes.FunctionCloseMenu
  • ComboboxStateChangeTypes.FunctionSetHighlightedIndex
  • ComboboxStateChangeTypes.FunctionSelectItem
  • ComboboxStateChangeTypes.FunctionSetInputValue
  • ComboboxStateChangeTypes.FunctionReset

MultiCombobox

  • MultipleSelectionStateChangeTypes.SelectedItemClick
  • MultipleSelectionStateChangeTypes.SelectedItemKeyDownDelete
  • MultipleSelectionStateChangeTypes.SelectedItemKeyDownBackspace
  • MultipleSelectionStateChangeTypes.SelectedItemKeyDownNavigationNext
  • MultipleSelectionStateChangeTypes.SelectedItemKeyDownNavigationPrevious
  • MultipleSelectionStateChangeTypes.DropdownKeyDownNavigationPrevious
  • MultipleSelectionStateChangeTypes.DropdownKeyDownBackspace
  • MultipleSelectionStateChangeTypes.DropdownClick
  • MultipleSelectionStateChangeTypes.FunctionAddSelectedItem
  • MultipleSelectionStateChangeTypes.FunctionRemoveSelectedItem
  • MultipleSelectionStateChangeTypes.FunctionSetSelectedItems
  • MultipleSelectionStateChangeTypes.FunctionSetActiveIndex
  • MultipleSelectionStateChangeTypes.FunctionReset

Combobox Props

ariaDescribedBy

Description

Id of the element that describes the combobox input

Type

string

Default

-


components

Description

This complex object includes all the compositional components that are used. If you wish to overwrite a component, pass in a component to the appropriate namespace

Type

SelectComponents

Default

-


containerStyle

Description

Style properties for the component container element

Type

CSSProperties

Default

-


defaultItems

Description

Default selectable options. Allows for uncontrolled component and internal creation of items. Can be an array of strings or objects

Type

Generic[]

Default

-


disableCreateItem

Description

If true, the new items cannot be created by typing in the text field

Type

boolean

Default

false


disabled

Description

If true, item will be disabled; it will appear dimmed and events will not fire

Type

boolean

Default

false


errorMessage

Description

Content of the error message. If a value is provided, the component will be styled to show an error state

Type

React.ReactNode

Default

-


helperMessage

Description

Content of the helper message

Type

React.ReactNode

Default

-


innerRef

Description

Reference to the input element in the combobox

Type

React.Ref

Default

-


inputStyle

Description

Style properties for the select trigger or combobox input

Type

CSSProperties

Default

-


isClearable

Description

If true, the component include a button for clearing the field

Type

boolean

Default

false


isInverse

Description

If true, the component will have inverse styling to better appear on a dark background

Type

boolean

Default

false


isLabelVisuallyHidden

Description

If true, label text will be hidden visually, but will still be read by assistive technology

Type

boolean

Default

false


isLoading

Description

If true, the loading component is shown

Type

boolean

Default

false


isTypeahead

Description

When false, the selected item gets validated to ensure it's in the original `items` list. When using Combobox for typeahead with a large `items` list, set this boolean to true to allow the selected item to not be part of the original `items` list. In addition, when this is true and `isLoading` is used, the loading indicator will appear on the list instead of the input

Type

boolean

Default

false


itemListMaxHeight

Description

Max-height for the item menu list ul element

Type

number | string

Default

-


items

Description

Default selectable options. Can be an array of strings or objects

Type

Generic[]

Default

-


labelPosition

Description

Position of text label relative to form field

Type

enum, one of:
LabelPosition.left
LabelPosition.top

Default

LabelPosition.top


labelStyle

Description

Style properties for the label

Type

CSSProperties

Default

-


labelText

Required

Description

Text for label

Type

string

Default

-


labelWidth

Description

If the labelPosition value is 'left' then Input labels have a specified width in percentage, otherwise no width is set.

Type

number

Default

-


menuStyle

Description

Style properties for the items menu

Type

CSSProperties

Default

-


messageStyle

Description

Style properties for the helper or error message

Type

CSSProperties

Default

-


newItemTransform

Description

Function passed in that transforms a newly created item to whatever format your items are in

Type

function

Default

-


onInputBlur

Description

Event that fires when the input loses focus

Type

function

Default

-


onInputChange

Description

Event that fires when the input's value is changed

Type

function

Default

-


onInputFocus

Description

Event that fires when the input gains focus

Type

function

Default

-


onInputKeyDown

Description

Event that will fire when input receives keypress.

Type

function

Default

-


onInputKeyPress

Description

Event that will fire when a character is typed in the input

Type

function

Default

-


onInputKeyUp

Description

Event that will fire when a keypress is released on the input

Type

function

Default

-


onInputValueChange

Description

Event that fires when the selected item changes

Type

function

Default

-


onItemCreated

Description

Event that fires when a new item is created with the create item option is clicked in the item list menu

Type

function

Default

-


placeholder

Description

Text for select trigger button or combobox input placeholder

Type

string

Default

-


toggleButtonRef

Description

Reference to the toggle button element wrapping the input in the combobox

Type

React.Ref

Default

-


On this page

Deploys by Netlify