Form item

Wraps an input control with a text label, requirement label, helper text, and error text.

Props

testId
string
Sets a data-testid attribute for automated testing.
label
string
Creates a label for the form item.
labelSize
compact | regular | large
Sets the label size. 'compact' for dense layouts, 'regular' for standard, 'large' for emphasis.
Defaults to regular.
helpText
string
Help text displayed under the form field to provide additional explanation.
error
string
Error text displayed under the form field. Leave blank to indicate a valid field.
requirement
optional | required
Marks the field with an optional or required label indicator.
maxWidth
string
Sets the maximum width of the form item.
Defaults to none.
version
1 | 2
The design system version for styling purposes.
Defaults to 1.
type
text-input | textarea | checkbox-list | radio-group
Specifies the input type for appropriate message spacing. Used with checkbox-list or radio-group.
name
string
Overrides the label value within the form-summary. For public-form use only.
Defaults to blank.
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.

Slots

error
Named slot for content
helptext
Named slot for content
Examples

Ask a long answer question with a maximum word count

ReactAngularWeb Components
const [value, setValue] = useState("");
<GoabFormItem
      label="Provide more detail"
      helpText="Maximum 500 words. Do not include personal or financial information."
    >
      <GoabTextarea
        name="program"
        onChange={(e) => setValue(e.value)}
        value={value}
        width="100%"
        rows={6}
        maxCount={500}
        countBy="word"
      />
    </GoabFormItem>

Add another item in a modal

ReactAngularWeb Components
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

ReactAngularWeb Components
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

ReactAngularWeb Components
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>

Ask a user for a birthday

ReactAngularWeb Components
const [birthdate, setBirthdate] = useState<Date | undefined>(undefined);
<GoabFormItem label="What is your date of birth?">
      <GoabDatePicker
        name="birthdate"
        type="input"
        value={birthdate}
        onChange={(e) => setBirthdate(e.value)}
      />
    </GoabFormItem>

The date picker component handles accessibility automatically. Ensure the form-item label clearly describes what date is being requested.

Ask a user for an address

ReactAngularWeb Components
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>

Ask a user for direct deposit information

ReactAngularWeb Components
const [bankNumber, setBankNumber] = useState("");
  const [transitNumber, setTransitNumber] = useState("");
  const [accountNumber, setAccountNumber] = useState("");
<GoabText as="h1" size="heading-l" mt="none" mb="m">Direct deposit information</GoabText>
      <GoabText as="p" mb="xl">
        Find this information on your bank's website or on your personal cheques.
        Contact your bank if you can't find this information.
      </GoabText>
      <form>
        <GoabFormItem
          label="Bank or Institution number"
          helpText="3-4 digits in length"
        >
          <GoabInput
            maxLength={4}
            name="bankNumber"
            onChange={(e) => setBankNumber(e.value)}
            value={bankNumber}
            ariaLabel="bankNumber"
            width="88px"
          />
        </GoabFormItem>
        <GoabFormItem
          label="Branch or Transit number"
          helpText="5 digits in length"
          mt="l"
        >
          <GoabInput
            maxLength={5}
            name="transitNumber"
            onChange={(e) => setTransitNumber(e.value)}
            value={transitNumber}
            ariaLabel="transitNumber"
            width="143px"
          />
        </GoabFormItem>
        <GoabFormItem
          label="Account number"
          helpText="3-12 digits in length"
          mt="l"
        >
          <GoabInput
            maxLength={12}
            name="accountNumber"
            value={accountNumber}
            onChange={(e) => setAccountNumber(e.value)}
            ariaLabel="accountNumber"
          />
        </GoabFormItem>
      </form>
      <GoabDetails heading="Where can I find this information on a personal cheque?" mt="l">
        <GoabText as="p" mb="m">
          Below is an example of where you can find the required bank information
          on a personal cheque.
        </GoabText>
        <img src="https://design.alberta.ca/images/details-demo.jpg" alt="Cheque example showing bank information locations" />
      </GoabDetails>

      <GoabButton type="submit" mt="2xl">
        Save and continue
      </GoabButton>

