PeoplePickerField
<PeoplePickerField />
is an input that provides a way to search for and select individuals or groups of people using an <AutocompleteField />
-based experience.Item | Name | Description |
---|---|---|
A | <TextField /> | The base input of the <PeoplePickerField /> |
B | <FilterTag /> | A selection represented by a <FilterTag /> using a name paired with either an individual's <Avatar /> or the <People /> icon for a group |
C | Hidden selections count | The number of selections that are hidden when the input is not focused |
D | Value | The placeholder or typed text string used for filtering the displayed options |
E | <Spinner /> | Rendered when loading is true to give a visual cue that the options are loading |
F | <IconButton /> | The <Close /> icon is used within <IconButton /> to clear the entered and selected values |
Item | Name | Description |
---|---|---|
A | <Check /> | Indicates that the option is selected |
B | <Persona /> | The content of an option within <PeoplePickerField /> |
A basic
PeoplePickerField
implementation using Formik and Yup to validate that a person has been selected.import React, { useMemo, useState } from 'react'import { Formik } from 'formik'import * as Yup from 'yup'import {FormLayout,PeoplePickerField,SubmitButton,} from '@gusto/workbench-formik'import { getRandomEmployee, useEmployeesQuery } from './_testData'export const Basic = () => {// 👇 track state of filter input query by passing `searchTerm` to `onDebouncedChange` propconst [searchTerm, setSearchTerm] = useState('')// 👇 fetch employees from an API, based on the current search termconst { data, loading } = useEmployeesQuery(searchTerm)// 👇 format each employee to match `PeoplePickerOption` type;const formattedFilteredEmployees = useMemo(() =>data?.map(employee => ({// 👇 link to custom avatar here to use a unique image instead of the default Gusto illustrationavatarSrc: employee.Avatar,// 👇 secondary line of text, displayed below the label in the option viewdetails: employee.Department,// 👇 displayed text of the optionlabel: employee['Name-FL'],// 👇 unique value of the option (i.e. UUID)value: employee.Email,})),[data],)return (<FormikinitialValues={{ employees: [] }}validationSchema={Yup.object().shape({employees: Yup.array().required('Required to pick an employee'),})}onSubmit={async values => {await new Promise(resolve => {setTimeout(resolve, 300 + Math.random() * 100)})alert(JSON.stringify(values, null, 2))}}><FormLayoutfields={<PeoplePickerFieldrequiredonDebouncedChange={e => setSearchTerm(e.target.value)}name="employees"label="Employees"placeholder="Add an employee"loading={loading}options={formattedFilteredEmployees ?? []}/>}actions={<SubmitButton>Submit</SubmitButton>}/></Formik>)}
import React, { useMemo, useState } from 'react'import { Formik } from 'formik'import * as Yup from 'yup'import type { PeoplePickerOption } from '@gusto/workbench'import {FormLayout,PeoplePickerField,SubmitButton,} from '@gusto/workbench-formik'import { getRandomEmployee, useEmployeesQuery } from './_testData'const initialEmployees = [getRandomEmployee(), getRandomEmployee()].map(employee => ({details: employee.Department,label: employee['Name-FL'],value: employee.Email,}),)/*** To make initial selections in the Formik PeoplePickerField, set the `initialValues` prop on the Formik* component to the initial values you want to pre-populate the field with. In order to render the selected* tags with the appropriate label and avatar, you must also pass the selected options to the* `selectedOptions` prop.*/export const WithInitialSelections = () => {const [searchTerm, setSearchTerm] = useState('')// 👇 fetch employees from an API, based on the current search termconst { data, loading } = useEmployeesQuery(searchTerm)// 👇 format each employee to match `PeoplePickerOption` type;const formattedFilteredEmployees = useMemo(() =>data?.map(employee => ({details: employee.Department,label: employee['Name-FL'],value: employee.Email,})) ?? [],[data],)// Because we're setting initial values and Formik only has access to the id strings of// the selected options, we need to take over display state management for the selected// options so that the label and avatar data can be used.// We start with the same initial employees array as the formik initialValues is using,// and then update the selected options as the user interacts with the field via// the onSelectedOptionsChange prop.const [selectedEmployeeOptions, setSelectedEmployeeOptions] =useState<PeoplePickerOption[]>(initialEmployees)return (<Formik// 👇 initialValues is Formik's initial state of the id or value of the employees when the form is submittedinitialValues={{employees: initialEmployees.map(employee => employee.value),}}validationSchema={Yup.object().shape({employees: Yup.array().required('Required to pick an employee'),})}onSubmit={async values => {await new Promise(resolve => {setTimeout(resolve, 300 + Math.random() * 100)})alert(JSON.stringify(values, null, 2))}}><FormLayoutfields={<PeoplePickerFieldrequiredonDebouncedChange={e => setSearchTerm(e.target.value)}name="employees"label="Employees"placeholder="Add an employee"loading={loading}// 👇 options is the filtered list of employees to select fromoptions={formattedFilteredEmployees}// 👇 set the initial options with the full PeoplePickerOption dataselectedOptions={selectedEmployeeOptions}// 👇 keep the selectedOptions in sync with the Formik values by updating the stored// state with full data when selected options changeonSelectedOptionsChange={selectedOptions =>setSelectedEmployeeOptions(selectedOptions)}/>}actions={<SubmitButton>Submit</SubmitButton>}/></Formik>)}
Options for
PeoplePickerField
can either be individual people or groups of people. The &quo;group&quo; variant is used to visually represent an option that represents a group of people based on an attribute.import React, { useMemo, useState } from 'react'import { Formik } from 'formik'import * as Yup from 'yup'import {FormLayout,PeoplePickerField,SubmitButton,} from '@gusto/workbench-formik'import { getRandomEmployee, useDepartmentsQuery } from './_testData'export const WithGroupVariantOptions = () => {const [searchTerm, setSearchTerm] = useState('')const { data, loading } = useDepartmentsQuery(searchTerm)const departments = useMemo(() =>data?.map(department =>({details:department.Count === 1? `${department.Count} employee`: `${department.Count} employees`,label: department.Name,value: department.Name,// 👇 this sets the <People /> icon on the option and filtertagvariant: 'group',}) as const,),[data],)return (<FormikinitialValues={{ departments: [] }}validationSchema={Yup.object().shape({departments: Yup.array().required('Required to pick a department'),})}onSubmit={async values => {await new Promise(resolve => {setTimeout(resolve, 300 + Math.random() * 100)})alert(JSON.stringify(values, null, 2))}}><FormLayoutfields={<PeoplePickerFieldrequiredonDebouncedChange={e => setSearchTerm(e.target.value)}name="departments"label="Departments"placeholder="Add a department"loading={loading}// 👇 pass in the options configured with groupsoptions={departments ?? []}/>}actions={<SubmitButton>Submit</SubmitButton>}/></Formik>)}
Options for
PeoplePickerField
can be visually grouped together in the listbox, which can make navigation easier by separating options into collections based on a shared attribute. This example shows employees grouped by their type of employment.import React, { useMemo, useState } from 'react'import { Formik } from 'formik'import type {PeoplePickerOption,PeoplePickerOptionGroup,} from '@gusto/workbench'import {FormLayout,PeoplePickerField,SubmitButton,} from '@gusto/workbench-formik'import {type Employee,getRandomEmployee,useEmployeesQuery,} from './_testData'export const WithOptionGroups = () => {// 👇 track state of filter input query by passing `searchTerm` to `onDebouncedChange` propconst [searchTerm, setSearchTerm] = useState('')// 👇 fetch employees from an API, based on current queryconst { data, loading } = useEmployeesQuery(searchTerm)// 👇 `options` need to be PeoplePickerOptions or PeoplePickerOptionGroup typeconst formattedAndGroupedOptions = useMemo(() => {const sortedEmployees =data?.sort((a, b) => a.Employment.localeCompare(b.Employment)) ?? []const groups: Record<string, Employee[]> = {}// sort employees employment type and create groups by employment typesortedEmployees.forEach((employee: Employee) => {const employmentType = employee.Employmentconst employeesInGroup = groups[employmentType] ?? []employeesInGroup.push(employee)groups[employmentType] = employeesInGroup})const options: PeoplePickerOptionGroup[] = []// format optionsObject.keys(groups).forEach(employmentType => {if (employmentType) {options.push({label: employmentType,options: groups[employmentType].map((employee: Employee): PeoplePickerOption => ({label: employee['Name-FL'],value: employee.Email,variant: 'individual',}),),})}})return options}, [data])return (<FormikinitialValues={{ employees: [] }}onSubmit={values => {alert(JSON.stringify(values, null, 2))return Promise.resolve()}}><FormLayoutfields={<PeoplePickerFieldname="employees"label="Employees"placeholder="Search for an employee..."onDebouncedChange={e => setSearchTerm(e.target.value)}loading={loading}options={formattedAndGroupedOptions}/>}actions={<SubmitButton>Submit</SubmitButton>}/></Formik>)}
PeoplePickerField
is multi-select by default, but it can be rendered as a single-select field if only one person is to be selected.import React, { useMemo, useState } from 'react'import { Formik } from 'formik'import {FormLayout,PeoplePickerField,SubmitButton,} from '@gusto/workbench-formik'import { getRandomEmployee, useEmployeesQuery } from './_testData'export const AsSingleSelect = () => {const [searchTerm, setSearchTerm] = useState('')const { data, loading } = useEmployeesQuery(searchTerm)const employees = useMemo(() =>data?.map(employee => ({label: employee['Name-FL'],value: employee.Email,})) ?? [],[data],)return (<FormikinitialValues={{ employee: '' }}onSubmit={values => {alert(JSON.stringify(values, null, 2))return Promise.resolve()}}><FormLayoutfields={<PeoplePickerFieldlabel="Employee"// 👇 set to single-select heremultiple={false}name="employee"placeholder="Search for an employee..."onDebouncedChange={e => setSearchTerm(e.target.value)}options={employees}loading={loading}/>}actions={<SubmitButton>Submit</SubmitButton>}/></Formik>)}
Name | Type | Default | Description |
---|---|---|---|
debounceDelay | number | 200 | In milliseconds, the amount of time between value changes before the onDebouncedChange event is triggered |
defaultSelectedOptions | PeoplePickerOption[] | - | The default options selected on initial render. Note: When using the Formik version, you must use selectedOptions and Formik initialValues instead. |
fit | content container | container | Determines the point of reference for the width of the component. If set to content , the size will be set by the size of the input. If set to container , the size will expand to fit the containing element. |
helperText | ReactNode | - | A brief description or hint for the input ; can be used to link to an external resource. |
invalid | boolean | - | If true , the field will be shown in an invalid state. |
label Required | string | - | Label associated with the input . |
loading | boolean | false | If true , the loading Spinner is displayed. |
multiple | boolean | true | Determines if field is rendered with multiple select or single select. Set to false to only allow one option to be selected. |
onClear | () => void | - | Called when the ESCAPE key is pressed or the clear button is clicked. |
onDebouncedChange | (e: React.ChangeEvent<HTMLInputElement>) => void | - | Called after the debounceDelay has passed since the last input value change. |
onSelectedOptionsChange | (options: PeoplePickerOption[]) => void | - | Called when the selected options change. |
optional | boolean | - | Used to indicate the field is optional |
options Required | PeoplePickerOptionOrGroup[] | - | Options that are available for selection. Can be a flat list, with an array of PeoplePickerOption objects, or a grouped list, with an array of PeoplePickerOptionGroup objects. A group should include a label property for the group heading and an options property with an array of PeoplePickerOption objects. |
selectedOptions | PeoplePickerOption[] | - | The data required to render the selected options when the component state is controlled externally. Note: When using the workbench-formik component, this should be derived from the Formik field's value . |
validationText | ReactNode | - | Validation message associated with the input . |