Skip to main content

Select Components

Select components provide comprehensive dropdown and multi-select functionality built on top of react-select with custom styling and React Hook Form integration for form validation and data management.

Component Overview

Core Components

Version Support

The components support multiple React Hook Form versions:

  • V7 - Latest components using React Hook Form v7
  • V2 - Enhanced components with improved styling
  • Legacy - Original components with basic functionality

MultiSelect

Primary multi-select component with search functionality and custom styling.

Component Location

import MultiSelect from 'components/Selects/MultiSelect';
import V2MultiSelect from 'components/Selects/V2MultiSelect'; // V2 version

Props

PropTypeDefaultDescription
optionsOption[]-Array of select options
valueOption | Option[]-Selected value(s)
onChangefunction-Change handler
placeholderstring-Placeholder text
isMultibooleanfalseEnable multi-select
isSearchablebooleantrueEnable search functionality
isLoadingbooleanfalseLoading state
isDisabledbooleanfalseDisabled state
isClearablebooleantrueEnable clear functionality
closeMenuOnSelectbooleantrueClose menu after selection
hideSelectedOptionsbooleanfalseHide selected options from list
allowSelectAllbooleanfalseEnable "Select All" option
allOptionOption-Custom "Select All" option
isInvalidbooleanfalseInvalid state for styling
componentsobject-Custom react-select components
optionalStylesobject-Additional custom styles

Usage Examples

Basic Multi-Select

import MultiSelect from 'components/Selects/MultiSelect';
import { useState } from 'react';

function BasicMultiSelect() {
const [selectedOptions, setSelectedOptions] = useState([]);

const options = [
{ value: 'option1', label: 'Option 1' },
{ value: 'option2', label: 'Option 2' },
{ value: 'option3', label: 'Option 3' },
];

return (
<MultiSelect
options={options}
value={selectedOptions}
onChange={setSelectedOptions}
placeholder='Select options...'
isMulti
/>
);
}

Single Select

import MultiSelect from 'components/Selects/MultiSelect';

function SingleSelect() {
const [selectedOption, setSelectedOption] = useState(null);

const options = [
{ value: 'kenya', label: 'Kenya' },
{ value: 'uganda', label: 'Uganda' },
{ value: 'nigeria', label: 'Nigeria' },
];

return (
<MultiSelect
options={options}
value={selectedOption}
onChange={setSelectedOption}
placeholder='Select a country...'
isMulti={false}
isClearable
/>
);
}

Multi-Select with Select All

import MultiSelect from 'components/Selects/MultiSelect';

function SelectAllExample() {
const [selectedEmployees, setSelectedEmployees] = useState([]);

const employees = [
{ value: 'emp1', label: 'John Doe' },
{ value: 'emp2', label: 'Jane Smith' },
{ value: 'emp3', label: 'Bob Johnson' },
];

const allOption = {
value: 'select-all',
label: 'Select All Employees',
};

return (
<MultiSelect
options={employees}
value={selectedEmployees}
onChange={setSelectedEmployees}
placeholder='Select employees...'
isMulti
allowSelectAll
allOption={allOption}
hideSelectedOptions={false}
/>
);
}

Loading and Disabled States

import MultiSelect from 'components/Selects/MultiSelect';

function LoadingDisabledExample() {
const [isLoading, setIsLoading] = useState(false);
const [isDisabled, setIsDisabled] = useState(false);

return (
<MultiSelect
options={options}
value={selectedValue}
onChange={handleChange}
placeholder='Loading options...'
isLoading={isLoading}
isDisabled={isDisabled}
isSearchable={!isLoading}
/>
);
}

SimpleSelect

Basic dropdown select component with rich option display.

Component Location

import SimpleSelect from 'components/Selects/Simple';

Props

PropTypeDefaultDescription
optionsOption[]-Array of select options
defaultValueOption-Default selected option
placeholderstring-Placeholder text
isSearchablebooleanfalseEnable search functionality
closeMenuOnSelectbooleantrueClose menu after selection
onChangefunction-Change handler

Option Structure

interface Option {
value: string;
label: {
name: string;
description?: string;
};
}

Usage Examples

Basic Simple Select

import SimpleSelect from 'components/Selects/Simple';