Ask a user for an Indian registration number

ReactAngularWeb Components
const [bandNo, setBandNo] = useState("");
  const [family, setFamily] = useState("");
  const [position, setPosition] = useState("");
<GoabFormItem label="Indian registration number" labelSize="large">
      <GoabBlock gap="m" direction="row">
        <GoabFormItem label="Band #" helpText="3 digits">
          <GoabInput
            onChange={(e) => setBandNo(e.value)}
            value={bandNo}
            name="bandNo"
            width="88px"
            maxLength={3}
          />
        </GoabFormItem>
        <GoabFormItem label="Family" helpText="Up to 5 digits">
          <GoabInput
            onChange={(e) => setFamily(e.value)}
            value={family}
            name="family"
            width="105px"
            maxLength={5}
          />
        </GoabFormItem>
        <GoabFormItem label="Position" helpText="2 digits">
          <GoabInput
            onChange={(e) => setPosition(e.value)}
            value={position}
            name="position"
            width="71px"
            maxLength={2}
          />
        </GoabFormItem>
      </GoabBlock>
    </GoabFormItem>

Ask a user for dollar amounts

ReactAngularWeb Components
const [tuitionAmount, setTuitionAmount] = useState("");
  const [suppliesAmount, setSuppliesAmount] = useState("");
  const [othersAmount, setOthersAmount] = useState("");
<GoabFormItem label="Tuition">
        <GoabInput
          onChange={(e) => setTuitionAmount(e.value)}
          value={tuitionAmount}
          name="tuitionAmount"
          leadingContent="$"
        />
      </GoabFormItem>
      <GoabFormItem label="Books/Supplies/Instruments" mt="l">
        <GoabInput
          onChange={(e) => setSuppliesAmount(e.value)}
          value={suppliesAmount}
          name="suppliesAmount"
          leadingContent="$"
        />
      </GoabFormItem>
      <GoabFormItem label="Other costs" mt="l">
        <GoabInput
          onChange={(e) => setOthersAmount(e.value)}
          value={othersAmount}
          name="othersAmount"
          leadingContent="$"
        />
      </GoabFormItem>

Ask a user one question at a time

ReactAngularWeb Components
<GoabLink leadingIcon="arrow-back" size="small">
        <a href="#">Back</a>
      </GoabLink>
      <GoabFormItem
        mt="xl"
        label="Are you currently in school?"
        labelSize="large"
        helpText="School includes foundational, skills and employment training, micro-credentials, post-secondary and continuing education.">
        <GoabRadioGroup name="school" ariaLabel="are you currently in school?" onChange={() => {}}>
          <GoabRadioItem value="yes" label="Yes" />
          <GoabRadioItem value="no" label="No" />
        </GoabRadioGroup>
      </GoabFormItem>
      <GoabButton type="submit" mt="2xl">
        Save and continue
      </GoabButton>

Confirm a change

ReactAngularWeb Components
const [open, setOpen] = useState(false);
  const [effectiveDate, setEffectiveDate] = useState<Date | undefined>(new Date());

  const onChangeEffectiveDate = (detail: GoabDatePickerOnChangeDetail) => {
    setEffectiveDate(detail.value as Date);
  };
<GoabButton onClick={() => setOpen(true)}>Save and continue</GoabButton>

      <GoabModal
        heading="Address has changed"
        open={open}
        onClose={() => setOpen(false)}
        actions={
          <GoabButtonGroup alignment="end">
            <GoabButton type="secondary" size="compact" onClick={() => setOpen(false)}>
              Undo address change
            </GoabButton>
            <GoabButton type="primary" size="compact" onClick={() => setOpen(false)}>
              Confirm
            </GoabButton>
          </GoabButtonGroup>
        }>
        <GoabContainer type="non-interactive" accent="filled" padding="compact" width="full">
          <GoabText as="h4" mt="none" mb="s">Before</GoabText>
          <GoabText mt="none">123456 78 Ave NW, Edmonton, Alberta</GoabText>
          <GoabText as="h4" mt="none" mb="s">After</GoabText>
          <GoabText mt="none" mb="none">881 12 Ave NW, Edmonton, Alberta</GoabText>
        </GoabContainer>
        <GoabFormItem label="Effective date" mt="l">
          <GoabDatePicker
            onChange={onChangeEffectiveDate}
            name="effectiveDate"
            value={effectiveDate}
          />
        </GoabFormItem>
      </GoabModal>

