Form

Single Select

A single select allows users to select only one option from a list. It is typically used when there are a large number of options.
1
import {SingleSelectConnected} from '@coveord/plasma-react';
2
3
export default () => (
4
<SingleSelectConnected
5
id="single-select-1"
6
items={[
7
{value: 'one', displayValue: 'Option 1'},
8
{value: 'two', displayValue: 'Option 2'},
9
{value: 'three', displayValue: 'Option 3'},
10
]}
11
/>
12
);

Props

NameTypeDefaultDescription
idrequiredstring
Unique identifier
buttonPrependReactNode
Content to display in the toggle button, before the selected option
canClearboolean
Whether a selected item can be unselected
childrenReactNode
classesstring[]
Additional CSS classes to set on the list element
customButtonComponentType<ISelectButtonProps>
A component to render instead of the default button. The button is what is displayed when the dropdown is not opened and used to open it.
deselectTooltipTextstring
The text that appears when hovering over the button to unselect an item
disabledboolean
Whether the SingleSelect is disabled
dropClassesstring
Additional CSS classes to set on the dropdown element
dropOptionPartial<IDropPodProps>
Additional Drop props to set on the Drop component (see IDropPodProps interface)
footerReactNode
Content that is displayed underneath the list of items inside the dropdown
hasFocusableChildboolean
Whether the dropdown has a child that needs to receive the focus upon opening the dropdown, for example: a filter input.
isLoadingboolean
Whether the items are currenty loading (being fetched). If true, it will render visual loading placeholders in the ListBox
itemsIItemBoxProps[]
The list of items to display as potential choices
noActiveboolean
If true, no highlight will be rendered on active items. An item is active when using keyboard navigation
noFixedWidthboolean
If true, the dropdown content can have a width that is larger than the button
noResultItemIItemBoxProps{value: 'No Items'}
The content shown in the list of items when there are no items to display.
onSelectOptionCallback(option: string) => void
A callback function executed after the selected item changes
  • option–The selected item value
onUpdate() => void
A callback function that is executed whenever an update of the items array is required. For example if a filter was applied and the filter is done on the server.
placeholderstring"Select an option"
The text displayed in the button when no item is selected
selectClassesstring
Additional CSS classes to set on the outermost element
toggleClassesstring
Additional CSS classes to set on the toggle button
wrapItems(items: ReactNode) => ReactNode
Useful if you need to wrap the list of items with another component. We use this when combining with the InfiniteScroll HOC
  • items–The list of items in the form of a React Component

Examples