function PolicySelect() {
const [selectedPolicy, setSelectedPolicy] = useState(null);

const leavePolicies = [
{
value: 'annual',
label: {
name: 'Annual Leave',
description: 'Days are accrued annually at the start of the year',
},
},
{
value: 'monthly',
label: {
name: 'Monthly Accrual',
description: 'Days are accrued monthly based on worked days',
},
},
];

return (
<SimpleSelect
options={leavePolicies}
placeholder='Select leave policy...'
onChange={setSelectedPolicy}
defaultValue={leavePolicies[0]}
/>
);
}

Rich Option Display

import SimpleSelect from 'components/Selects/Simple';

function BenefitSelect() {
const benefitOptions = [
{
value: 'health',
label: {
name: 'Health Insurance',
description: 'Comprehensive health coverage for employees and family',
},
},
{
value: 'pension',
label: {
name: 'Pension Scheme',
description: 'Retirement savings plan with company matching',
},
},
];

return (
<SimpleSelect
options={benefitOptions}
placeholder='Select benefit...'
onChange={option => console.log(option)}
/>
);
}

WPSelect

React Hook Form integrated select component (V7).

Component Location

import WPSelect from 'components/Forms/V7/WPSelect';

Props

PropTypeDefaultDescription
namestring-Field name for form registration
labelstring-Select label text
controlControl-React Hook Form control object
rulesRegisterOptions-Validation rules
optionsOption[]-Array of select options
placeholderstring-Placeholder text
isDisabledbooleanfalseDisabled state
isLoadingbooleanfalseLoading state
isMultibooleanfalseEnable multi-select
isClearablebooleantrueEnable clear functionality
isSearchablebooleantrueEnable search functionality
onInputChangefunction-Search input change handler
formHelperTextstring-Helper text to display
sizestring-Input size
wstring | number-Width of the select

Usage Examples

Basic WPSelect

import WPSelect from 'components/Forms/V7/WPSelect';
import { useForm } from 'hook-form-7';

function FormSelect() {
const { control } = useForm();

const countryOptions = [
{ value: 'KE', label: 'Kenya' },
{ value: 'UG', label: 'Uganda' },
{ value: 'NG', label: 'Nigeria' },
];

return (
<WPSelect
name='country'
label='Country'
control={control}
options={countryOptions}
placeholder='Select a country...'
rules={{
required: 'Country is required',
}}
formHelperText='Select your country of residence'
/>
);
}

Multi-Select with Validation

import WPSelect from 'components/Forms/V7/WPSelect';

function MultiSelectForm() {
const { control } = useForm();

const skillOptions = [
{ value: 'react', label: 'React' },
{ value: 'typescript', label: 'TypeScript' },
{ value: 'nodejs', label: 'Node.js' },
];

return (
<WPSelect
name='skills'
label='Skills'
control={control}
options={skillOptions}
placeholder='Select your skills...'
isMulti
rules={{
required: 'At least one skill is required',
validate: value =>
value?.length >= 2 || 'Please select at least 2 skills',
}}
formHelperText='Select all applicable skills'
/>
);
}

Searchable Select with Loading

import WPSelect from 'components/Forms/V7/WPSelect';
import { useState, useEffect } from 'react';

function SearchableSelect() {
const { control } = useForm();
const [isLoading, setIsLoading] = useState(false);
const [options, setOptions] = useState([]);

const handleInputChange = inputValue => {
if (inputValue.length > 2) {
setIsLoading(true);
// Fetch options based on search
fetchOptions(inputValue).then(newOptions => {
setOptions(newOptions);
setIsLoading(false);
});
}
};

return (
<WPSelect
name='employee'
label='Employee'
control={control}
options={options}
placeholder='Search for employee...'
onInputChange={handleInputChange}
isLoading={isLoading}
rules={{
required: 'Employee selection is required',
}}
/>
);
}

CustomSelectDropdown

Custom styled dropdown component with advanced filtering.

Component Location

import CustomSelectDropdown from 'components/Selects/CustomSelect';

Props

PropTypeDefaultDescription
optionsOption[]-Array of select options
onChangefunction-Change handler
onVariantChangefunction-Variant change handler
variant'base' | 'filters''base'Select variant style
namestring-Select name/label
myValuestring-Current selected value
queryParamValuestring-Query parameter value
isDisabledbooleanfalseDisabled state
isClearablebooleantrueEnable clear functionality

