Picker

A select is very similar to a dropdown visually but it let users select options from the panel and then represent the selection in the button.

installyarn add @clayui/core
versionNPM Version
useimport {Option, Picker} from '@clayui/core';

Example

import {Provider, Option, Picker} from '@clayui/core';
import Form from '@clayui/form';
import React from 'react';

import '@clayui/css/lib/css/atlas.css';

export default function App() {
	return (
		<Provider spritemap="/public/icons.svg">
			<div className="p-4">
				<div style={{width: '100px'}}>
					<Form.Group>
						<label htmlFor="picker" id="picker-label">
							Year
						</label>
						<Picker
							width={85}
							aria-labelledby="picker-label"
							id="picker"
							items={[
								'2020',
								'2021',
								'2022',
								'2023',
								'2024',
								'2025',
								'2026',
								'2027',
								'2028',
							]}
							placeholder="Year"
						>
							{(item) => <Option key={item}>{item}</Option>}
						</Picker>
					</Form.Group>
				</div>
			</div>
		</Provider>
	);
}

Introduction

The Picker component is visually similar to a DropDown component but developed specifically for the user to select options from the menu but following the w3c accessibility recommendations.

Like the TreeView, VerticalBar and other components this is a middle-level component rather than a low-level or high-level component. Unlike a DropDown, the Picker has simplified APIs and OOTB features to follow accessibility patterns for a combobox-only but following the same APIs pattern and allowing flexibility to customize the content of the options and the trigger.

Content

Content rendered in the <Picker /> Menu can be done in two different ways, static and dynamic content just like in DropDown, the choice depends on the use case.

Static

Static content is when the <Picker /> options do not change during the lifecycle of the application or are hardcoded options.

<Form.Group>
	<label htmlFor="picker" id="picker-label">
		Choose a fruit
	</label>
	<Picker aria-labelledby="picker-label" id="picker">
		<Option key="apple">Apple</Option>
		<Option disabled key="banana">
			Banana
		</Option>
		<Option key="mangos">Mangos</Option>
		<Option key="blueberry">Blueberry</Option>
	</Picker>
</Form.Group>

Dynamic

Unlike static content, dynamic content is when the options can change during the lifecycle of the application or when the data comes from a service. Dynamic content rendering is data agnostic, this allows you to configure how to render the component options regardless of the chosen data structure.

<Form.Group>
	<label htmlFor="picker" id="picker-label">
		Choose a fruit
	</label>
	<Picker
		aria-labelledby="picker-label"
		id="picker"
		items={['Apple', 'Banana', 'Mangos']}
	>
		{(item) => <Option key={item}>{item}</Option>}
	</Picker>
</Form.Group>

Controlled and Uncontrolled component

As with any component in Clay, controlled and uncontrolled components are the ability to manage state in the parent or let Clay control the state of the component. You can read more about this in our blog.

For the <Picker /> component you can control the selectedKey and active states by adding a state to your component, this is only advisable when you need this information otherwise don’t use it.

If you just need to set the initial state of the selectedKey, use the defaultSelectedKey property which is appropriate for that use case.

Info The selectedKey property is represented by the key property configured in the Option component, so the component can identify which value is selected and which should be shown in the trigger.


When the content rendering is dynamic and the data has id property defined, the component uses id instead of key.

function Example() {
	// Controlled
	const [active, setActive] = useState(false);

	// Controlled
	const [selectedKey, setSelectedKey] = useState('');

	return (
		<Form.Group>
			<label htmlFor="picker" id="picker-label">
				Choose a fruit
			</label>
			<Picker
				active={active}
				aria-labelledby="picker-label"
				id="picker"
				onActiveChange={setActive}
				onSelectionChange={setSelectedKey}
				selectedKey={selectedKey}
			>
				<Option key="apple">Apple</Option>
				<Option key="banana">Banana</Option>
				<Option key="mangos">Mangos</Option>
			</Picker>
		</Form.Group>
	);
}

Composition

The composition allows you to customize the component or add new features. See some examples:

Custom Trigger

import {Provider, Option, Picker, Icon} from '@clayui/core';
import {useId} from '@clayui/shared';
import Form from '@clayui/form';
import React from 'react';

import '@clayui/css/lib/css/atlas.css';

const Trigger = React.forwardRef(({children, ...otherProps}, ref) => (
	<div ref={ref} {...otherProps} tabIndex={0}>
		<Icon className="mr-2" symbol="user" />
		{children}
	</div>
));

