Combobox

A field that is typed in to narrow a list, and a choice made from the list: one value, or many as tags.

Try it

One value. Type “and”.
Anthony Jackson
Brian Stewart
Charles Griffin
Elizabeth Hall
Justin Anderson
Laura Lee
Laura Martinez
Léa Martin
Patrick Moore
Sandra Brown
Many, as drawn: tags, a checkbox before every option, a row for all.
Anthony JacksonLaura LeeLéa Martin3 selected
All
Anthony Jackson
Brian Stewart
Charles Griffin
Elizabeth Hall
Justin Anderson
Laura Lee
Laura Martinez
Léa Martin
Patrick Moore
Sandra Brown

Choosing a combobox

A Select is for a list that can be read: a dozen services, five locations. A combobox is for one nobody wants to scroll — patients, staff, countries — where the reader already knows a piece of the name. It costs a field to type in, so under ten options or so it is the wrong trade.

The value is always from the list. What is typed is a way to an option and not an answer: leave the field with zzz in it and the last choice comes back; leave it empty and the choice is taken away. Free text with suggestions under it is another pattern, and is an Input.

What was typed, in the label

An option stays if its label holds what was typed anywhere in it, whatever the case or the accents, so and finds Sandra and lea finds Léa. Anywhere, and not only at the start, asks the eye why a row is there: the part that matched is in a heavier weight. Weight, not colour — the accent is the check’s. The first option that can be chosen is active as soon as something is typed, so Enter takes the best match. Nothing is completed in the field: it suggests and never completes, the rule of the site’s own search, because a field that types for you is a field that types the wrong thing.

Many

With multiple each choice is a tag in the field, which grows with them; every option has the Checkbox’s box before it; and selectAllLabel adds a first row that is checked, empty, or mixed. That row is hidden while something is typed, where “all” would not mean the ones in view. The list stays open after a choice, and what was typed is cleared, since it has done its work.

The drawn box is filled with the inverse surface; this one is the accent, because it is the Checkbox’s own and one checkbox is enough for a system. It is drawn again here as a picture and is not the component: an input inside an option is a control inside a control.

Anatomy

Combobox geometry, in pixels
PartValue
FieldInput’s box: 32, 40 or 48 tall, radius 12; it grows with its tags
Tag24 tall, a capsule on the raised surface, 12 / 16
A tag’s button20
Clear24, the least a target may be
Optionthe Select’s: 40 tall, radius 6
Checkbox20, the Checkbox’s own box
Mosteight options, then it scrolls

The tag is private to the Combobox for now. The system’s Tag is on the roadmap and not yet drawn; when it is, this becomes that.

Accessibility

The field is a text input with role="combobox" and aria-autocomplete="list" that owns a listbox; the focus never leaves it, and the active option is named to it. With many the listbox says it takes many, a change says how many are chosen, and the row for all says mixed. A tag’s button is reached by the pointer and not by Tab — six tags would be six stops before the field — because the keyboard has two other ways: Backspace takes the last away, and the list unchecks any. The Enter that commits an IME composition is the composition’s and chooses nothing.

Combobox, by keyboard
KeyDoes
TypingOpens the list, narrows it, and makes the best match active.
Down, UpOpen the list, and move over the options past what cannot be chosen.
Alt + DownOpens the list and makes nothing active.
Home, EndThe caret’s, until an option is active; then the first and the last.
EnterChooses the active option. With many, the list stays open.
EscCloses the list. Again, puts back what was typed over. Neither goes on to a Dialog around it.
BackspaceWith many and nothing typed, takes the last tag away.
Leaving the fieldWhat was typed and chose nothing is put back: the value is always from the list. Emptied, a single choice is taken away.

Props

TSX
import { Combobox, Avatar, Field } from 'alpenglow';

// One value.
<Field label="Patient">
  <Combobox
    name="patient"
    placeholder="Name or record number"
    options={patients}
    value={patient}
    onChange={setPatient}
    emptyText={(q) => `No patients match “${q}”`}
  />
</Field>

// Many: tags in the field, a checkbox before every option.
<Combobox multiple selectAllLabel="All" options={people} value={team} onChange={setTeam} />

// A server does the narrowing.
<Combobox filter={null} onInputChange={search} loading={pending} options={results} … />
Combobox props
PropTypeDefault
optionsSelectEntry[] — the Select’s options and groupsrequired
multiplebooleanfalse
value, defaultValuestring — or string[] with multiple—
onChange(value) => void—
placeholderstring—
filter(option, query) => boolean, or nullthe label holds it
onInputChange(query: string) => void—
loading, loadingTextboolean, stringfalse, 'Loading…'
emptyTextstring | (query) => string'No results'
clearable, clearLabelboolean, stringwith multiple, 'Clear'
selectAllLabelstring, with multiple—
removeLabel, countLabel(label) => string, (count) => stringEnglish
size, invalid, disabled, required, name, iconStartas the Select—
id, aria-label, aria-labelledby, aria-describedby, classNamestringfrom Field