Form Pill Group
A Form Pill Group is an editable set of Pills that represent a collection of selectable or removable objects.
const BasicFormPillGroup = () => {const pillState = useFormPillState();return (<form><FormPillGroup {...pillState} aria-label="Products:"><FormPill {...pillState}>Voice</FormPill><FormPill {...pillState}><ProductVideoIcon decorative />Video</FormPill><FormPill {...pillState}><ProductVerifyIcon decorative />Verify</FormPill></FormPillGroup></form>);};render(<BasicFormPillGroup />)
A Form Pill Group is an editable set of Pills that represents a collection of selectable and/or removable objects. They are used almost exclusively in multi-select editing situations.
A Form Pill Group can be used on its own to represent selection across a number of fields, such as showing currently applied filters. It can also be paired directly with an input field, such as a Combobox, to represent multiple selections.
The only accessibility requirement is providing the Pill Group a descriptive label via the aria-label
React prop.
The Form Pill Group is focusable, but only one pill is focusable at a time. This means the Pill Group is a single tab stop to a keyboard user. The dismiss button in a Form Pill is not a focusable element, but can be clickable.
Once a user focuses within the Form Pill Group, they can use these keyboard interactions:
Keyboard interaction | Action |
---|---|
Left and right arrow keys | Moving focus within the group |
Enter key | Triggers the supplied action via onClick |
Spacebar or enter keys | Selects a pill |
Delete or backspace keys | Dismisses a pill |
Form Pill Group creates a list from which a user may select items, whereas a Display Pill Group creates a list of static items.
Use the table below to get a better understanding of when to use Form Pill or Display Pill.
Functionality | Display Pill | Form Pill |
---|---|---|
Used to edit data in a form | ||
Uses Listbox, Option semantic | ||
Provides advanced keyboard navigation | ||
Pills can be selected | ||
Pills can perform an action | ||
Pills can be dismissed | ||
Used to view data | ||
Uses List, List Item semantic | ||
Pills can link to a page |
This Form Pill example shows the basic static component that's exported by Paste. A Form Pill can have an optional Avatar or Icon placed before the text.
Interaction states on Form Pills need to be managed separately as shown in the examples after this one by using the useFormPillState
hook. The returned state should be spread onto each component. This will provide you with some internal state management and keyboard navigation.
A Form Pill can have an optional Avatar or Icon placed before the text.
const BasicFormPillGroup = () => {const pillState = useFormPillState();return (<form><FormPillGroup {...pillState} aria-label="Products:"><FormPill {...pillState}>Voice</FormPill><FormPill {...pillState}><ProductVideoIcon decorative />Video</FormPill><FormPill {...pillState}><ProductVerifyIcon decorative />Verify</FormPill></FormPillGroup></form>);};render(<BasicFormPillGroup />)
Use a Selectable Form Pill to show an option that a user can select or deselect.
To make pills inside the Pill Group selectable, you can manage the selection state yourself and combine it with the state returned from the useFormPillState
hook. To do so, track which pill is selected in a separate store of state. When rendering the Form Pill Group, cross reference the rendered pills with the selection state, and conditionally set selected
on each FormPill
that requires it.
The onSelect
event handler will fire whenever the pill is clicked, or the spacebar or enter key is pressed. Use this to respond to your users' selection interactions.
const SelectableFormPillGroup = () => {const [pills] = React.useState(['SMS', 'MMS', 'Fax', 'Voice', 'Messaging', 'Chat']);const [selectedSet, updateSelectedSet] = React.useState(new Set(['MMS', 'Voice', 'Chat']));const pillState = useFormPillState();const iconMap = {['Fax']: <FaxCapableIcon decorative/>,['Voice']: <ProductVoiceIcon decorative/>,['Messaging']: <ProductMessagingIcon decorative/>,['Chat']: <ProductChatIcon decorative/>,}return (<form><FormPillGroup {...pillState} aria-label="Products:">{pills.map((pill) => (<FormPillkey={pill}{...pillState}selected={selectedSet.has(pill)}onSelect={() => {const newSelectedSet = new Set(selectedSet);if (newSelectedSet.has(pill)) {newSelectedSet.delete(pill);} else {newSelectedSet.add(pill);}updateSelectedSet(newSelectedSet);}}>{iconMap[pill]}{pill}</FormPill>))}</FormPillGroup></form>);};render(<SelectableFormPillGroup />)
Use a Dismissible Form Pill to show an option that a user can remove from the group. Once the pill is dismissed, it can’t be rerendered.
Form Pills are given a close button when provided an onDismiss
event handler. Because the Form Pill Group is largely presentational, provide dismissible functionality by managing the state of the rendered pills. By responding to user interactions and hooking into the onDismiss
event handler, the rendered state of the Form Pill Group can be updated and pills can be selectively removed from the collection.
The onDismiss
event handler will fire when a user clicks on the close button, or presses their backspace or delete key when focused on a pill.
const DismissableFormPillGroup = () => {const [pills, setPills] = React.useState(['Frontline', 'Phone Numbers', 'Authy']);const pillState = useFormPillState();const iconMap = {['Phone Numbers']: <ProductPhoneNumbersIcon decorative/>,}return (<form><FormPillGroup {...pillState} aria-label="Products:">{pills.map((pill, index) => (<FormPillkey={pill}{...pillState}onDismiss={() => {setPills(pills.filter((_, i) => i !== index));}}>{iconMap[pill]}{pill}</FormPill>))}</FormPillGroup></form>);};render(<DismissableFormPillGroup />)
Use a Selectable and Dismissible Form Pill to show an option that a user can select, deselect, or dismiss. Once the pill is dismissed, it can’t be re-rendered.
The onSelect
event handler will fire when a user clicks on the pill. The onDismiss
event handler will fire when a user clicks on the close button.
The onSelect
event handler will fire when a user presses the spacebar or enter keys. The onDismiss
event handler will fire when a user presses the delete or backspace keys.
const SelectableAndDismissableFormPillGroup = () => {const [pills, setPills] = React.useState(['Proxy', 'Interconnect', 'Trust Hub']);const [selectedSet, updateSelectedSet] = React.useState(new Set(['Interconnect']));const pillState = useFormPillState();const iconMap = {['Interconnect']: <ProductInterconnectIcon decorative/>,}return (<form><FormPillGroup {...pillState} aria-label="Products:">{pills.map((pill, index) => (<FormPillkey={pill}{...pillState}onDismiss={() => {setPills(pills.filter((_, i) => i !== index));if (selectedSet.has(pill)) {const newSelectedSet = new Set(selectedSet);newSelectedSet.delete(pill);updateSelectedSet(newSelectedSet);}}}selected={selectedSet.has(pill)}onSelect={() => {const newSelectedSet = new Set(selectedSet);if (newSelectedSet.has(pill)) {newSelectedSet.delete(pill);} else {newSelectedSet.add(pill);}updateSelectedSet(newSelectedSet);}}>{iconMap[pill]}{pill}</FormPill>))}</FormPillGroup></form>);};render(<SelectableAndDismissableFormPillGroup />)
To internationalize the form pill group, simply pass different text as children to the pills. The only exceptions to this are the visually hidden text that explains how to dismiss and select pills and the error label for the error variant. To change these, pass the i18nKeyboardControls
and i18nErrorLabel
props.
const I18nFormPillGroup = () => {const pillState = useFormPillState();return (<form><FormPillGroup{...pillState}aria-label="Votre sports favoris:"i18nKeyboardControls="Appuyez sur Supprimer ou Retour arrière pour supprimer. Appuyez sur Entrée pour basculer la sélection."><FormPill {...pillState} variant="error" i18nErrorLabel="(Erreur)">Le tennis</FormPill><FormPill {...pillState}>Le football américain</FormPill><FormPill {...pillState} variant="error" i18nErrorLabel="(Erreur)">Le ski</FormPill><FormPill {...pillState}>Le football</FormPill></FormPillGroup></form>);};render(<I18nFormPillGroup />)
The default state of a Form Pill indicates that the control is static or not selected.
const BasicFormPillGroup = () => {const pillState = useFormPillState();return (<form><FormPillGroup {...pillState} aria-label="Products:"><FormPill {...pillState}>Voice</FormPill><FormPill {...pillState}><ProductVideoIcon decorative />Video</FormPill><FormPill {...pillState}><ProductVerifyIcon decorative />Verify</FormPill></FormPillGroup></form>);};render(<BasicFormPillGroup />)
A Form Pill can be placed into a selected state by setting the selected
property.
const SelectedStateExample = () => {const pillState = useFormPillState();return (<form><FormPillGroup {...pillState} aria-label="Products:"><FormPill {...pillState} selected>Voice</FormPill><FormPill {...pillState} selected><ProductVideoIcon decorative />Video</FormPill><FormPill {...pillState} selected><ProductVerifyIcon decorative />Verify</FormPill></FormPillGroup></form>);};render(<SelectedStateExample />)
Use an Error Form Pill to highlight an object that the user must be made aware of because it’s considered to be in a bad or broken state and should be addressed. An error icon will display as a prefix to the rest of the children in the pill.
When used in a form field, display an error message below the form field to provide guidance on how to fix the error. For additional guidance on how to compose error messages, refer to the error state pattern.
const ErrorStateExample = () => {const pillState = useFormPillState();return (<form><FormPillGroup {...pillState} aria-label="Products:"><FormPill {...pillState} variant="error">Voice</FormPill><FormPill {...pillState} variant="error" selected><ProductVideoIcon decorative />Video</FormPill><FormPill {...pillState} variant="error"><ProductVerifyIcon decorative />Verify</FormPill><FormPill {...pillState} variant="error" selected>Usage</FormPill></FormPillGroup></form>);};render(<ErrorStateExample />)
Use a disabled Form Pill to indicate that a particular option cannot be interacted with or can be safely ignored.
const DisabledStateExample = () => {const pillState = useFormPillState();return (<form><FormPillGroup {...pillState} aria-label="Products:"><FormPill {...pillState} disabled>Voice</FormPill><FormPill {...pillState} disabled><ProductVideoIcon decorative />Video</FormPill><FormPill {...pillState} disabled><ProductVerifyIcon decorative />Verify</FormPill><FormPill {...pillState} disabled>Usage</FormPill></FormPillGroup></form>);};render(<DisabledStateExample />)
Pill text should never wrap to the next line. If the text length is longer than the max width of the pill group’s container, consider moving the Pill to a new row or use Truncate to truncate the Form Pill text.
const TruncateFormPillGroup = () => {const pillState = useFormPillState();return (<form><FormPillGroup {...pillState} aria-label="Products:"><FormPill {...pillState}><ProductInternetOfThingsIcon decorative /><Box maxWidth="size10"><Truncate title="Internet of Things">Internet of Things</Truncate></Box></FormPill><FormPill {...pillState}><ProductMarketingCampaignsIcon decorative /><Box maxWidth="size10"><Truncate title="Marketing Campaigns">Marketing Campaigns</Truncate></Box></FormPill><FormPill {...pillState}><ProductCodeExchangePartnerIcon decorative /><Box maxWidth="size10"><Truncate title="CodeExchange Partner">CodeExchange Partner</Truncate></Box></FormPill><FormPill {...pillState}><ProductEngagementIntelligencePlatformIcon decorative /><Box maxWidth="size10"><Truncate title="Engagement Intelligence Platform">Engagement Intelligence Platform</Truncate></Box></FormPill></FormPillGroup></form>);};render(<TruncateFormPillGroup />)
A Form Pill can have an optional Avatar. Considering the size of a Form Pill, it is recommended to use either an image or icon within an Avatar, and to avoid using initials as some initials may get cut off if the characters are particularly wide.
We recommend placing the Avatar ahead of the pill text. Use no more than one before or after the text.
const AvatarFormPillGroup = () => {const pillState = useFormPillState();return (<form><FormPillGroup {...pillState} aria-label="People:"><FormPill {...pillState}><Avatar size='sizeIcon30' name='Customer' src="/images/avatars/avatar4.png" />Customer</FormPill><FormPill {...pillState}><Avatar size='sizeIcon30' name='Agent' icon={AgentIcon} />Agent</FormPill></FormPillGroup></form>);};render(<AvatarFormPillGroup />)
A Form Pill can have an optional Icon. We recommend placing the Icon ahead of the pill text. Use no more than one before or after the text.
const IconFormPillGroup = () => {const pillState = useFormPillState();return (<form><FormPillGroup {...pillState} aria-label="Products:"><FormPill {...pillState}><ProductMessagingIcon decorative />Messaging</FormPill><FormPill {...pillState}><ProductBillingIcon decorative />Billing</FormPill><FormPill {...pillState}><ProductLookupIcon decorative />Lookup</FormPill><FormPill {...pillState}><ProductConversationsIcon decorative />Conversations</FormPill></FormPillGroup></form>);};render(<IconFormPillGroup />)
Use a Form Pill Group when you’re editing a collection of data within a form. It can be used to represent selection across multiple fields such as filtering, or from a single field like a Combobox.
Do
Use Form Pills inside of a form or when editing a collection of data.
Don't
Don’t use Form Pills in non-editable pages to represent a collection of similar objects, outside of a form, or outside of editing scenarios. Use a Display Pill Group instead.
Do
Only pass FormPill as a direct descendent of a FormPillGroup.
Don't
Don't pass FormPillGroup any other component type, and do not wrap FormPill in any other component of DOM element.