export default function App() {
	const pickerId = useId();
	const labelId = useId();

	return (
		<Provider spritemap="/public/icons.svg">
			<div className="p-4">
				<div style={{width: '150px'}}>
					<Form.Group>
						<label htmlFor={pickerId} id={labelId}>
							Choose a user
						</label>
						<Picker
							aria-labelledby={labelId}
							as={Trigger}
							id={pickerId}
							items={['Liam', 'Noah', 'Oliver']}
						>
							{(item) => <Option key={item}>{item}</Option>}
						</Picker>
					</Form.Group>
				</div>
			</div>
		</Provider>
	);
}

Custom Options

import {Provider, Option, Picker, Text} from '@clayui/core';
import {useId} from '@clayui/shared';
import Layout from '@clayui/layout';
import Label from '@clayui/label';
import Form from '@clayui/form';
import React from 'react';

import '@clayui/css/lib/css/atlas.css';

export default function App() {
	const pickerId = useId();
	const labelId = useId();

	return (
		<Provider spritemap="/public/icons.svg">
			<div className="p-4">
				<div style={{width: '150px'}}>
					<Form.Group>
						<label htmlFor={pickerId} id={labelId}>
							Choose a user
						</label>
						<Picker
							aria-labelledby={labelId}
							id={pickerId}
							items={['Liam', 'Noah', 'Oliver']}
						>
							{(item) => (
								<Option key={item} textValue={item}>
									<Layout.ContentRow>
										<Layout.ContentCol expand>
											<Text size={3} weight="semi-bold">
												{item}
											</Text>
											<Text
												aria-hidden
												color="secondary"
												size={2}
											>
												Description
											</Text>
										</Layout.ContentCol>
										<Layout.ContentCol>
											<Label
												aria-hidden
												displayType="success"
											>
												Active
											</Label>
										</Layout.ContentCol>
									</Layout.ContentRow>
								</Option>
							)}
						</Picker>
					</Form.Group>
				</div>
			</div>
		</Provider>
	);
}

Group

import {Provider, Option, Picker} from '@clayui/core';
import {useId} from '@clayui/shared';
import DropDown from '@clayui/drop-down';
import Form from '@clayui/form';
import React from 'react';

import '@clayui/css/lib/css/atlas.css';

export default function App() {
	const pickerId = useId();
	const labelId = useId();

	return (
		<Provider spritemap="/public/icons.svg">
			<div className="p-4">
				<div style={{width: '150px'}}>
					<Form.Group>
						<label htmlFor={pickerId} id={labelId}>
							Select an option
						</label>
						<Picker
							aria-labelledby={labelId}
							id={pickerId}
							items={[
								{
									items: [
										{label: 'Apple', value: '1'},
										{label: 'Banana', value: '2'},
										{label: 'Mangos', value: '3'},
									],
									label: 'Fruit',
								},
								{
									items: [
										{label: 'Onions', value: '4'},
										{label: 'abc', value: '5'},
										{label: 'def', value: '6'},
									],
									label: 'Vegetable',
								},
							]}
						>
							{(group) => (
								<DropDown.Group
									header={group.label}
									items={group.items}
								>
									{(item) => (
										<Option key={item.value}>
											{item.label}
										</Option>
									)}
								</DropDown.Group>
							)}
						</Picker>
					</Form.Group>
				</div>
			</div>
		</Provider>
	);
}

Flexible width

import {Provider, Option, Picker} from '@clayui/core';
import Form from '@clayui/form';
import React from 'react';

import '@clayui/css/lib/css/atlas.css';

export default function App() {
	return (
		<Provider spritemap="/public/icons.svg">
			<div className="p-4">
				<div style={{width: '100px'}}>
					<Form.Group>
						<label htmlFor="picker" id="picker-label">
							Year
						</label>
						<Picker
							width={85}
							aria-labelledby="picker-label"
							id="picker"
							items={[
								'2020',
								'2021',
								'2022',
								'2023',
								'2024',
								'2025',
								'2026',
								'2027',
								'2028',
							]}
							placeholder="Year"
						>
							{(item) => <Option key={item}>{item}</Option>}
						</Picker>
					</Form.Group>
				</div>
			</div>
		</Provider>
	);
}

Hybrid component

Native select for mobile devices offers a better experience compared to Picker in some cases. The Picker offers the possibility to render using the native selector of the browser of the device when it is detected that it is on a mobile device, by default this property is disabled but it can be enabled by setting the native property to true.

Warning If the content of the Option component is not simple text, for it to work correctly set the property textValue to ensure that the component knows what the option label is.

<Form.Group>
	<label htmlFor="picker" id="picker-label">
		Choose a fruit
	</label>
	<Picker aria-labelledby="picker-label" id="picker" native>
		<Option key="apple">Apple</Option>
		<Option key="banana">Banana</Option>
		<Option key="mangos">Mangos</Option>
	</Picker>
</Form.Group>