Usage Examples

Base Variant Dropdown

import CustomSelectDropdown from 'components/Selects/CustomSelect';

function FilterDropdown() {
const [selectedStatus, setSelectedStatus] = useState('');

const statusOptions = [
{ value: 'active', label: 'Active' },
{ value: 'inactive', label: 'Inactive' },
{ value: 'pending', label: 'Pending' },
];

return (
<CustomSelectDropdown
options={statusOptions}
onChange={setSelectedStatus}
variant='base'
name='Status'
myValue={selectedStatus}
queryParamValue={selectedStatus}
/>
);
}

Filter Variant Dropdown

import CustomSelectDropdown from 'components/Selects/CustomSelect';

function TableFilter() {
const [department, setDepartment] = useState('all');

const departmentOptions = [
{ value: 'all', label: 'All Departments' },
{ value: 'hr', label: 'Human Resources' },
{ value: 'finance', label: 'Finance' },
{ value: 'engineering', label: 'Engineering' },
];

return (
<CustomSelectDropdown
options={departmentOptions}
onChange={setDepartment}
onVariantChange={setDepartment}
variant='filters'
name='Department'
myValue={department}
queryParamValue={department}
/>
);
}

TableMultiSelect

Optimized multi-select component for use in data tables.

Component Location

import TableMultiSelect from 'components/Selects/TableMultiSelect';

Props

PropTypeDefaultDescription
optionsOption[]-Array of select options
valueOption | Option[]-Selected value(s)
onChangefunction-Change handler
placeholderstring-Placeholder text
isMultibooleanfalseEnable multi-select
isSearchablebooleantrueEnable search functionality
closeMenuOnSelectbooleantrueClose menu after selection
allowSelectAllbooleanfalseEnable "Select All" option
componentsobject-Custom react-select components
optionalStylesobject-Additional custom styles

Usage Examples

Table Column Filter

import TableMultiSelect from 'components/Selects/TableMultiSelect';

function TableColumnFilter() {
const [selectedDepartments, setSelectedDepartments] = useState([]);

const departmentOptions = [
{ value: 'hr', label: 'Human Resources' },
{ value: 'finance', label: 'Finance' },
{ value: 'engineering', label: 'Engineering' },
];

return (
<TableMultiSelect
options={departmentOptions}
value={selectedDepartments}
onChange={setSelectedDepartments}
placeholder='Filter by department...'
isMulti
closeMenuOnSelect={false}
optionalStyles={{
container: provided => ({
...provided,
minWidth: '200px',
}),
}}
/>
);
}

Specialized Selects

Domain-specific select components for common use cases.

RedeemTypeSelect

Select component for overtime redemption types.

import RedeemTypeSelect from 'components/Selects/RedeemTypeSelect';

function OvertimeForm() {
const { control } = useForm();

return (
<RedeemTypeSelect
control={control}
isDisabled={false}
name='redeemType'
rules={{ required: 'Redeem type is required' }}
label='How do you want to redeem?'
placeholder='Select redeem type'
formHelperText='Choose how you want to redeem your overtime'
/>
);
}

AttendanceReportTypeSelect

Select component for attendance report types.

import AttendanceReportTypeSelect from 'components/Selects/AttendanceReportTypeSelect';

function AttendanceReportForm() {
const { control } = useForm();

return (
<AttendanceReportTypeSelect
control={control}
isDisabled={false}
name='reportType'
rules={{ required: 'Report type is required' }}
/>
);
}

FormShiftsSelect

Select component for shift selection with search.

import FormShiftSelect from 'components/Selects/FormShiftsSelect';

function ShiftAssignmentForm() {
const { control } = useForm();

return (
<FormShiftSelect
control={control}
isDisabled={false}
name='shift'
rules={{ required: 'Shift selection is required' }}
isClearable
isMulti={false}
/>
);
}

TAScheduleFrequencySelect

Select component for time attendance schedule frequency.

import TAScheduleFrequencySelect from 'components/Selects/TAScheduleFrequencySelect';