Disabled button with a required field

ReactAngularWeb Components
const [inputValue, setInputValue] = useState("");

  const handleInputChange = (detail: GoabInputOnChangeDetail) => {
    setInputValue(detail.value);
  };

  const handleConfirm = () => {
    // Handle form submission
    console.log("Form submitted with:", inputValue);
  };

  const handleCancel = () => {
    // Handle cancellation
    setInputValue("");
  };
<form>
      <GoabFormItem label="Name" requirement="required">
        <GoabInput
          name="input"
          type="text"
          onChange={handleInputChange}
          value={inputValue}
          width="100%"
        />
      </GoabFormItem>

      <GoabButtonGroup alignment="start" mt="xl">
        <GoabButton disabled={inputValue.trim() === ""} onClick={handleConfirm}>
          Confirm
        </GoabButton>
        <GoabButton type="secondary" onClick={handleCancel}>
          Cancel
        </GoabButton>
      </GoabButtonGroup>
    </form>

Dynamically add an item to a dropdown list

ReactAngularWeb Components
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

ReactAngularWeb Components
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>

Filter data in a table

ReactAngularWeb Components
const [typedChips, setTypedChips] = useState<string[]>([]);
  const [inputValue, setInputValue] = useState("");
  const [inputError, setInputError] = useState("");
  const errorEmpty = "Empty filter";
  const errorDuplicate = "Enter a unique filter";

  const data = useMemo(
    () => [
      {
        status: { type: "information" as GoabBadgeType, text: "In progress" },
        name: "Ivan Schmidt",
        id: "7838576954",
      },
      {
        status: { type: "success" as GoabBadgeType, text: "Completed" },
        name: "Luz Lakin",
        id: "8576953364",
      },
      {
        status: { type: "information" as GoabBadgeType, text: "In progress" },
        name: "Keith McGlynn",
        id: "9846041345",
      },
      {
        status: { type: "success" as GoabBadgeType, text: "Completed" },
        name: "Melody Frami",
        id: "7385256175",
      },
      {
        status: { type: "important" as GoabBadgeType, text: "Updated" },
        name: "Frederick Skiles",
        id: "5807570418",
      },
      {
        status: { type: "success" as GoabBadgeType, text: "Completed" },
        name: "Dana Pfannerstill",
        id: "5736306857",
      },
    ],
    []
  );

  const [dataFiltered, setDataFiltered] = useState(data);

  const handleInputChange = (detail: GoabInputOnChangeDetail) => {
    const newValue = detail.value.trim();
    setInputValue(newValue);
  };

  const handleInputKeyPress = (detail: GoabInputOnKeyPressDetail) => {
    if (detail.key === "Enter") {
      applyFilter();
    }
  };

  const applyFilter = () => {
    if (inputValue === "") {
      setInputError(errorEmpty);
      return;
    }
    if (typedChips.length > 0 && typedChips.includes(inputValue)) {
      setInputError(errorDuplicate);
      return;
    }
    setTypedChips([...typedChips, inputValue]);
    setTimeout(() => {
      setInputValue("");
    }, 0);
    setInputError("");
  };

  const removeTypedChip = (chip: string) => {
    setTypedChips(typedChips.filter(c => c !== chip));
    setInputError("");
  };

  const checkNested = useCallback((obj: object, chip: string): boolean => {
    return Object.values(obj).some(value =>
      typeof value === "object" && value !== null
        ? checkNested(value, chip)
        : typeof value === "string" && value.toLowerCase().includes(chip.toLowerCase())
    );
  }, []);

  const getFilteredData = useCallback(
    (typedChips: string[]) => {
      if (typedChips.length === 0) {
        return data;
      }
      return data.filter((item: object) =>
        typedChips.every(chip => checkNested(item, chip))
      );
    },
    [checkNested, data]
  );

  useEffect(() => {
    setDataFiltered(getFilteredData(typedChips));
  }, [getFilteredData, typedChips]);