With a filter box
1
import {SingleSelectWithFilter} from '@coveord/plasma-react';
2
3
export default () => (
4
<SingleSelectWithFilter
5
id="single-select-2"
6
items={[
7
{value: 'one', displayValue: 'Option 1'},
8
{value: 'two', displayValue: 'Option 2'},
9
{value: 'three', displayValue: 'Option 3'},
10
{value: 'four', displayValue: 'Option 4'},
11
{value: 'five', displayValue: 'Option 5'},
12
]}
13
/>
14
);
With predicates options
1
import {IItemBoxProps, SingleSelectWithPredicate} from '@coveord/plasma-react';
2
3
export default () => (
4
<SingleSelectWithPredicate
5
id="single-select-3"
6
items={[
7
{value: '1', displayValue: 'Option 1'},
8
{value: '2', displayValue: 'Option 2'},
9
{value: '3', displayValue: 'Option 3'},
10
{value: '4', displayValue: 'Option 4'},
11
{value: '5', displayValue: 'Option 5'},
12
]}
13
options={[
14
{id: 'all', option: {content: 'All'}, selected: true},
15
{id: 'even', option: {content: 'Even'}},
16
{id: 'odd', option: {content: 'Odd'}},
17
]}
18
matchPredicate={(predicate: string, item: IItemBoxProps) => {
19
const value = parseInt(item.value, 10);
20
switch (predicate) {
21
case 'even':
22
return value % 2 === 0;
23
case 'odd':
24
return value % 2 === 1;
25
default:
26
return true;
27
}
28
}}
29
/>
30
);
With infinite scroll and server side handling of pagination and filtering
1
import {
2
FilterBoxSelectors,
3
IItemBoxProps,
4
ISingleSelectOwnProps,
5
selectWithFilter,
6
selectWithInfiniteScroll,
7
SelectWithInfiniteScrollProps,
8
SingleSelectConnected,
9
withServerSideProcessing,
10
} from '@coveord/plasma-react';
11
import {ComponentType, FunctionComponent, useState, useEffect} from 'react';
12
import {useSelector} from 'react-redux';
13
import {compose} from 'redux';
14
15
const ServerSideSingleSelect: ComponentType<
16
React.PropsWithChildren<ISingleSelectOwnProps & SelectWithInfiniteScrollProps>
17
> = compose<any>(withServerSideProcessing, selectWithFilter, selectWithInfiniteScroll)(SingleSelectConnected);
18
19
export default () => {
20
const filterValue = useSelector((state) => FilterBoxSelectors.getFilterText(state, {id: 'single-select-4'}));
21
const [photos, totalEntries, fetchPhotos] = usePhotosAPIMock();
22
const [pageNbr, setPage] = useState(1);
23
24
useEffect(() => {
25
fetchPhotos({_page: 1, _limit: PER_PAGE});
26
}, []);
27
28
const fetchNextPage = () => {
29
fetchPhotos({_page: pageNbr + 1, _limit: PER_PAGE, q: filterValue}, false);
30
setPage(pageNbr + 1);
31
};
32
33
const applyFilter = () => {
34
fetchPhotos({_page: 1, _limit: PER_PAGE, q: filterValue});
35
setPage(1);
36
};
37
38
return (
39
<ServerSideSingleSelect
40
id="single-select-4"
41
items={photos.map(
42
(photo: PhotoProps): IItemBoxProps => ({
43
value: photo.id,
44
displayValue: <PhotoItem {...photo} />,
45
})
46
)}
47
totalEntries={totalEntries}
48
next={fetchNextPage}
49
onUpdate={applyFilter}
50
canClear
51
noFixedWidth
52
/>
53
);
54
};
55
56
export interface PhotoProps {
57
albumId: string;
58
id: string;
59
title: string;
60
url: string;
61
thumbnailUrl: string;
62
}
63
64
const PhotoItem: FunctionComponent<PhotoProps> = ({id, url, title, thumbnailUrl}) => (
65
<div className="flex flex-center">
66
<a href={url} target="__blank" className="mr2 flex">
67
<img src={thumbnailUrl} alt={title} width={IMG_SIZE} height={IMG_SIZE} />
68
</a>
69
<span>{title}</span>
70
</div>
71
);
72
73
const PER_PAGE = 10;
74
const IMG_SIZE = 50;
75
76
const clean = <T extends Record<string, unknown>>(object: T) => {
77
Object.keys(object).forEach((key) => (object[key] === undefined ? delete object[key] : {}));
78
return object;
79
};
80
81
const usePhotosAPIMock = (): [any[], number, (params?: any, overwrite?: boolean) => void] => {
82
const [photos, setPhotos] = useState([]);
83
const [totalEntries, setTotalEntries] = useState(0);
84
85
const fetchPhotos = (params?: any, overwrite = true) => {
86
const cleanParams = clean(params);
87
const queryString =
88
cleanParams && Object.keys(cleanParams).length > 0
89
? `?${new URLSearchParams(Object.entries(cleanParams)).toString()}`
90
: '';
91
92
return fetch(`https://jsonplaceholder.typicode.com/photos${queryString}`)
93
.then((response) => {
94
setTotalEntries(parseInt(response.headers.get('x-total-count'), 10));
95
return response.json();
96
})
97
.then((newPhotos) => {
98
if (overwrite) {
99
setPhotos(newPhotos);
100
} else {
101
setPhotos([...photos, ...newPhotos]);
102
}
103
});
104
};
105
106
return [photos, totalEntries, fetchPhotos];
107
};

Best Practices

Use a single select to have users filter options from a long list and select only one. A single select is especially appropriate when the list of available options is very long or when the space is limited.

List options in alphanumerical order unless a more suited ordering rationale applies, for example when listing size or security level options.

Always include the ability to filter options when the list contains 20 or more.

Labeling

Keep titles and labels short, preferably under three words.

Title

The title should indicate the type of information to provide.

Provide a descriptive title without action verbs. For instance, write "Favorite drink" rather than "Select your favorite drink".

Labels

Labels identify each option and should be self-explanatory. The width of the input should fully display the name of the selected option.

Use a consistent writing style for all options in the list.

Help Text and Instructions

The placeholder text should indicate the type of information to select. Use an action verb. For example, write "Select a drink" rather than "Select an option".

Feedback and Validation

Allow the addition of custom values only when it doesn't increase the risk of failure and when there may be options other than those listed.

Examples:

  • When users select a country: do not allow custom values.
  • When users select their favorite color: allow custom values.

Related Components

If your use case doesn't match the guidelines above, consider using one of the following components instead:

Edit guidelines