function ScheduleForm() {
const { control } = useForm();

return (
<TAScheduleFrequencySelect
control={control}
isDisabled={false}
name='frequency'
rules={{ required: 'Frequency is required' }}
scheduleType='shift' // 'shift' | 'off_day' | 'rest_day'
/>
);
}

Option Structures

Basic Option

interface BasicOption {
value: string | number;
label: string;
}

Rich Option

interface RichOption {
value: string | number;
label: {
name: string;
description?: string;
};
}

Employee Option

interface EmployeeOption {
value: string;
label: string;
data?: {
profile_picture?: string;
employee_no?: string;
designation_name?: string;
};
flag?: ReactNode; // Country flag component
}

Grouped Options

interface GroupedOption {
label: string; // Group label
options: BasicOption[];
}

Custom Components

Custom Option Component

import { components } from 'react-select';
import { HStack, Text, Avatar } from '@chakra-ui/react';

const CustomOption = props => {
return (
<components.Option {...props}>
<HStack spacing={3}>
<Avatar
size='sm'
name={props.label}
src={props.data?.profile_picture}
/>
<div>
<Text fontWeight='medium'>{props.label}</Text>
<Text fontSize='sm' color='gray.500'>
{props.data?.employee_no}
</Text>
</div>
</HStack>
</components.Option>
);
};

// Usage
<MultiSelect
options={employeeOptions}
components={{ Option: CustomOption }}
// ... other props
/>;

Custom MultiValue Component

import { components } from 'react-select';
import { Tag, TagLabel, TagCloseButton } from '@chakra-ui/react';

const CustomMultiValue = props => {
return (
<Tag size='sm' variant='solid' colorScheme='green' m={1}>
&lt;TagLabel&gt;{props.data.label}</TagLabel>
<TagCloseButton onClick={props.removeProps.onClick} />
</Tag>
);
};

// Usage
<MultiSelect
isMulti
options={options}
components={{ MultiValue: CustomMultiValue }}
// ... other props
/>;

Styling and Theming

Custom Styles

const customStyles = {
control: (provided, state) => ({
...provided,
borderColor: state.isFocused ? '#62a446' : '#e2e8f0',
boxShadow: state.isFocused ? '0 0 0 1px #62a446' : 'none',
'&:hover': {
borderColor: '#62a446',
},
}),
option: (provided, state) => ({
...provided,
backgroundColor: state.isSelected ? '#62a446' : 'white',
color: state.isSelected ? 'white' : '#2d3748',
'&:hover': {
backgroundColor: state.isSelected ? '#62a446' : '#f7fafc',
},
}),
multiValue: provided => ({
...provided,
backgroundColor: '#f0fff4',
}),
multiValueLabel: provided => ({
...provided,
color: '#276749',
}),
multiValueRemove: provided => ({
...provided,
color: '#276749',
'&:hover': {
backgroundColor: '#68d391',
color: 'white',
},
}),
};

// Usage
<MultiSelect
options={options}
optionalStyles={customStyles}
// ... other props
/>;

Theme Integration

import { useTheme } from '@chakra-ui/react';

function ThemedSelect() {
const theme = useTheme();

const selectStyles = {
control: provided => ({
...provided,
borderColor: theme.colors.gray[300],
'&:hover': {
borderColor: theme.colors.green[500],
},
}),
};

return (
<MultiSelect
options={options}
optionalStyles={selectStyles}
// ... other props
/>
);
}

Form Integration

React Hook Form V7

import WPSelect from 'components/Forms/V7/WPSelect';
import { useForm } from 'hook-form-7';

function SelectForm() {
const { control, handleSubmit } = useForm();

const onSubmit = data => {
console.log(data);
};

return (
<form onSubmit={handleSubmit(onSubmit)}>
<WPSelect
name='department'
label='Department'
control={control}
options={departmentOptions}
rules={{
required: 'Department is required',
}}
/>

<WPSelect
name='skills'
label='Skills'
control={control}
options={skillOptions}
isMulti
rules={{
required: 'At least one skill is required',
validate: value => value?.length <= 5 || 'Maximum 5 skills allowed',
}}
/>

<button type='submit'>Submit</button>
</form>
);
}

Manual Form Integration

import MultiSelect from 'components/Selects/MultiSelect';
import { useForm } from 'react-hook-form';