<GoabFormItem id="filterChipInput" error={inputError} mb="m">
        <GoabBlock gap="xs" direction="row" alignment="start" width="100%">
          <div style={{ flex: 1 }}>
            <GoabInput
              name="filterChipInput"
              aria-labelledby="filterChipInput"
              value={inputValue}
              leadingIcon="search"
              width="100%"
              onChange={handleInputChange}
              onKeyPress={handleInputKeyPress}
            />
          </div>
          <GoabButton type="secondary" onClick={applyFilter} leadingIcon="filter">
            Filter
          </GoabButton>
        </GoabBlock>
      </GoabFormItem>

      {typedChips.length > 0 && (
        <div>
          <GoabText tag="span" color="secondary" mb="xs" mr="xs">
            Filter:
          </GoabText>
          {typedChips.map((typedChip, index) => (
            <GoabFilterChip
              key={index}
              content={typedChip}
              mb="xs"
              mr="xs"
              onClick={() => removeTypedChip(typedChip)}
            />
          ))}
          <GoabButton type="tertiary" size="compact" mb="xs" onClick={() => setTypedChips([])}>
            Clear all
          </GoabButton>
        </div>
      )}

      <GoabTable width="full">
        <thead>
          <tr>
            <th>Status</th>
            <th>Name</th>
            <th className="goa-table-number-header">ID Number</th>
          </tr>
        </thead>
        <tbody>
          {dataFiltered.map(item => (
            <tr key={item.id}>
              <td>
                <GoabBadge type={item.status.type} content={item.status.text} icon={false} />
              </td>
              <td>{item.name}</td>
              <td className="goa-table-number-column">{item.id}</td>
            </tr>
          ))}
        </tbody>
      </GoabTable>

      {dataFiltered.length === 0 && data.length > 0 && (
        <GoabBlock mt="l" mb="l">
          No results found
        </GoabBlock>
      )}

Give background information before asking a question

ReactAngularWeb Components
const [selectedValue, setSelectedValue] = useState<string>("");

  const handleChange = (event: GoabRadioGroupOnChangeDetail) => {
    setSelectedValue(event.value as string);
  };

  const handleSubmit = () => {
    console.log("Selected:", selectedValue);
  };
<GoabLink leadingIcon="arrow-back" size="small" mb="none">
        Back
      </GoabLink>

      <GoabText as="h2" mt="xl" mb="m">Current school status</GoabText>
      <GoabText mt="none" mb="s">
        School can encompass foundational programs that help individuals gain basic skills for
        further learning and living, such as literacy and numeracy courses. It also includes
        skills and employment training programs, designed to equip you with specific skills for
        the job market.
      </GoabText>
      <GoabText mt="none" mb="s">
        Post-secondary education, such as Bachelor's, Master's, or Doctoral degrees, and
        continuing education courses for professional or personal development, are also
        categorized under 'school'.
      </GoabText>
      <GoabText mt="none" mb="xl">
        Contact your provider if you're concerned about your school status.
      </GoabText>

      <GoabFormItem label="Are you currently in school?">
        <GoabRadioGroup
          name="school"
          ariaLabel="are you currently in school?"
          onChange={handleChange}>
          <GoabRadioItem value="yes" label="Yes" />
          <GoabRadioItem value="no" label="No" />
        </GoabRadioGroup>
      </GoabFormItem>

      <GoabButton type="submit" mt="2xl" onClick={handleSubmit}>
        Save and continue
      </GoabButton>
  );
}

