Dropdown
Present a list of options to the user to select from.
Props
name
string
Identifier for the dropdown. Should be unique.
ariaLabel
string
Defines how the selected value will be translated for the screen reader. If not specified it will fall back to the name.
ariaLabelledBy
string
The aria-labelledby attribute identifies the element(or elements) that labels the dropdown it is applied to. Normally it is the id of the label.
value
string | undefined
Stores the value of the item selected from the dropdown.
filterable
boolean
When true the dropdown will have the ability to filter options by typing into the input field.
Defaults to
false.
leadingIcon
GoAIconType
Show an icon to the left of the dropdown option.
maxHeight
string
Maximum height of the dropdown menu items popover. Non-native only.
Defaults to
276px.
placeholder
string
The text displayed for the dropdown before a selection is made. Non-native only.
width
string
Overrides the autosized menu width. Non-native only.
maxWidth
string
Sets the maximum width of the dropdown. Use a CSS unit (px, %, ch, rem, em).
disabled
boolean
Disable this control.
Defaults to
false.
error
boolean
Show an error state.
Defaults to
false.
multiselect
boolean
When true, allows multiple items to be selected.
Defaults to
false.
native
boolean
When true will render the native select HTML element.
Defaults to
false.
size
default | compact
Sets the size of the dropdown. Compact reduces height for dense layouts.
Defaults to
default.
version
1 | 2
The design system version for styling purposes.
Defaults to
1.
relative
string
This property has no effect and will be removed in a future version.
autoComplete
string
Specifies the autocomplete attribute for the dropdown input. Native only.
testId
string
Sets a data-testid attribute for automated testing.
disableGlobalClosePopover
boolean
Prevents the popover from closing when clicking outside. Used for nested dropdowns or complex interactions.
Defaults to
false.
mt, mr, mb, ml
none | 3xs | 2xs | xs | s | m | l | xl | 2xl | 3xl | 4xl
Apply margin to the top, right, bottom, and/or left of the component.
Events
onChange
(event: Event) => void
_change
CustomEvent
const [open, setOpen] = useState(false);
const [type, setType] = useState<string>();
const [name, setName] = useState<string>();
const [description, setDescription] = useState<string>();<GoabButton type="tertiary" leadingIcon="add" onClick={() => setOpen(true)}>
Add another item
</GoabButton>
<GoabModal
heading="Add a new item"
open={open}
actions={
<GoabButtonGroup alignment="end">
<GoabButton type="tertiary" size="compact" onClick={() => setOpen(false)}>
Cancel
</GoabButton>
<GoabButton type="primary" size="compact" onClick={() => setOpen(false)}>
Save new item
</GoabButton>
</GoabButtonGroup>
}
>
<p>Fill in the information to create a new item</p>
<GoabFormItem label="Type" mt="l">
<GoabDropdown onChange={(e) => setType(e.value)} value={type}>
<GoabDropdownItem value="1" label="Option 1" />
<GoabDropdownItem value="2" label="Option 2" />
</GoabDropdown>
</GoabFormItem>
<GoabFormItem label="Name" mt="l">
<GoabInput
onChange={(e) => setName(e.value)}
value={name}
name="name"
width="100%"
/>
</GoabFormItem>
<GoabFormItem label="Description" mt="l">
<GoabTextarea
name="description"
rows={3}
width="100%"
onChange={(e) => setDescription(e.value)}
value={description}
/>
</GoabFormItem>
</GoabModal>Add a record using a drawer
const [open, setOpen] = useState(false);<GoabButton leadingIcon="add" onClick={() => setOpen(true)}>
Add Record
</GoabButton>
<GoabDrawer
maxSize="492px"
open={open}
heading="Add Record"
position="right"
onClose={() => setOpen(false)}
actions={
<GoabButtonGroup>
<GoabButton type="primary" size="compact" onClick={() => setOpen(false)}>
Add record
</GoabButton>
<GoabButton type="tertiary" size="compact" onClick={() => setOpen(false)}>
Cancel
</GoabButton>
</GoabButtonGroup>
}
>
<GoabFormItem label="Level of education">
<GoabDropdown onChange={() => {}} name="education" value="university">
<GoabDropdownItem value="high-school" label="High School Diploma" />
<GoabDropdownItem value="college" label="College Diploma" />
<GoabDropdownItem value="university" label="University Degree" />
<GoabDropdownItem value="masters" label="Master's Degree" />
<GoabDropdownItem value="doctorate" label="Doctorate" />
</GoabDropdown>
</GoabFormItem>
<GoabFormItem label="Educational institution" mt="l">
<GoabInput name="education" type="text" onChange={() => {}} />
</GoabFormItem>
<GoabFormItem label="Field of study" requirement="optional" mt="l">
<GoabInput name="fieldOfStudy" type="text" onChange={() => {}} />
</GoabFormItem>
<GoabFormItem label="Is the person currently attending?" mt="l">
<GoabRadioGroup name="attendTraining" orientation="horizontal" onChange={() => {}}>
<GoabRadioItem value="yes" label="Yes" />
<GoabRadioItem value="no" label="No" />
</GoabRadioGroup>
</GoabFormItem>
<GoabFormItem label="Start date" mt="l">
<GoabDatePicker onChange={() => {}} value={new Date("2022-09-01")} />
<GoabCheckbox name="startDateApproximate" text="Approximate date" value="y" mt="s" />
</GoabFormItem>
<GoabFormItem label="Credential received?" mt="l">
<GoabRadioGroup name="credentialReceived" orientation="horizontal" onChange={() => {}}>
<GoabRadioItem value="yes" label="Yes" />
<GoabRadioItem value="no" label="No" />
</GoabRadioGroup>
</GoabFormItem>
</GoabDrawer>Add and edit lots of filters
const [open, setOpen] = useState(false);<GoabButton onClick={() => setOpen(true)}>Filters</GoabButton>
<GoabDrawer
heading="Filters"
open={open}
onClose={() => setOpen(false)}
position="right"
actions={
<GoabButtonGroup>
<GoabButton type="primary" size="compact" onClick={() => setOpen(false)}>
Apply filters
</GoabButton>
<GoabButton type="tertiary" size="compact" onClick={() => setOpen(false)}>
Cancel
</GoabButton>
</GoabButtonGroup>
}
>
<GoabFormItem label="Entry status">
<GoabCheckboxList name="entryStatus" onChange={() => {}}>
<GoabCheckbox name="draft" text="Draft" value="draft" />
<GoabCheckbox name="published" text="Published" value="published" />
</GoabCheckboxList>
</GoabFormItem>
<GoabFormItem label="Assigned to - Region" mt="l">
<GoabCheckboxList name="region" onChange={() => {}}>
<GoabCheckbox name="calgary" text="Calgary" value="calgary" />
<GoabCheckbox name="central" text="Central" value="central" />
<GoabCheckbox name="edmonton" text="Edmonton" value="edmonton" />
<GoabCheckbox name="north" text="North" value="north" />
<GoabCheckbox name="south" text="South" value="south" />
</GoabCheckboxList>
</GoabFormItem>
<GoabFormItem label="Assigned to" mt="l">
<GoabDropdown name="assignedTo" onChange={() => {}}>
<GoabDropdownItem value="1" label="Person 1" />
<GoabDropdownItem value="2" label="Person 2" />
</GoabDropdown>
</GoabFormItem>
<GoabFormItem label="Date taken" mt="l">
<GoabRadioGroup name="dateTaken" onChange={() => {}}>
<GoabRadioItem value="24" label="Last 24 hours" />
<GoabRadioItem value="72" label="Last 72 hours" />
</GoabRadioGroup>
</GoabFormItem>
</GoabDrawer>const [address, setAddress] = useState("");
const [suite, setSuite] = useState("");
const [city, setCity] = useState("");
const [province, setProvince] = useState("");
const [postalCode, setPostalCode] = useState("");<GoabText size="heading-l" mt="none" mb="xl">What is your address?</GoabText>
<GoabFormItem label="Street Address">
<GoabInput
name="address"
type="text"
value={address}
onChange={(e) => setAddress(e.value)}
width="100%"
/>
</GoabFormItem>
<GoabFormItem label="Suite or unit #" mt="l">
<GoabInput
name="suite"
type="text"
value={suite}
onChange={(e) => setSuite(e.value)}
width="100%"
/>
</GoabFormItem>
<GoabFormItem label="City or town" mt="l">
<GoabInput
name="city"
type="text"
value={city}
onChange={(e) => setCity(e.value)}
width="100%"
/>
</GoabFormItem>
<GoabBlock direction="row" gap="l" mt="l">
<GoabFormItem label="Province or territory">
<GoabDropdown
onChange={(e) => setProvince(e.value ?? "")}
name="province"
value={province}
>
<GoabDropdownItem label="Alberta" value="AB" />
<GoabDropdownItem label="British Columbia" value="BC" />
<GoabDropdownItem label="Manitoba" value="MB" />
<GoabDropdownItem label="New Brunswick" value="NB" />
<GoabDropdownItem label="Newfoundland and Labrador" value="NL" />
<GoabDropdownItem label="Northwest Territories" value="NT" />
<GoabDropdownItem label="Nova Scotia" value="NS" />
<GoabDropdownItem label="Nunavut" value="NU" />
<GoabDropdownItem label="Ontario" value="ON" />
<GoabDropdownItem label="Prince Edward Island" value="PE" />
<GoabDropdownItem label="Quebec" value="QC" />
<GoabDropdownItem label="Saskatchewan" value="SK" />
<GoabDropdownItem label="Yukon" value="YT" />
</GoabDropdown>
</GoabFormItem>
<GoabFormItem label="Postal Code">
<GoabInput
name="postalCode"
type="text"
value={postalCode}
onChange={(e) => setPostalCode(e.value)}
width="7ch"
/>
</GoabFormItem>
</GoabBlock>
<GoabButtonGroup alignment="start" mt="2xl">
<GoabButton type="primary" onClick={() => {}}>
Save and continue
</GoabButton>
<GoabButton type="secondary" onClick={() => {}}>
Cancel
</GoabButton>
</GoabButtonGroup>Dynamically add an item to a dropdown list
type Task = {
value: string;
label: string;
mount: GoabDropdownItemMountType;
};
const DEFAULT_TASKS: Task[] = [
{ label: "Finish Report", value: "finish-report", mount: "append" },
{ label: "Attend Meeting", value: "attend-meeting", mount: "append" },
{ label: "Reply Emails", value: "reply-emails", mount: "append" },
];
const [tasks, setTasks] = useState<Task[]>(DEFAULT_TASKS);
const [newTask, setNewTask] = useState<string>("");
const [mountType, setMountType] = useState<string>("append");
const [selectedTask, setSelectedTask] = useState<string>("");
const [taskError, setTaskError] = useState<boolean>(false);
const [isReset, setIsReset] = useState<boolean>(false);
function onMountTypeChange(value: string | undefined) {
setMountType(value as string);
}
function addTask() {
if (newTask === "") {
setTaskError(true);
return;
}
setTaskError(false);
const task: Task = {
label: newTask,
value: newTask.toLowerCase().replace(" ", "-"),
mount: mountType as GoabDropdownItemMountType,
};
setTasks([...tasks, task]);
setNewTask("");
setIsReset(false);
}
function reset() {
setTasks(DEFAULT_TASKS);
setMountType("append");
setNewTask("");
setSelectedTask("");
setTaskError(false);
setIsReset(true);
}<GoabFormItem
requirement="required"
label="Name of item"
error={taskError ? "Please enter item name" : undefined}
helpText="Add an item to the dropdown list below">
<GoabInput
onChange={(event: GoabInputOnChangeDetail) => setNewTask(event.value)}
name="item"
value={newTask}
/>
</GoabFormItem>
<GoabFormItem mt="l" label="Add to">
<GoabRadioGroup
name="mountType"
onChange={(event: GoabRadioGroupOnChangeDetail) => onMountTypeChange(event.value)}
value={mountType}
orientation="horizontal">
<GoabRadioItem value="prepend" label="Start" />
<GoabRadioItem value="append" label="End" />
</GoabRadioGroup>
</GoabFormItem>
<GoabButtonGroup alignment="start" gap="relaxed" mt="l">
<GoabButton type="primary" onClick={addTask}>
Add new item
</GoabButton>
<GoabButton type="tertiary" onClick={reset}>
Reset list
</GoabButton>
</GoabButtonGroup>
<GoabDivider mt="l" />
<GoabFormItem mt="l" label="All items">
<div style={{ width: isReset ? "320px" : "auto" }}>
<GoabDropdown
key={tasks.length}
onChange={(event: GoabDropdownOnChangeDetail) =>
setSelectedTask(event.value as string)
}
value={selectedTask}
name="selectedTask">
{tasks.map(task => (
<GoabDropdownItem
key={task.value}
value={task.value}
mountType={task.mount}
label={task.label}
/>
))}
</GoabDropdown>
</div>
</GoabFormItem>Dynamically change items in a dropdown list
const [children, setChildren] = useState<string[]>([]);
const parents = ["All", "Big", "Small"];
const childrenAll = ["Bus", "Elephant", "Key", "Pen", "Watch", "Truck"];
const childrenBig = ["Elephant", "Truck", "Bus"];
const childrenSmall = ["Key", "Pen", "Watch"];
const loadItems = (value: string) => {
if (value === "All") setChildren(childrenAll);
else if (value === "Big") setChildren(childrenBig);
else setChildren(childrenSmall);
};
const logSelection = () => {
console.log("Item selected");
};<GoabFormItem
label="Size"
requirement="optional"
helpText="Choose the size to change the list below">
<GoabDropdown
name="parent"
placeholder="Select a value"
onChange={(event: GoabDropdownOnChangeDetail) =>
loadItems(event.value as string)
}>
{parents.map(parent => (
<GoabDropdownItem key={parent} value={parent} label={parent} />
))}
</GoabDropdown>
</GoabFormItem>
<GoabFormItem label="Items" requirement="optional" mt="xl">
<GoabDropdown name="children" placeholder="Select a value" onChange={logSelection}>
{children.map((child) => (
<GoabDropdownItem
key={crypto.randomUUID()}
value={child}
label={child}
mountType="reset"
/>
))}
</GoabDropdown>
</GoabFormItem>Group related questions together on a question page
const [addressLine1, setAddressLine1] = useState("");
const [addressLine2, setAddressLine2] = useState("");
const [townCity, setTownCity] = useState("");
const [province, setProvince] = useState("");
const [postalCode, setPostalCode] = useState("");<GoabLink leadingIcon="arrow-back" size="small" mb="none">
Back
</GoabLink>
<GoabText as="h2" mt="xl" mb="m">Your address</GoabText>
<GoabText mt="none" mb="xl">This is the home address of the person applying</GoabText>
<GoabFormItem label="Address line 1">
<GoabInput
onChange={(event) => setAddressLine1(event.value)}
value={addressLine1}
name="address-line-1"
ariaLabel="Address line 1"
width="100%"
/>
</GoabFormItem>
<GoabFormItem label="Address line 2" mt="l">
<GoabInput
onChange={(event) => setAddressLine2(event.value)}
value={addressLine2}
name="address-line-2"
ariaLabel="Address line 2"
width="100%"
/>
</GoabFormItem>
<GoabFormItem label="Town or city" mt="l">
<GoabInput
onChange={(event) => setTownCity(event.value)}
value={townCity}
name="town-city"
ariaLabel="Town or city name"
width="460px"
/>
</GoabFormItem>
<GoabFormItem label="Province or territory" mt="l" id="provinceLabel">
<GoabDropdown
onChange={(event) => setProvince(event.value ?? "")}
value={province}
name="province-territory"
ariaLabelledBy="provinceLabel"
>
<GoabDropdownItem value="AB" label="Alberta" />
<GoabDropdownItem value="BC" label="British Columbia" />
<GoabDropdownItem value="MB" label="Manitoba" />
<GoabDropdownItem value="NB" label="New Brunswick" />
<GoabDropdownItem value="NL" label="Newfoundland and Labrador" />
<GoabDropdownItem value="NS" label="Nova Scotia" />
<GoabDropdownItem value="ON" label="Ontario" />
<GoabDropdownItem value="PE" label="Prince Edward Island" />
<GoabDropdownItem value="QC" label="Quebec" />
<GoabDropdownItem value="SK" label="Saskatchewan" />
<GoabDropdownItem value="NT" label="Northwest Territories" />
<GoabDropdownItem value="NU" label="Nunavut" />
<GoabDropdownItem value="YT" label="Yukon" />
</GoabDropdown>
</GoabFormItem>
<GoabFormItem label="Postal code" mt="l">
<GoabInput
onChange={(event) => setPostalCode(event.value)}
value={postalCode}
name="postal-code"
width="105px"
/>
</GoabFormItem>
<GoabButton type="submit" mt="2xl">
Save and continue
</GoabButton>Review and action
<GoabGrid minChildWidth="315px">
<GoabContainer accent="thin" type="non-interactive">
<GoabText size="heading-m" mt="none" mb="m">Appearance details</GoabText>
<GoabGrid minChildWidth="200px" gap="m">
<GoabBlock direction="column" gap="xs">
<GoabText size="body-s" color="secondary" mt="none" mb="none">Accused name</GoabText>
<GoabText size="body-m" mt="none" mb="none">Doe, John Scott</GoabText>
</GoabBlock>
<GoabBlock direction="column" gap="xs">
<GoabText size="body-s" color="secondary" mt="none" mb="none">Date of birth</GoabText>
<GoabText size="body-m" mt="none" mb="none">Mar 14, 2021</GoabText>
</GoabBlock>
<GoabBlock direction="column" gap="xs">
<GoabText size="body-s" color="secondary" mt="none" mb="none">Court location</GoabText>
<GoabText size="body-m" mt="none" mb="none">Calgary</GoabText>
</GoabBlock>
<GoabBlock direction="column" gap="xs">
<GoabText size="body-s" color="secondary" mt="none" mb="none">Upcoming appearance date(s)</GoabText>
<GoabText size="body-m" mt="none" mb="none">Sep 20, 2021</GoabText>
</GoabBlock>
</GoabGrid>
<GoabText size="heading-xs" mt="l" mb="s">Docket number(s) & charges</GoabText>
<GoabContainer type="non-interactive" padding="compact">
<GoabText size="heading-xs" mt="none" mb="xs">1Show number of results per page
interface User {
id: string;
firstName: string;
lastName: string;
age: number;
}
const [users, setUsers] = useState<User[]>([]);
const [pageUsers, setPageUsers] = useState<User[]>([]);
const [page, setPage] = useState<number>(1);
const [perPage, setPerPage] = useState<number>(10);
useEffect(() => {
// Generate sample data
const firstNames = ["Emma", "Liam", "Olivia", "Noah", "Ava", "James", "Sophia", "William", "Isabella", "Oliver", "Mia", "Benjamin", "Charlotte", "Elijah", "Amelia", "Lucas", "Harper", "Mason", "Evelyn", "Logan"];
const lastNames = ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez", "Wilson", "Anderson", "Taylor", "Thomas", "Moore", "Jackson", "Martin", "Lee", "Thompson", "White"];
const _users: User[] = [];
for (let i = 1; i <= 100; i++) {
_users.push({
id: `user-${i}`,
firstName: firstNames[(i - 1) % firstNames.length],
lastName: lastNames[(i - 1) % lastNames.length],
age: 20 + (i % 40),
});
}
setUsers(_users);
setPageUsers(_users.slice(0, perPage));
}, [perPage]);
function changePage(newPage: number) {
const offset = (newPage - 1) * perPage;
const _users = users.slice(offset, offset + perPage);
setPage(newPage);
setPageUsers(_users);
}
function handlePerPageCountChangeEvent(event: GoabDropdownOnChangeDetail) {
const perPageValue = parseInt(event.value || "10");
setPage(1);
setPerPage(perPageValue);
const _users = users.slice(0, perPageValue);
setPageUsers(_users);
}<GoabTable width="100%" mb="xl">
<thead>
<tr>
<th>First name</th>
<th>Last name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
{pageUsers.map((u) => (
<tr key={u.id}>
<td>{u.firstName}</td>
<td>{u.lastName}</td>
<td>{u.age}</td>
</tr>
))}
</tbody>
</GoabTable>
<GoabBlock alignment="center" width="100%">
<GoabBlock mb="m" alignment="center">
Show
<GoabDropdown
onChange={handlePerPageCountChangeEvent}
value={perPage.toString()}
width="9ch"
>
<GoabDropdownItem value="10" label="10" />
<GoabDropdownItem value="20" label="20" />
<GoabDropdownItem value="30" label="30" />
</GoabDropdown>
<span style={{ width: "75px" }}>per page</span>
</GoabBlock>
<GoabSpacer hSpacing="fill" />
<GoabPagination
itemCount={users.length}
perPageCount={perPage}
pageNumber={page}
onChange={(event) => changePage(event.page)}
/>
</GoabBlock>States
When you must disable a button or input:
- Provide nearby text explaining what needs to happen first
- Consider showing the element enabled with validation on submit instead
- Use aria-describedby to link the disabled element to explanatory text
Don't
Don't disable buttons or inputs without explaining why. Disabled controls can be confusing and users may not understand why they can't interact with an element.
Sizing
Do
Define dropdown widths based on the widest dropdown in the form.
Do
Use the default width for dropdowns. The dropdown automatically sets its width based on the length of the options.
Content
Don't
Don't truncate labels. Longer labels should wrap to the next line.
Placeholder text disappears when users start typing, leaving them without context for what the field is asking for.
Always use a visible label above or beside the input field. Placeholder text can provide an example of the expected format, but should never be the only indication of what information is needed.
Don't
Don't use placeholder text as a label
Positioning
Don't
Don't allow the dropdown menu to hide below the viewport.
Other
The form item automatically associates the label with the input for screen readers, ensuring your form is accessible.
Do
Use a form item wrapper on all inputs to add a label, helper text, error message, and more.