function ManualFormIntegration() {
const { register, setValue, watch, handleSubmit } = useForm();
const selectedOptions = watch('options');

const handleSelectChange = selected => {
setValue('options', selected);
};

return (
<form onSubmit={handleSubmit(console.log)}>
<input type='hidden' {...register('options', { required: true })} />

<MultiSelect
options={options}
value={selectedOptions}
onChange={handleSelectChange}
placeholder='Select options...'
/>

<button type='submit'>Submit</button>
</form>
);
}

Validation Patterns

Common Validation Rules

const selectValidationRules = {
required: 'Selection is required',

// Minimum selections for multi-select
minSelections: value =>
value?.length >= 2 || 'Please select at least 2 options',

// Maximum selections for multi-select
maxSelections: value => value?.length <= 5 || 'Maximum 5 selections allowed',

// Custom validation based on other fields
conditionalRequired: (value, { otherField }) =>
!otherField || value || 'This field is required when other field is set',

// Validate specific values
allowedValues: value => {
const allowed = ['option1', 'option2', 'option3'];
return allowed.includes(value?.value) || 'Invalid selection';
},
};

Conditional Select Validation

import WPSelect from 'components/Forms/V7/WPSelect';

function ConditionalValidation() {
const { control, watch } = useForm();
const employmentType = watch('employmentType');

return (
<>
<WPSelect
name='employmentType'
label='Employment Type'
control={control}
options={employmentTypeOptions}
rules={{ required: 'Employment type is required' }}
/>

<WPSelect
name='benefits'
label='Benefits'
control={control}
options={benefitsOptions}
isMulti
rules={{
validate: value => {
if (employmentType?.value === 'contractor') {
return (
!value?.length || 'Contractors are not eligible for benefits'
);
}
return value?.length > 0 || 'Please select at least one benefit';
},
}}
/>
</>
);
}

Performance Optimization

Memoized Options

import { useMemo } from 'react';
import MultiSelect from 'components/Selects/MultiSelect';

function OptimizedSelect({ rawData }) {
const options = useMemo(
() =>
rawData.map(item => ({
value: item.id,
label: item.name,
})),
[rawData],
);

return (
<MultiSelect
options={options}
// ... other props
/>
);
}

Virtualized Large Lists

import { FixedSizeList } from 'react-window';
import { components } from 'react-select';

const VirtualizedMenuList = props => {
const { children, maxHeight, getValue } = props;
const [value] = getValue();
const initialOffset =
children.indexOf(children.find(child => child.props.isFocused)) * 35;

return (
<FixedSizeList
height={Math.min(maxHeight, children.length * 35)}
itemCount={children.length}
itemSize={35}
initialScrollOffset={initialOffset}
>
{({ index, style }) => <div style={style}>{children[index]}</div>}
</FixedSizeList>
);
};

// Usage for large option lists
<MultiSelect
options={largeOptionsList}
components={{ MenuList: VirtualizedMenuList }}
// ... other props
/>;

Accessibility Features

  • Keyboard Navigation: Full keyboard support for option selection
  • Screen Reader Support: Proper ARIA labels and announcements
  • Focus Management: Logical focus order and visual indicators
  • Option Announcements: Screen readers announce selected/deselected options
  • High Contrast: Support for high contrast themes
  • Live Regions: Dynamic content updates are announced

Best Practices

  1. Option Structure: Use consistent option structures across the application
  2. Loading States: Show loading indicators for async data fetching
  3. Error Handling: Display clear error messages for validation failures
  4. Search Optimization: Implement efficient search for large option lists
  5. Accessibility: Always provide proper labels and ARIA attributes
  6. Performance: Memoize options and use virtualization for large lists
  7. Validation: Implement both client-side and server-side validation
  8. User Experience: Provide clear feedback for user actions

Migration Notes

From Legacy to V2

  1. Styling: Updated styling with improved theming support
  2. Performance: Better performance for large option lists
  3. Accessibility: Enhanced accessibility features
  4. API: Some prop names and behaviors may have changed

From V6 to V7 React Hook Form

  1. Control Object: Use control prop instead of register
  2. Validation: Pass validation rules directly to component
  3. Error Handling: Errors are handled automatically by component
  4. Value Management: Component manages its own value state