Give context before asking a long answer question

ReactAngularWeb Components
const [textValue, setTextValue] = useState("");

  const handleChange = (event: GoabTextareaOnChangeDetail) => {
    setTextValue(event.value);
  };

  const handleContinue = () => {
    console.log("Submitted:", textValue);
  };
<GoabLink leadingIcon="arrow-back" size="small" mb="none">
        Back
      </GoabLink>

      <GoabText as="h2" mt="xl" mb="m">Submit a question about your benefits</GoabText>
      <GoabText mt="none" mb="xl">
        If you need clarification about your benefit eligibility, payment schedule, or application status, submit your
        question here.
      </GoabText>

      <form>
        <GoabFormItem
          label="Provide details about your situation"
          helpText="Include specific details to help us answer your question quickly.">
          <GoabTextarea
            name="program"
            onChange={handleChange}
            value={textValue}
            maxCount={400}
            countBy="character"
          />
        </GoabFormItem>
      </form>

      <GoabDetails mt="m" heading="What kind of information is useful?">
        <p>
          Include your benefit program name, mention any recent correspondence you received and/or provide any
          relevant case or reference numbers.
        </p>
      </GoabDetails>

      <GoabButtonGroup alignment="start" mt="2xl">
        <GoabButton type="primary" onClick={handleContinue}>
          Continue
        </GoabButton>
      </GoabButtonGroup>
ReactAngularWeb Components
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>
ReactAngularWeb Components
<GoabFormItem label="How would you like to be contacted?">
      <GoabCheckboxList name="contact-options">
        <GoabCheckbox
          name="email"
          text="Email"
          value="email"
          description={
            <span>
              Help text with a <a href="#">link</a>.
            </span>
          }
        />
        <GoabCheckbox name="phone" text="Phone" value="phone" />
        <GoabCheckbox name="text" text="Text message" value="text" />
      </GoabCheckboxList>
    </GoabFormItem>

Include descriptions for items in a checkbox list

ReactAngularWeb Components
const [selected, setSelected] = useState("1");
<GoabFormItem label="How do you want to sign in?">
      <GoabRadioGroup
        name="selectOne"
        value={selected}
        onChange={(event) => setSelected(event.value)}
      >
        <GoabRadioItem
          value="1"
          label="Sign in as a business"
          description="Use the account associated with the business"
        />
        <GoabRadioItem
          value="2"
          label="Sign in as an individual"
          description="If you don't have a Alberta.ca login, you can create one"
        />
      </GoabRadioGroup>
    </GoabFormItem>

Question page

ReactAngularWeb Components
const [answer, setAnswer] = useState("");

  const handleContinue = () => {
    console.log("Answer submitted:", answer);
  };
<GoabLink leadingIcon="arrow-back" size="small" mb="none">
        Back
      </GoabLink>

      <GoabText as="h1" mt="xl" mb="m">What is your email address?</GoabText>
      <GoabText mt="none" mb="xl">We'll use this to send you confirmation of your application.</GoabText>

      <GoabFormItem label="Email address">
        <GoabInput
          name="email"
          type="email"
          value={answer}
          onChange={(e) => setAnswer(e.value)}
          width="100%"
        />
      </GoabFormItem>

      <GoabButtonGroup alignment="start" mt="2xl">
        <GoabButton type="primary" onClick={handleContinue}>
          Continue
        </GoabButton>
      </GoabButtonGroup>
  );
}

Reset date picker field

ReactAngularWeb Components
const [date, setDate] = useState<Date | undefined>();

  const setNewDate = (value: Date | undefined) => {
    setDate(value);
  };

  function setValue() {
    const d = new Date();
    d.setDate(d.getDate() - 7);
    setDate(d);
  }

  function clearValue() {
    setDate(undefined);
  }
<GoabFormItem label="Date Picker">
        <GoabDatePicker
          name="item"
          value={date}
          onChange={(e) => setNewDate(e.value as Date)}
          mb="xl"
        />
      </GoabFormItem>

      <GoabButtonGroup mt="xs" alignment="start">
        <GoabButton onClick={setValue}>
          Set Value
        </GoabButton>
        <GoabButton onClick={clearValue}>Clear Value</GoabButton>
      </GoabButtonGroup>

Reveal input based on a selection

ReactAngularWeb Components
const [contactMethod, setContactMethod] = useState("");
  const [checkboxSelection, setCheckboxSelection] = useState<string[]>([]);
<GoabFormItem
        label="How would you like to be contacted?"
        helpText="Select one option"
      >
        <GoabRadioGroup
          name="contactMethod"
          value={contactMethod}
          onChange={(e) => setContactMethod(e.value)}
        >
          <GoabRadioItem
            value="email"
            label="Email"
            reveal={
              <GoabFormItem label="Email address">
                <GoabInput name="email" onChange={() => {}} value="" />
              </GoabFormItem>
            }
          />
          <GoabRadioItem
            value="phone"
            label="Phone"
            reveal={
              <GoabFormItem label="Phone number">
                <GoabInput name="phoneNumber" onChange={() => {}} value="" />
              </GoabFormItem>
            }
          />
          <GoabRadioItem
            value="text"
            label="Text message"
            reveal={
              <GoabFormItem label="Mobile phone number">
                <GoabInput name="mobilePhoneNumber" onChange={() => {}} value="" />
              </GoabFormItem>
            }
          />
        </GoabRadioGroup>
      </GoabFormItem>

      <GoabFormItem label="How would you like to be contacted?" mt="xl">
        <GoabCheckboxList
          name="contactMethods"
          value={checkboxSelection}
          onChange={(e) => setCheckboxSelection(e.values || [])}
        >
          <GoabCheckbox
            name="email"
            text="Email"
            value="email"
            reveal={
              <GoabFormItem label="Email address">
                <GoabInput name="email" onChange={() => {}} value="" />
              </GoabFormItem>
            }
          />
          <GoabCheckbox
            name="phone"
            text="Phone"
            value="phone"
            reveal={
              <GoabFormItem label="Phone number">
                <GoabInput name="phoneNumber" onChange={() => {}} value="" />
              </GoabFormItem>
            }
          />
          <GoabCheckbox
            name="text"
            text="Text message"
            value="text"
            reveal={
              <GoabFormItem label="Mobile phone number">
                <GoabInput name="mobilePhoneNumber" onChange={() => {}} value="" />
              </GoabFormItem>
            }
          />
        </GoabCheckboxList>
      </GoabFormItem>

Review and action

ReactAngularWeb Components
<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) &amp; charges</GoabText>
        <GoabContainer type="non-interactive" padding="compact">
          <GoabText size="heading-xs" mt="none" mb="xs">1
ReactAngularWeb Components
const [search, setSearch] = useState("");

  const onClick = () => {
    console.log("search:", search);
  };
<form>
      <GoabFormItem>
        <GoabBlock gap="xs" direction="row">
          <GoabInput
            type="search"
            name="search"
            value={search}
            onChange={(e) => setSearch(e.value)}
            leadingIcon="search"
          />
          <GoabButton type="primary" onClick={onClick}>
            Search
          </GoabButton>
        </GoabBlock>
      </GoabFormItem>
    </form>

Select one or more from a list of options

ReactAngularWeb Components
const [selectedOptions, setSelectedOptions] = useState<string[]>([]);
<GoabFormItem
      label="How would you like to be contacted?"
      helpText="Choose all that apply"
    >
      <GoabCheckboxList
        name="contactPreferences"
        value={selectedOptions}
        onChange={(e) => setSelectedOptions(e.detail.value)}
      >
        <GoabCheckbox name="email" text="Email" value="email" />
        <GoabCheckbox name="phone" text="Phone" value="phone" />
        <GoabCheckbox name="text" text="Text message" value="text" />
      </GoabCheckboxList>
    </GoabFormItem>

Set a max width on a long radio item

ReactAngularWeb Components
const [selectOne, setSelectOne] = useState<string>("1");
<form>
      <GoabFormItem label="Select one option">
        <GoabRadioGroup
          name="selectOne"
          value={selectOne}
          onChange={(e: GoabRadioGroupOnChangeDetail) => setSelectOne(e.value)}>
          <GoabRadioItem
            value="1"
            label="Option one which has a very long label with lots of text"
            maxWidth="300px"
          />
          <GoabRadioItem value="2" label="Option two" />
          <GoabRadioItem value="3" label="Option three" />
        </GoabRadioGroup>
      </GoabFormItem>
    </form>

Show a list to help answer a question

ReactAngularWeb Components
const handleChange = (event: GoabRadioGroupOnChangeDetail) => {
    console.log("value is", event.value);
  };
<form>
      <GoabFormItem
        label="Do you have additional education expenses?"
        helpText="You can request funding for these now or at any time during your program."
        mb="m">
        <GoabRadioGroup name="additional" onChange={handleChange}>
          <GoabRadioItem label="Yes" value="yes" name="additional" />
          <GoabRadioItem label="No" value="no" name="additional" />
        </GoabRadioGroup>
      </GoabFormItem>

      <GoabDetails heading="What are additional education expenses?">
        <GoabBlock gap="m" mt="m">
          <div>
            <strong>Examples of education expenses</strong>
            <ul className="goa-unordered-list">
              <li>Laptop and computer hardware</li>
              <li>Computer apps and subscriptions</li>
              <li>Home internet</li>
              <li>Testing and exam fees</li>
              <li>Work or school clothing, like work boots</li>
            </ul>
          </div>
          <div>
            <strong>Do not include</strong>
            <ul className="goa-unordered-list">
              <li>Tuition</li>
              <li>Mandatory fees</li>
              <li>Books and supplies</li>
              <li>School association fees</li>
            </ul>
          </div>
        </GoabBlock>
      </GoabDetails>
    </form>

Show a section title on a question page

ReactAngularWeb Components
<GoabLink leadingIcon="arrow-back" size="small" mb="none">
        Back
      </GoabLink>

      <GoabText as="h3" size="body-m" mt="xl" mb="m" color="secondary">Personal information</GoabText>

      <GoabFormItem label="Do you currently live in Canada?" labelSize="large">
        <GoabRadioGroup
          name="canada"
          ariaLabel="Do you currently live in Canada?"
          onChange={() => {}}>
          <GoabRadioItem value="yes" label="Yes" />
          <GoabRadioItem value="no" label="No" />
        </GoabRadioGroup>
      </GoabFormItem>

      <GoabButton type="submit" mt="2xl">
        Save and continue
      </GoabButton>

Show a simple progress indicator on a question page

ReactAngularWeb Components
<GoabLink leadingIcon="arrow-back" size="small" mb="none">
        Back
      </GoabLink>

      <GoabText as="h3" size="body-m" mt="xl" mb="m" color="secondary">Question 3 of 9</GoabText>

      <GoabFormItem label="Do you currently live in Canada?" labelSize="large">
        <GoabRadioGroup
          name="canada"
          ariaLabel="Do you currently live in Canada?"
          onChange={() => {}}>
          <GoabRadioItem value="yes" label="Yes" />
          <GoabRadioItem value="no" label="No" />
        </GoabRadioGroup>
      </GoabFormItem>

      <GoabButton type="submit" mt="2xl">
        Save and continue
      </GoabButton>

Show a simple progress indicator on a question page with multiple questions

ReactAngularWeb Components
<GoabLink leadingIcon="arrow-back" size="small" mb="none">
        Back
      </GoabLink>

      <GoabText as="h3" size="body-m" mt="xl" mb="none" color="secondary">Step 1 of 5</GoabText>
      <GoabText as="h2" mt="xs" mb="xl">Personal information</GoabText>

      <GoabFormItem label="What is your name?">
        <GoabInput onChange={() => {}} name="name" ariaLabel="what is your name?" width="50ch" />
      </GoabFormItem>

      <GoabFormItem label="What is your phone number?" mt="l">
        <GoabInput
          onChange={() => {}}
          name="phone-number"
          ariaLabel="what is your phone number?"
          leadingContent="+1"
        />
      </GoabFormItem>

      <GoabFormItem label="What is your home postal code?" mt="l">
        <GoabInput
          onChange={() => {}}
          name="postal-code"
          width="14ch"
          ariaLabel="what is your home postal code"
        />
      </GoabFormItem>

      <GoabButton type="submit" mt="2xl">
        Save and continue
      </GoabButton>

Show more information to help answer a question

ReactAngularWeb Components
<GoabLink leadingIcon="arrow-back" size="small" mb="none">
        Back
      </GoabLink>

      <GoabFormItem
        mt="xl"
        label="Do you pay for child care?"
        labelSize="large"
        helpText="Examples of child care are daycares, day homes and baby-sisters."
      >
        <GoabRadioGroup
          name="child-care"
          ariaLabel="Do you pay for child care?"
          onChange={() => {}}
        >
          <GoabRadioItem value="yes" label="Yes" />
          <GoabRadioItem value="no" label="No" />
        </GoabRadioGroup>
      </GoabFormItem>

      <GoabDetails heading="Why are we asking this question?" mt="l">
        <p>We ask this question to determine if you are eligible for child care benefits.</p>
      </GoabDetails>

      <GoabButton type="submit" mt="2xl">
        Save and continue
      </GoabButton>

Slotted error text in a form item

ReactAngularWeb Components
const [value, setValue] = useState("");

  const onChange = (detail: GoabInputOnChangeDetail) => {
    setValue(detail.value);
  };

  const errorMessage = (
    <>
      <i>This is </i> slotted <b>error text</b>.
    </>
  );
<GoabFormItem label="First name" error={errorMessage}>
      <GoabInput onChange={onChange} value={value} name="item" error={true} />
    </GoabFormItem>

Slotted helper text in a form item

ReactAngularWeb Components
const [value, setValue] = useState("");

  const onChange = (detail: GoabInputOnChangeDetail) => {
    setValue(detail.value);
  };

  const helpText = (
    <>
      <i>This is </i> slotted <b>help text</b>.
    </>
  );
<GoabFormItem label="First name" helpText={helpText}>
      <GoabInput onChange={onChange} value={value} name="item" />
    </GoabFormItem>

Type to create a new filter

ReactAngularWeb Components
const [typedChips, setTypedChips] = useState<string[]>([]);
  const [inputValue, setInputValue] = useState("");
<GoabFormItem label="Type to create a chip" mb="m">
        <GoabInput
          name="chipInput"
          value={inputValue}
          onChange={(e) => setInputValue(e.value.trim())}
          onKeyPress={(e) => {
            if (e.key === "Enter" && e.value.trim()) {
              setTypedChips([...typedChips, e.value.trim()]);
              setTimeout(() => setInputValue(""), 0);
            } else if (e.key === "Backspace" && !e.value.trim() && typedChips.length > 0) {
              setTypedChips(typedChips.slice(0, -1));
            }
          }}
          width="100%"
        />
      </GoabFormItem>
      <div>
        {typedChips.map((chip, index) => (
          <GoabFilterChip
            key={index}
            content={chip}
            mb="xs"
            mr="xs"
            onClick={() => setTypedChips(typedChips.filter((c) => c !== chip))}
          />
        ))}
      </div>

Other

The form item automatically associates the label with the input for screen readers, ensuring your form is accessible.

Use a form item wrapper on all inputs to add a label, helper text, error message, and more.
All GoA Design System components are built to meet WCAG 2.2 AA standards. The following guidelines provide additional context for accessible implementation.

No accessibility-specific guidelines have been documented for this component yet.