Input

A single-line field where users can input and edit text.

Props

type
text | number | password | email | date | datetime-local | month | range | search | tel | time | url | week
Sets the type of the input field.
Defaults to text.
name
string
Name of input value that is received in the onChange event.
value
string
Bound to value.
autoCapitalize
on | off | none | sentences | words | characters
Controls whether and how text input is automatically capitalized as it is entered/edited by the user. This only works on mobile devices.
Defaults to off.
autoComplete
string
Specifies the autocomplete attribute for the input field.
placeholder
string
Text displayed within the input when no value is set.
leadingIcon
GoAIconType
Icon shown to the left of the text.
trailingIcon
GoAIconType
Icon shown to the right of the text.
variant
goa | bare
Sets the visual style variant. 'goa' for standard GoA styling, 'bare' for minimal styling.
Defaults to goa.
disabled
boolean
Disables this input. The input will not receive focus or events. Use [attr.disabled] with [formControl].
Defaults to false.
handleTrailingIconClick
boolean
Flag that will result in an icon button component being rendered instead of an icon.
Defaults to false.
focused
boolean
Sets the cursor focus to the input.
Defaults to false.
readOnly
boolean
Makes the input readonly.
Defaults to false.
error
boolean
Sets the input to an error state.
Defaults to false.
testId
string
Sets a data-testid attribute for automated testing.
width
string
Sets the width of the text input area.
Defaults to 30ch.
ariaLabel
string
Defines how the input 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 input.
min
string
A string value that supports any number, or an ISO 8601 format if using the date or datetime type.
max
string
A string value that supports any number, or an ISO 8601 format if using the date or datetime type.
step
number
How much a number or date should change by.
Defaults to 1.
prefix
string
Use leadingContent slot instead.
suffix
string
Use trailingContent slot instead.
debounce
number
Debounce delay in milliseconds before firing the change event. 0 means no debounce.
Defaults to 0.
maxLength
number
Defines the maximum number of characters (as UTF-16 code units) the user can enter into the input.
id
string
Unique identifier for the input element. Used for label associations and accessibility.
trailingIconAriaLabel
string
Aria label for the trailing icon. Use only when the trailing icon is interactive.
textAlign
left | right
Sets the text alignment within the input field.
Defaults to left.
size
default | compact
Sets the size of the input. 'compact' reduces height for dense layouts.
Defaults to default.
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
onKeyPress
(event: Event) => void
_keyPress
CustomEvent
onFocus
(event: Event) => void
_focus
CustomEvent
onBlur
(event: Event) => void
_blur
CustomEvent
onTrailingIconClick
(event: Event) => void
_trailingIconClick
CustomEvent

Slots

leadingContent
Named slot for content
trailingContent
Named slot for content
Examples

Add a record using a drawer

const [open, setOpen] = useState(false);
<GoabxButton leadingIcon="add" onClick={() => setOpen(true)}>
        Add Record
      </GoabxButton>
      <GoabxDrawer
        maxSize="492px"
        open={open}
        heading="Add Record"
        position="right"
        onClose={() => setOpen(false)}
        actions={
          <GoabButtonGroup>
            <GoabxButton type="primary" size="compact" onClick={() => setOpen(false)}>
              Add record
            </GoabxButton>
            <GoabxButton type="tertiary" size="compact" onClick={() => setOpen(false)}>
              Cancel
            </GoabxButton>
          </GoabButtonGroup>
        }
      >
        <GoabxFormItem label="Level of education">
          <GoabxDropdown onChange={() => {}} name="education" value="university">
            <GoabxDropdownItem value="high-school" label="High School Diploma" />
            <GoabxDropdownItem value="college" label="College Diploma" />
            <GoabxDropdownItem value="university" label="University Degree" />
            <GoabxDropdownItem value="masters" label="Master's Degree" />
            <GoabxDropdownItem value="doctorate" label="Doctorate" />
          </GoabxDropdown>
        </GoabxFormItem>
        <GoabxFormItem label="Educational institution" mt="l">
          <GoabxInput name="education" type="text" onChange={() => {}} />
        </GoabxFormItem>
        <GoabxFormItem label="Field of study" requirement="optional" mt="l">
          <GoabxInput name="fieldOfStudy" type="text" onChange={() => {}} />
        </GoabxFormItem>
        <GoabxFormItem label="Is the person currently attending?" mt="l">
          <GoabxRadioGroup name="attendTraining" orientation="horizontal" onChange={() => {}}>
            <GoabxRadioItem value="yes" label="Yes" />
            <GoabxRadioItem value="no" label="No" />
          </GoabxRadioGroup>
        </GoabxFormItem>
        <GoabxFormItem label="Start date" mt="l">
          <GoabxDatePicker onChange={() => {}} value={new Date("2022-09-01")} />
          <GoabxCheckbox name="startDateApproximate" text="Approximate date" value="y" mt="s" />
        </GoabxFormItem>
        <GoabxFormItem label="Credential received?" mt="l">
          <GoabxRadioGroup name="credentialReceived" orientation="horizontal" onChange={() => {}}>
            <GoabxRadioItem value="yes" label="Yes" />
            <GoabxRadioItem value="no" label="No" />
          </GoabxRadioGroup>
        </GoabxFormItem>
      </GoabxDrawer>
open = false;

  onClick() {
    this.open = true;
  }

  onClose() {
    this.open = false;
  }

  dropdownOnChange(event: any) {
    console.log(event);
  }

  inputOnChange(event: any) {
    console.log(event);
  }

  radioOnChange(event: any) {
    console.log(event);
  }

  dateOnChange(event: any) {
    console.log(event);
  }

  closeDrawer() {
    this.open = false;
  }
<goabx-button leadingIcon="add" (onClick)="onClick()">Add Record</goabx-button>
<goabx-drawer maxSize="492px" [open]="open" heading="Add Record" position="right" (onClose)="onClose()" [actions]="actions">
  <goabx-form-item label="Level of education">
    <goabx-dropdown (onChange)="dropdownOnChange($event)" name="education" value="university">
      <goabx-dropdown-item value="high-school" label="High School Diploma"></goabx-dropdown-item>
      <goabx-dropdown-item value="college" label="College Diploma"></goabx-dropdown-item>
      <goabx-dropdown-item value="university" label="University Degree"></goabx-dropdown-item>
      <goabx-dropdown-item value="masters" label="Master's Degree"></goabx-dropdown-item>
      <goabx-dropdown-item value="doctorate" label="Doctorate"></goabx-dropdown-item>
    </goabx-dropdown>
  </goabx-form-item>
  <goabx-form-item label="Educational institution" mt="l">
    <goabx-input name="education" type="text" (onChange)="inputOnChange($event)"></goabx-input>
  </goabx-form-item>
  <goabx-form-item label="Field of study" requirement="optional" mt="l">
    <goabx-input name="fieldOfStudy" type="text" (onChange)="inputOnChange($event)"></goabx-input>
  </goabx-form-item>
  <goabx-form-item label="Is the person currently attending?" mt="l">
    <goabx-radio-group name="attendTraining" orientation="horizontal" (onChange)="radioOnChange($event)">
      <goabx-radio-item value="yes" label="Yes"></goabx-radio-item>
      <goabx-radio-item value="no" label="No"></goabx-radio-item>
    </goabx-radio-group>
  </goabx-form-item>
  <goabx-form-item label="Start date" mt="l">
    <goabx-date-picker (onChange)="dateOnChange($event)"></goabx-date-picker>
    <goabx-checkbox name="startDateApproximate" text="Approximate date" value="y" mt="s"></goabx-checkbox>
  </goabx-form-item>
  <goabx-form-item label="Credential received?" mt="l">
    <goabx-radio-group name="credentialReceived" orientation="horizontal" (onChange)="radioOnChange($event)">
      <goabx-radio-item value="yes" label="Yes"></goabx-radio-item>
      <goabx-radio-item value="no" label="No"></goabx-radio-item>
    </goabx-radio-group>
  </goabx-form-item>
  <ng-template #actions>
    <goab-button-group>
      <goabx-button type="primary" size="compact" (onClick)="closeDrawer()">Add record</goabx-button>
      <goabx-button type="tertiary" size="compact" (onClick)="closeDrawer()">Cancel</goabx-button>
    </goab-button-group>
  </ng-template>
</goabx-drawer>
const drawer = document.getElementById("record-drawer");
const openBtn = document.getElementById("open-drawer-btn");
const addBtn = document.getElementById("add-record-btn");
const cancelBtn = document.getElementById("cancel-btn");

openBtn.addEventListener("_click", () => {
  drawer.setAttribute("open", "true");
});

drawer.addEventListener("_close", () => {
  drawer.removeAttribute("open");
});

addBtn.addEventListener("_click", () => {
  drawer.removeAttribute("open");
});

cancelBtn.addEventListener("_click", () => {
  drawer.removeAttribute("open");
});
<goa-button version="2" id="open-drawer-btn" leadingicon="add">Add Record</goa-button>
<goa-drawer version="2" id="record-drawer" max-size="492px" heading="Add Record" position="right">
  <goa-form-item version="2" label="Level of education">
    <goa-dropdown version="2" name="education" value="university">
      <goa-dropdown-item value="high-school" label="High School Diploma"></goa-dropdown-item>
      <goa-dropdown-item value="college" label="College Diploma"></goa-dropdown-item>
      <goa-dropdown-item value="university" label="University Degree"></goa-dropdown-item>
      <goa-dropdown-item value="masters" label="Master's Degree"></goa-dropdown-item>
      <goa-dropdown-item value="doctorate" label="Doctorate"></goa-dropdown-item>
    </goa-dropdown>
  </goa-form-item>
  <goa-form-item version="2" label="Educational institution" mt="l">
    <goa-input version="2" name="education" type="text"></goa-input>
  </goa-form-item>
  <goa-form-item version="2" label="Field of study" requirement="optional" mt="l">
    <goa-input version="2" name="fieldOfStudy" type="text"></goa-input>
  </goa-form-item>
  <goa-form-item version="2" label="Is the person currently attending?" mt="l">
    <goa-radio-group version="2" name="attendTraining" orientation="horizontal">
      <goa-radio-item value="yes" label="Yes"></goa-radio-item>
      <goa-radio-item value="no" label="No"></goa-radio-item>
    </goa-radio-group>
  </goa-form-item>
  <goa-form-item version="2" label="Start date" mt="l">
    <goa-date-picker version="2"></goa-date-picker>
    <goa-checkbox version="2" name="startDateApproximate" text="Approximate date" value="y" mt="s"></goa-checkbox>
  </goa-form-item>
  <goa-form-item version="2" label="Credential received?" mt="l">
    <goa-radio-group version="2" name="credentialReceived" orientation="horizontal">
      <goa-radio-item value="yes" label="Yes"></goa-radio-item>
      <goa-radio-item value="no" label="No"></goa-radio-item>
    </goa-radio-group>
  </goa-form-item>
  <div slot="actions">
    <goa-button-group>
      <goa-button version="2" id="add-record-btn" type="primary" size="compact">Add record</goa-button>
      <goa-button version="2" id="cancel-btn" type="tertiary" size="compact">Cancel</goa-button>
    </goa-button-group>
  </div>
</goa-drawer>

Add another item in a modal

const [open, setOpen] = useState(false);
  const [type, setType] = useState<string>();
  const [name, setName] = useState<string>();
  const [description, setDescription] = useState<string>();
<GoabxButton type="tertiary" leadingIcon="add" onClick={() => setOpen(true)}>
        Add another item
      </GoabxButton>
      <GoabxModal
          heading="Add a new item"
          open={open}
          actions={
            <GoabButtonGroup alignment="end">
              <GoabxButton type="tertiary" size="compact" onClick={() => setOpen(false)}>
                Cancel
              </GoabxButton>
              <GoabxButton type="primary" size="compact" onClick={() => setOpen(false)}>
                Save new item
              </GoabxButton>
            </GoabButtonGroup>
          }
        >
          <p>Fill in the information to create a new item</p>
          <GoabxFormItem label="Type" mt="l">
            <GoabxDropdown onChange={(e) => setType(e.value)} value={type}>
              <GoabxDropdownItem value="1" label="Option 1" />
              <GoabxDropdownItem value="2" label="Option 2" />
            </GoabxDropdown>
          </GoabxFormItem>
          <GoabxFormItem label="Name" mt="l">
            <GoabxInput
              onChange={(e) => setName(e.value)}
              value={name}
              name="name"
              width="100%"
            />
          </GoabxFormItem>
          <GoabxFormItem label="Description" mt="l">
            <GoabxTextArea
              name="description"
              rows={3}
              width="100%"
              onChange={(e) => setDescription(e.value)}
              value={description}
            />
          </GoabxFormItem>
      </GoabxModal>
open = false;
  type: string | undefined = "";
  name = "";
  description = "";

  toggleModal() {
    this.open = !this.open;
  }

  updateType(event: any) {
    this.type = event.value;
  }

  updateName(event: any) {
    this.name = event.value;
  }

  updateDescription(event: any) {
    this.description = event.value;
  }
<goabx-button type="tertiary" leadingIcon="add" (onClick)="toggleModal()">Add another item</goabx-button>
<goabx-modal [open]="open" (onClose)="toggleModal()" heading="Add a new item" [actions]="actions">
  <p>Fill in the information to create a new item</p>
  <goabx-form-item label="Type" mt="l">
    <goabx-dropdown (onChange)="updateType($event)" [value]="type">
      <goabx-dropdown-item value="1" label="Option 1"></goabx-dropdown-item>
      <goabx-dropdown-item value="2" label="Option 2"></goabx-dropdown-item>
    </goabx-dropdown>
  </goabx-form-item>
  <goabx-form-item label="Name" mt="l">
    <goabx-input name="name" width="100%" (onChange)="updateName($event)" [value]="name"></goabx-input>
  </goabx-form-item>
  <goabx-form-item label="Description" mt="l">
    <goabx-textarea name="description" width="100%" [rows]="3" (onChange)="updateDescription($event)" [value]="description"></goabx-textarea>
  </goabx-form-item>
  <ng-template #actions>
    <goab-button-group alignment="end">
      <goabx-button type="tertiary" size="compact" (onClick)="toggleModal()">Cancel</goabx-button>
      <goabx-button type="primary" size="compact" (onClick)="toggleModal()">Save new item</goabx-button>
    </goab-button-group>
  </ng-template>
</goabx-modal>
const modal = document.getElementById("add-item-modal");
const openBtn = document.getElementById("open-modal-btn");
const cancelBtn = document.getElementById("cancel-btn");
const saveBtn = document.getElementById("save-btn");

openBtn.addEventListener("_click", () => {
  modal.setAttribute("open", "true");
});

modal.addEventListener("_close", () => {
  modal.removeAttribute("open");
});

cancelBtn.addEventListener("_click", () => {
  modal.removeAttribute("open");
});

saveBtn.addEventListener("_click", () => {
  modal.removeAttribute("open");
});
<goa-button version="2" id="open-modal-btn" type="tertiary" leadingicon="add">Add another item</goa-button>
<goa-modal version="2" id="add-item-modal" heading="Add a new item">
  <p>Fill in the information to create a new item</p>
  <goa-form-item version="2" label="Type" mt="l">
    <goa-dropdown version="2" id="type-dropdown">
      <goa-dropdown-item value="1" label="Option 1"></goa-dropdown-item>
      <goa-dropdown-item value="2" label="Option 2"></goa-dropdown-item>
    </goa-dropdown>
  </goa-form-item>
  <goa-form-item version="2" label="Name" mt="l">
    <goa-input version="2" name="name" width="100%" id="name-input"></goa-input>
  </goa-form-item>
  <goa-form-item version="2" label="Description" mt="l">
    <goa-textarea version="2" name="description" width="100%" rows="3" id="description-textarea"></goa-textarea>
  </goa-form-item>
  <div slot="actions">
    <goa-button-group alignment="end">
      <goa-button version="2" id="cancel-btn" type="tertiary" size="compact">Cancel</goa-button>
      <goa-button version="2" id="save-btn" type="primary" size="compact">Save new item</goa-button>
    </goa-button-group>
  </div>
</goa-modal>

Ask a user for an address

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>
      <GoabxFormItem label="Street Address">
        <GoabxInput
          name="address"
          type="text"
          value={address}
          onChange={(e) => setAddress(e.value)}
          width="100%"
        />
      </GoabxFormItem>
      <GoabxFormItem label="Suite or unit #" mt="l">
        <GoabxInput
          name="suite"
          type="text"
          value={suite}
          onChange={(e) => setSuite(e.value)}
          width="100%"
        />
      </GoabxFormItem>
      <GoabxFormItem label="City or town" mt="l">
        <GoabxInput
          name="city"
          type="text"
          value={city}
          onChange={(e) => setCity(e.value)}
          width="100%"
        />
      </GoabxFormItem>
      <GoabBlock direction="row" gap="l" mt="l">
        <GoabxFormItem label="Province or territory">
          <GoabxDropdown
            onChange={(e) => setProvince(e.value ?? "")}
            name="province"
            value={province}
          >
            <GoabxDropdownItem label="Alberta" value="AB" />
            <GoabxDropdownItem label="British Columbia" value="BC" />
            <GoabxDropdownItem label="Manitoba" value="MB" />
            <GoabxDropdownItem label="New Brunswick" value="NB" />
            <GoabxDropdownItem label="Newfoundland and Labrador" value="NL" />
            <GoabxDropdownItem label="Northwest Territories" value="NT" />
            <GoabxDropdownItem label="Nova Scotia" value="NS" />
            <GoabxDropdownItem label="Nunavut" value="NU" />
            <GoabxDropdownItem label="Ontario" value="ON" />
            <GoabxDropdownItem label="Prince Edward Island" value="PE" />
            <GoabxDropdownItem label="Quebec" value="QC" />
            <GoabxDropdownItem label="Saskatchewan" value="SK" />
            <GoabxDropdownItem label="Yukon" value="YT" />
          </GoabxDropdown>
        </GoabxFormItem>
        <GoabxFormItem label="Postal Code">
          <GoabxInput
            name="postalCode"
            type="text"
            value={postalCode}
            onChange={(e) => setPostalCode(e.value)}
            width="7ch"
          />
        </GoabxFormItem>
      </GoabBlock>
      <GoabButtonGroup alignment="start" mt="2xl">
        <GoabxButton type="primary" onClick={() => {}}>
          Save and continue
        </GoabxButton>
        <GoabxButton type="secondary" onClick={() => {}}>
          Cancel
        </GoabxButton>
      </GoabButtonGroup>
form!: FormGroup;

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      address: [""],
      suite: [""],
      city: [""],
      province: [""],
      postalCode: [""],
    });
  }

  onClick() {
    // Handle form submission
  }
<goab-text size="heading-l" mt="none" mb="xl">What is your address?</goab-text>
<goabx-form-item label="Street Address">
  <goabx-input name="address" type="text" [formControl]="form.controls.address" width="100%"></goabx-input>
</goabx-form-item>
<goabx-form-item label="Suite or unit #" mt="l">
  <goabx-input name="suite" type="text" [formControl]="form.controls.suite" width="100%"></goabx-input>
</goabx-form-item>
<goabx-form-item label="City or town" mt="l">
  <goabx-input name="city" type="text" [formControl]="form.controls.city" width="100%"></goabx-input>
</goabx-form-item>
<goab-block direction="row" gap="l" mt="l">
  <goabx-form-item label="Province or territory">
    <goabx-dropdown name="province" [formControl]="form.controls.province">
      <goabx-dropdown-item label="Alberta" value="AB"></goabx-dropdown-item>
      <goabx-dropdown-item label="British Columbia" value="BC"></goabx-dropdown-item>
      <goabx-dropdown-item label="Manitoba" value="MB"></goabx-dropdown-item>
      <goabx-dropdown-item label="New Brunswick" value="NB"></goabx-dropdown-item>
      <goabx-dropdown-item label="Newfoundland and Labrador" value="NL"></goabx-dropdown-item>
      <goabx-dropdown-item label="Northwest Territories" value="NT"></goabx-dropdown-item>
      <goabx-dropdown-item label="Nova Scotia" value="NS"></goabx-dropdown-item>
      <goabx-dropdown-item label="Nunavut" value="NU"></goabx-dropdown-item>
      <goabx-dropdown-item label="Ontario" value="ON"></goabx-dropdown-item>
      <goabx-dropdown-item label="Prince Edward Island" value="PE"></goabx-dropdown-item>
      <goabx-dropdown-item label="Quebec" value="QC"></goabx-dropdown-item>
      <goabx-dropdown-item label="Saskatchewan" value="SK"></goabx-dropdown-item>
      <goabx-dropdown-item label="Yukon" value="YT"></goabx-dropdown-item>
    </goabx-dropdown>
  </goabx-form-item>
  <goabx-form-item label="Postal Code">
    <goabx-input name="postalCode" type="text" [formControl]="form.controls.postalCode" width="7ch"></goabx-input>
  </goabx-form-item>
</goab-block>
<goab-button-group alignment="start" mt="2xl">
  <goabx-button type="primary" (onClick)="onClick()">Save and continue</goabx-button>
  <goabx-button type="secondary" (onClick)="onClick()">Cancel</goabx-button>
</goab-button-group>
document.getElementById("save-btn")?.addEventListener("_click", () => {
  console.log("Form submitted");
});
<goa-text size="heading-l" mt="none" mb="xl">What is your address?</goa-text>
<goa-form-item version="2" label="Street Address">
  <goa-input version="2" name="address" type="text" width="100%" id="address-input"></goa-input>
</goa-form-item>
<goa-form-item version="2" label="Suite or unit #" mt="l">
  <goa-input version="2" name="suite" type="text" width="100%" id="suite-input"></goa-input>
</goa-form-item>
<goa-form-item version="2" label="City or town" mt="l">
  <goa-input version="2" name="city" type="text" width="100%" id="city-input"></goa-input>
</goa-form-item>
<goa-block direction="row" gap="l" mt="l">
  <goa-form-item version="2" label="Province or territory">
    <goa-dropdown version="2" name="province" id="province-dropdown">
      <goa-dropdown-item label="Alberta" value="AB"></goa-dropdown-item>
      <goa-dropdown-item label="British Columbia" value="BC"></goa-dropdown-item>
      <goa-dropdown-item label="Manitoba" value="MB"></goa-dropdown-item>
      <goa-dropdown-item label="New Brunswick" value="NB"></goa-dropdown-item>
      <goa-dropdown-item label="Newfoundland and Labrador" value="NL"></goa-dropdown-item>
      <goa-dropdown-item label="Northwest Territories" value="NT"></goa-dropdown-item>
      <goa-dropdown-item label="Nova Scotia" value="NS"></goa-dropdown-item>
      <goa-dropdown-item label="Nunavut" value="NU"></goa-dropdown-item>
      <goa-dropdown-item label="Ontario" value="ON"></goa-dropdown-item>
      <goa-dropdown-item label="Prince Edward Island" value="PE"></goa-dropdown-item>
      <goa-dropdown-item label="Quebec" value="QC"></goa-dropdown-item>
      <goa-dropdown-item label="Saskatchewan" value="SK"></goa-dropdown-item>
      <goa-dropdown-item label="Yukon" value="YT"></goa-dropdown-item>
    </goa-dropdown>
  </goa-form-item>
  <goa-form-item version="2" label="Postal Code">
    <goa-input version="2" name="postalCode" type="text" width="7ch" id="postal-input"></goa-input>
  </goa-form-item>
</goa-block>
<goa-button-group alignment="start" mt="2xl">
  <goa-button version="2" type="primary" id="save-btn">Save and continue</goa-button>
  <goa-button version="2" type="secondary" id="cancel-btn">Cancel</goa-button>
</goa-button-group>

Ask a user for an Indian registration number

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

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      bandNo: [""],
      family: [""],
      position: [""],
    });
  }
<goabx-form-item label="Indian registration number" labelSize="large">
  <goab-block gap="m" direction="row">
    <goabx-form-item label="Band #" helpText="3 digits">
      <goabx-input
        [formControl]="form.controls.bandNo"
        name="bandNo"
        width="88px"
        [maxLength]="3">
      </goabx-input>
    </goabx-form-item>
    <goabx-form-item label="Family" helpText="Up to 5 digits">
      <goabx-input
        [formControl]="form.controls.family"
        name="family"
        width="105px"
        [maxLength]="5">
      </goabx-input>
    </goabx-form-item>
    <goabx-form-item label="Position" helpText="2 digits">
      <goabx-input
        [formControl]="form.controls.position"
        name="position"
        width="71px"
        [maxLength]="2">
      </goabx-input>
    </goabx-form-item>
  </goab-block>
</goabx-form-item>
["band-input", "family-input", "position-input"].forEach((id) => {
  document.getElementById(id)?.addEventListener("_change", (e) => {
    console.log(`${id}:`, e.detail.value);
  });
});
<goa-form-item version="2" label="Indian registration number" labelsize="large">
  <goa-block gap="m" direction="row">
    <goa-form-item version="2" label="Band #" helptext="3 digits">
      <goa-input version="2"
        name="bandNo"
        id="band-input"
        width="88px"
        maxlength="3">
      </goa-input>
    </goa-form-item>
    <goa-form-item version="2" label="Family" helptext="Up to 5 digits">
      <goa-input version="2"
        name="family"
        id="family-input"
        width="105px"
        maxlength="5">
      </goa-input>
    </goa-form-item>
    <goa-form-item version="2" label="Position" helptext="2 digits">
      <goa-input version="2"
        name="position"
        id="position-input"
        width="71px"
        maxlength="2">
      </goa-input>
    </goa-form-item>
  </goa-block>
</goa-form-item>

Ask a user for direct deposit information

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>
        <GoabxFormItem
          label="Bank or Institution number"
          helpText="3-4 digits in length"
        >
          <GoabxInput
            maxLength={4}
            name="bankNumber"
            onChange={(e) => setBankNumber(e.value)}
            value={bankNumber}
            ariaLabel="bankNumber"
            width="88px"
          />
        </GoabxFormItem>
        <GoabxFormItem
          label="Branch or Transit number"
          helpText="5 digits in length"
          mt="l"
        >
          <GoabxInput
            maxLength={5}
            name="transitNumber"
            onChange={(e) => setTransitNumber(e.value)}
            value={transitNumber}
            ariaLabel="transitNumber"
            width="143px"
          />
        </GoabxFormItem>
        <GoabxFormItem
          label="Account number"
          helpText="3-12 digits in length"
          mt="l"
        >
          <GoabxInput
            maxLength={12}
            name="accountNumber"
            value={accountNumber}
            onChange={(e) => setAccountNumber(e.value)}
            ariaLabel="accountNumber"
          />
        </GoabxFormItem>
      </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>

      <GoabxButton type="submit" mt="2xl">
        Save and continue
      </GoabxButton>
form!: FormGroup;

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      bankNumber: [""],
      transitNumber: [""],
      accountNumber: [""],
    });
  }
<goab-text as="h1" size="heading-l" mt="none" mb="m">Direct deposit information</goab-text>
<goab-text 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.
</goab-text>
<form [formGroup]="form">
  <goabx-form-item
    label="Bank or Institution number"
    helpText="3-4 digits in length">
    <goabx-input
      [maxLength]="4"
      name="bankNumber"
      [formControl]="form.controls.bankNumber"
      ariaLabel="bankNumber"
      width="88px">
    </goabx-input>
  </goabx-form-item>
  <goabx-form-item
    label="Branch or Transit number"
    helpText="5 digits in length"
    mt="l">
    <goabx-input
      [maxLength]="5"
      name="transitNumber"
      [formControl]="form.controls.transitNumber"
      ariaLabel="transitNumber"
      width="143px">
    </goabx-input>
  </goabx-form-item>
  <goabx-form-item
    label="Account number"
    helpText="3-12 digits in length"
    mt="l">
    <goabx-input
      [maxLength]="12"
      name="accountNumber"
      [formControl]="form.controls.accountNumber"
      ariaLabel="accountNumber">
    </goabx-input>
  </goabx-form-item>
</form>
<goab-details heading="Where can I find this information on a personal cheque?" mt="l">
  <goab-text as="p" mb="m">Below is an example of where you can find the required bank information on a personal cheque.</goab-text>
  <img src="https://design.alberta.ca/images/details-demo.jpg" alt="Cheque example" />
</goab-details>

<goabx-button type="submit" mt="2xl" (onClick)="onSubmit()">
  Save and continue
</goabx-button>
["bank-number-input", "transit-number-input", "account-number-input"].forEach((id) => {
  document.getElementById(id)?.addEventListener("_change", (e) => {
    console.log(`${id}:`, e.detail.value);
  });
});
<goa-text as="h1" size="heading-l" mt="none" mb="m">Direct deposit information</goa-text>
<goa-text 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.
</goa-text>
<form>
  <goa-form-item version="2"
    label="Bank or Institution number"
    helptext="3-4 digits in length">
    <goa-input version="2"
      maxlength="4"
      name="bankNumber"
      id="bank-number-input"
      aria-label="bankNumber"
      width="88px">
    </goa-input>
  </goa-form-item>
  <goa-form-item version="2"
    label="Branch or Transit number"
    helptext="5 digits in length"
    mt="l">
    <goa-input version="2"
      maxlength="5"
      name="transitNumber"
      id="transit-number-input"
      aria-label="transitNumber"
      width="143px">
    </goa-input>
  </goa-form-item>
  <goa-form-item version="2"
    label="Account number"
    helptext="3-12 digits in length"
    mt="l">
    <goa-input version="2"
      maxlength="12"
      name="accountNumber"
      id="account-number-input"
      aria-label="accountNumber">
    </goa-input>
  </goa-form-item>
</form>
<goa-details heading="Where can I find this information on a personal cheque?" mt="l">
  <goa-text as="p" mb="m">Below is an example of where you can find the required bank information on a personal cheque.</goa-text>
  <img src="https://design.alberta.ca/images/details-demo.jpg" alt="Cheque example" />
</goa-details>

<goa-button version="2" id="submit-btn" type="submit" mt="2xl">
  Save and continue
</goa-button>

Ask a user for dollar amounts

const [tuitionAmount, setTuitionAmount] = useState("");
  const [suppliesAmount, setSuppliesAmount] = useState("");
  const [othersAmount, setOthersAmount] = useState("");
<GoabxFormItem label="Tuition">
        <GoabxInput
          onChange={(e) => setTuitionAmount(e.value)}
          value={tuitionAmount}
          name="tuitionAmount"
          leadingContent="$"
        />
      </GoabxFormItem>
      <GoabxFormItem label="Books/Supplies/Instruments" mt="l">
        <GoabxInput
          onChange={(e) => setSuppliesAmount(e.value)}
          value={suppliesAmount}
          name="suppliesAmount"
          leadingContent="$"
        />
      </GoabxFormItem>
      <GoabxFormItem label="Other costs" mt="l">
        <GoabxInput
          onChange={(e) => setOthersAmount(e.value)}
          value={othersAmount}
          name="othersAmount"
          leadingContent="$"
        />
      </GoabxFormItem>
costFormGroup = new FormGroup({
    tuitionFeeAmount: new FormControl(""),
    suppliesAmount: new FormControl(""),
    othersAmount: new FormControl(""),
  });
<form [formGroup]="costFormGroup">
  <goabx-form-item label="Tuition">
    <goabx-input name="tuition" formControlName="tuitionFeeAmount">
      <div slot="leadingContent">$</div>
    </goabx-input>
  </goabx-form-item>
  <goabx-form-item label="Books/Supplies/Instruments" mt="l">
    <goabx-input name="book" formControlName="suppliesAmount">
      <div slot="leadingContent">$</div>
    </goabx-input>
  </goabx-form-item>
  <goabx-form-item label="Other costs" mt="l">
    <goabx-input name="others" formControlName="othersAmount">
      <div slot="leadingContent">$</div>
    </goabx-input>
  </goabx-form-item>
</form>
["tuition-input", "supplies-input", "others-input"].forEach((id) => {
  document.getElementById(id)?.addEventListener("_change", (e) => {
    console.log(`${id}:`, e.detail.value);
  });
});
<form>
  <goa-form-item version="2" label="Tuition">
    <goa-input version="2" name="tuition" id="tuition-input">
      <div slot="leadingContent">$</div>
    </goa-input>
  </goa-form-item>
  <goa-form-item version="2" label="Books/Supplies/Instruments" mt="l">
    <goa-input version="2" name="book" id="supplies-input">
      <div slot="leadingContent">$</div>
    </goa-input>
  </goa-form-item>
  <goa-form-item version="2" label="Other costs" mt="l">
    <goa-input version="2" name="others" id="others-input">
      <div slot="leadingContent">$</div>
    </goa-input>
  </goa-form-item>
</form>

Disabled button with a required field

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>
      <GoabxFormItem label="Name" requirement="required">
        <GoabxInput
          name="input"
          type="text"
          onChange={handleInputChange}
          value={inputValue}
          width="100%"
        />
      </GoabxFormItem>

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

  onInputChange(detail: GoabInputOnChangeDetail): void {
    this.inputValue = detail.value;
  }

  onConfirm(): void {
    // Handle form submission
    console.log("Form submitted with:", this.inputValue);
  }

  onCancel(): void {
    // Handle cancellation
    this.inputValue = "";
  }

  get isDisabled(): boolean {
    return this.inputValue.trim() === "";
  }
<form>
  <goabx-form-item label="Name" requirement="required">
    <goabx-input
      name="input"
      type="text"
      (onChange)="onInputChange($event)"
      [value]="inputValue"
      width="100%">
    </goabx-input>
  </goabx-form-item>

  <goab-button-group alignment="start" mt="xl">
    <goabx-button [disabled]="isDisabled" (onClick)="onConfirm()">
      Confirm
    </goabx-button>
    <goabx-button type="secondary" (onClick)="onCancel()">
      Cancel
    </goabx-button>
  </goab-button-group>
</form>
const nameInput = document.getElementById('name-input');
const confirmBtn = document.getElementById('confirm-btn');
const cancelBtn = document.getElementById('cancel-btn');

nameInput.addEventListener('_change', (e) => {
  const value = e.detail.value.trim();
  if (value === '') {
    confirmBtn.setAttribute('disabled', 'true');
  } else {
    confirmBtn.removeAttribute('disabled');
  }
});

confirmBtn.addEventListener('_click', () => {
  console.log('Form submitted with:', nameInput.value);
});

cancelBtn.addEventListener('_click', () => {
  nameInput.value = '';
  confirmBtn.setAttribute('disabled', 'true');
});
<form id="required-field-form">
  <goa-form-item version="2" label="Name" requirement="required">
    <goa-input version="2" id="name-input" name="input" type="text" width="100%"></goa-input>
  </goa-form-item>

  <goa-button-group alignment="start" mt="xl">
    <goa-button version="2" id="confirm-btn" disabled="true">Confirm</goa-button>
    <goa-button version="2" id="cancel-btn" type="secondary">Cancel</goa-button>
  </goa-button-group>
</form>

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);
  }
<GoabxFormItem
          requirement="required"
          label="Name of item"
          error={taskError ? "Please enter item name" : undefined}
          helpText="Add an item to the dropdown list below">
          <GoabxInput
            onChange={(event: GoabInputOnChangeDetail) => setNewTask(event.value)}
            name="item"
            value={newTask}
          />
        </GoabxFormItem>

        <GoabxFormItem mt="l" label="Add to">
          <GoabxRadioGroup
            name="mountType"
            onChange={(event: GoabRadioGroupOnChangeDetail) => onMountTypeChange(event.value)}
            value={mountType}
            orientation="horizontal">
            <GoabxRadioItem value="prepend" label="Start" />
            <GoabxRadioItem value="append" label="End" />
          </GoabxRadioGroup>
        </GoabxFormItem>

        <GoabButtonGroup alignment="start" gap="relaxed" mt="l">
          <GoabxButton type="primary" onClick={addTask}>
            Add new item
          </GoabxButton>
          <GoabxButton type="tertiary" onClick={reset}>
            Reset list
          </GoabxButton>
        </GoabButtonGroup>

        <GoabDivider mt="l" />

        <GoabxFormItem mt="l" label="All items">
          <div style={{ width: isReset ? "320px" : "auto" }}>
            <GoabxDropdown
              key={tasks.length}
              onChange={(event: GoabDropdownOnChangeDetail) =>
                setSelectedTask(event.value as string)
              }
              value={selectedTask}
              name="selectedTask">
              {tasks.map(task => (
                <GoabxDropdownItem
                  key={task.value}
                  value={task.value}
                  mountType={task.mount}
                  label={task.label}
                />
              ))}
            </GoabxDropdown>
          </div>
      </GoabxFormItem>
defaultTasks: 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" },
  ];

  tasks: Task[] = [...this.defaultTasks];
  newTask = "";
  mountType: GoabDropdownItemMountType = "append";
  selectedTask = "";
  taskError = false;
  renderTrigger = true;

  onMountTypeChange(event: GoabRadioGroupOnChangeDetail): void {
    this.mountType = event.value as GoabDropdownItemMountType;
  }

  onNewTaskChange(event: GoabInputOnChangeDetail): void {
    this.newTask = event.value;
    this.taskError = false;
  }

  onSelectedTaskChange(event: GoabDropdownOnChangeDetail): void {
    this.selectedTask = event.value as string;
  }

  addTask(): void {
    if (this.newTask === "") {
      this.taskError = true;
      return;
    }
    this.taskError = false;

    const task: Task = {
      label: this.newTask,
      value: this.newTask.toLowerCase().replace(" ", "-"),
      mount: this.mountType,
    };
    this.tasks = this.mountType === "prepend"
      ? [task, ...this.tasks]
      : [...this.tasks, task];
    this.newTask = "";
  }

  reset(): void {
    this.newTask = "";
    this.selectedTask = "";
    this.taskError = false;
    this.tasks = [...this.defaultTasks];
    this.forceRerender();
  }

  forceRerender(): void {
    this.renderTrigger = false;
    setTimeout(() => {
      this.renderTrigger = true;
    }, 0);
  }

  trackByFn(index: number, item: Task): string {
    return item.value;
  }
<goabx-form-item
  requirement="required"
  label="Name of item"
  [error]="taskError ? 'Please enter item name' : undefined"
  helpText="Add an item to the dropdown list below">
  <goabx-input
    name="item"
    [value]="newTask"
    (onChange)="onNewTaskChange($event)">
  </goabx-input>
</goabx-form-item>

<goabx-form-item mt="l" label="Add to">
  <goabx-radio-group
    name="mountType"
    [value]="mountType"
    orientation="horizontal"
    (onChange)="onMountTypeChange($event)">
    <goabx-radio-item value="prepend" label="Start"></goabx-radio-item>
    <goabx-radio-item value="append" label="End"></goabx-radio-item>
  </goabx-radio-group>
</goabx-form-item>

<goab-button-group alignment="start" gap="relaxed" mt="l">
  <goabx-button type="primary" (onClick)="addTask()">
    Add new item
  </goabx-button>
  <goabx-button type="tertiary" (onClick)="reset()">
    Reset list
  </goabx-button>
</goab-button-group>

<goab-divider mt="l"></goab-divider>

<goabx-form-item mt="l" label="All items">
  <ng-container *ngIf="renderTrigger">
    <goabx-dropdown
      [value]="selectedTask"
      name="selectedTask"
      (onChange)="onSelectedTaskChange($event)">
      @for (task of tasks; track task.value) {
      <goabx-dropdown-item
        [value]="task.value"
        [mountType]="task.mount"
        [label]="task.label">
      </goabx-dropdown-item>
      }
    </goabx-dropdown>
  </ng-container>
</goabx-form-item>
const itemInput = document.getElementById('item-input');
const itemFormItem = document.getElementById('item-form-item');
const mountTypeGroup = document.getElementById('mount-type');
const addBtn = document.getElementById('add-btn');
const resetBtn = document.getElementById('reset-btn');
const dropdown = document.getElementById('task-dropdown');

let mountType = 'append';
let newTask = '';

const defaultItems = [
  { value: 'finish-report', label: 'Finish Report' },
  { value: 'attend-meeting', label: 'Attend Meeting' },
  { value: 'reply-emails', label: 'Reply Emails' }
];

mountTypeGroup.addEventListener('_change', (e) => {
  mountType = e.detail.value;
});

itemInput.addEventListener('_change', (e) => {
  newTask = e.detail.value;
  itemFormItem.removeAttribute('error');
});

addBtn.addEventListener('_click', () => {
  if (newTask === '') {
    itemFormItem.setAttribute('error', 'Please enter item name');
    return;
  }

  const newItem = document.createElement('goa-dropdown-item');
  newItem.setAttribute('value', newTask.toLowerCase().replace(' ', '-'));
  newItem.setAttribute('label', newTask);
  newItem.setAttribute('mount', mountType);
  dropdown.appendChild(newItem);

  itemInput.value = '';
  newTask = '';
});

resetBtn.addEventListener('_click', () => {
  dropdown.innerHTML = '';
  defaultItems.forEach(item => {
    const dropdownItem = document.createElement('goa-dropdown-item');
    dropdownItem.setAttribute('value', item.value);
    dropdownItem.setAttribute('label', item.label);
    dropdown.appendChild(dropdownItem);
  });
  itemInput.value = '';
  newTask = '';
  itemFormItem.removeAttribute('error');
});
<goa-form-item version="2"
  id="item-form-item"
  requirement="required"
  label="Name of item"
  helptext="Add an item to the dropdown list below">
  <goa-input version="2" id="item-input" name="item"></goa-input>
</goa-form-item>

<goa-form-item version="2" mt="l" label="Add to">
  <goa-radio-group version="2" id="mount-type" name="mountType" value="append" orientation="horizontal">
    <goa-radio-item value="prepend" label="Start"></goa-radio-item>
    <goa-radio-item value="append" label="End"></goa-radio-item>
  </goa-radio-group>
</goa-form-item>

<goa-button-group alignment="start" gap="relaxed" mt="l">
  <goa-button version="2" id="add-btn" type="primary">Add new item</goa-button>
  <goa-button version="2" id="reset-btn" type="tertiary">Reset list</goa-button>
</goa-button-group>

<goa-divider mt="l"></goa-divider>

<goa-form-item version="2" mt="l" label="All items">
  <goa-dropdown version="2" id="task-dropdown" name="selectedTask">
    <goa-dropdown-item value="finish-report" label="Finish Report"></goa-dropdown-item>
    <goa-dropdown-item value="attend-meeting" label="Attend Meeting"></goa-dropdown-item>
    <goa-dropdown-item value="reply-emails" label="Reply Emails"></goa-dropdown-item>
  </goa-dropdown>
</goa-form-item>

Filter data in a table

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]);
<GoabxFormItem id="filterChipInput" error={inputError} mb="m">
        <GoabBlock gap="xs" direction="row" alignment="start" width="100%">
          <div style={{ flex: 1 }}>
            <GoabxInput
              name="filterChipInput"
              aria-labelledby="filterChipInput"
              value={inputValue}
              leadingIcon="search"
              width="100%"
              onChange={handleInputChange}
              onKeyPress={handleInputKeyPress}
            />
          </div>
          <GoabxButton type="secondary" onClick={applyFilter} leadingIcon="filter">
            Filter
          </GoabxButton>
        </GoabBlock>
      </GoabxFormItem>

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

      <GoabxTable 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>
                <GoabxBadge 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>
      </GoabxTable>

      {dataFiltered.length === 0 && data.length > 0 && (
        <GoabBlock mt="l" mb="l">
          No results found
        </GoabBlock>
      )}
typedChips: string[] = [];
  inputValue = "";
  inputError = "";
  readonly errorEmpty = "Empty filter";
  readonly errorDuplicate = "Enter a unique filter";

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

  dataFiltered = this.getFilteredData(this.typedChips);

  handleInputChange(detail: GoabInputOnChangeDetail): void {
    const newValue = detail.value.trim();
    this.inputValue = newValue;
  }

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

  applyFilter(): void {
    if (this.inputValue === "") {
      this.inputError = this.errorEmpty;
      return;
    }
    if (this.typedChips.includes(this.inputValue)) {
      this.inputError = this.errorDuplicate;
      return;
    }
    this.typedChips = [...this.typedChips, this.inputValue];
    this.inputValue = "";
    this.inputError = "";
    this.dataFiltered = this.getFilteredData(this.typedChips);
  }

  removeTypedChip(chip: string): void {
    this.typedChips = this.typedChips.filter(c => c !== chip);
    this.dataFiltered = this.getFilteredData(this.typedChips);
    this.inputError = "";
  }

  removeAllTypedChips(): void {
    this.typedChips = [];
    this.dataFiltered = this.getFilteredData(this.typedChips);
    this.inputError = "";
  }

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

  checkNested(obj: object, chip: string): boolean {
    return Object.values(obj).some(value =>
      typeof value === "object" && value !== null
        ? this.checkNested(value, chip)
        : typeof value === "string" && value.toLowerCase().includes(chip.toLowerCase())
    );
  }
<goabx-form-item id="filterChipInput" [error]="inputError" mb="m">
  <goab-block gap="xs" direction="row" alignment="start" width="100%">
    <div style="flex: 1">
      <goabx-input
        name="filterChipInput"
        aria-labelledby="filterChipInput"
        [value]="inputValue"
        leadingIcon="search"
        width="100%"
        (onChange)="handleInputChange($event)"
        (onKeyPress)="handleInputKeyPress($event)"
      >
      </goabx-input>
    </div>
    <goabx-button type="secondary" (onClick)="applyFilter()" leadingIcon="filter">
      Filter
    </goabx-button>
  </goab-block>
</goabx-form-item>

@if (typedChips.length > 0) {
<ng-container>
  <goab-text tag="span" color="secondary" mb="xs" mr="xs"> Filter: </goab-text>
  @for (typedChip of typedChips; track typedChip; let index = $index) {
  <goabx-filter-chip
    [content]="typedChip"
    mb="xs"
    mr="xs"
    (onClick)="removeTypedChip(typedChip)"
  >
  </goabx-filter-chip>
  }
  <goabx-button type="tertiary" size="compact" mb="xs" (onClick)="removeAllTypedChips()">
    Clear all
  </goabx-button>
</ng-container>
}

<goabx-table width="full">
  <thead>
    <tr>
      <th>Status</th>
      <th>Name</th>
      <th class="goa-table-number-header">ID Number</th>
    </tr>
  </thead>
  <tbody>
    @for (item of dataFiltered; track $index) {
    <tr>
      <td>
        <goabx-badge
          [type]="item.status.type"
          [content]="item.status.text"
          [icon]="false"
        ></goabx-badge>
      </td>
      <td>{{ item.name }}</td>
      <td class="goa-table-number-column">{{ item.id }}</td>
    </tr>
    }
  </tbody>
</goabx-table>

@if (dataFiltered.length === 0 && data.length > 0) {
<goab-block mt="l" mb="l"> No results found </goab-block>
}
const filterInput = document.getElementById('filter-input');
const filterBtn = document.getElementById('filter-btn');
const filterFormItem = document.getElementById('filter-form-item');
const chipsContainer = document.getElementById('chips-container');
const chipsList = document.getElementById('chips-list');
const clearAllBtn = document.getElementById('clear-all-btn');
const tableRows = document.querySelectorAll('tbody tr');

let typedChips = [];

function filterTable() {
  tableRows.forEach(row => {
    const badge = row.querySelector('goa-badge');
    const badgeText = badge ? badge.getAttribute('content') || '' : '';
    const text = (row.textContent + ' ' + badgeText).toLowerCase();
    const matches = typedChips.length === 0 || typedChips.every(chip => text.includes(chip.toLowerCase()));
    row.style.display = matches ? '' : 'none';
  });
}

function renderChips() {
  chipsList.innerHTML = '';
  typedChips.forEach(chip => {
    const filterChip = document.createElement('goa-filter-chip');
    filterChip.setAttribute('version', '2');
    filterChip.setAttribute('content', chip);
    filterChip.setAttribute('mb', 'xs');
    filterChip.setAttribute('mr', 'xs');
    filterChip.addEventListener('_click', () => removeChip(chip));
    chipsList.appendChild(filterChip);
  });
  chipsContainer.style.display = typedChips.length > 0 ? 'block' : 'none';
  filterTable();
}

function applyFilter() {
  const value = filterInput.value.trim();
  if (value === '') {
    filterFormItem.setAttribute('error', 'Empty filter');
    return;
  }
  if (typedChips.includes(value)) {
    filterFormItem.setAttribute('error', 'Enter a unique filter');
    return;
  }
  typedChips.push(value);
  filterInput.value = '';
  filterFormItem.removeAttribute('error');
  renderChips();
}

function removeChip(chip) {
  typedChips = typedChips.filter(c => c !== chip);
  renderChips();
}

filterBtn.addEventListener('_click', applyFilter);
clearAllBtn.addEventListener('_click', () => {
  typedChips = [];
  renderChips();
});
<goa-form-item version="2" id="filter-form-item" mb="m">
  <goa-block gap="xs" direction="row" alignment="center" width="100%">
    <div style="flex: 1;">
      <goa-input version="2"
        id="filter-input"
        name="filterChipInput"
        leadingicon="search"
        width="100%">
      </goa-input>
    </div>
    <goa-button version="2" id="filter-btn" type="secondary" leadingicon="filter">
      Filter
    </goa-button>
  </goa-block>
</goa-form-item>

<div id="chips-container" style="display: none;">
  <goa-text tag="span" color="secondary" mb="xs" mr="xs">Filter:</goa-text>
  <span id="chips-list"></span>
  <goa-button version="2" id="clear-all-btn" type="tertiary" size="compact" mb="xs">
    Clear all
  </goa-button>
</div>

<goa-table version="2" width="100%" mt="s">
  <table style="width: 100%;">
    <thead>
      <tr>
        <th>Status</th>
        <th>Name</th>
        <th class="goa-table-number-header">ID Number</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td><goa-badge version="2" type="information" content="In progress" icon="false"></goa-badge></td>
        <td>Ivan Schmidt</td>
        <td class="goa-table-number-column">7838576954</td>
      </tr>
      <tr>
        <td><goa-badge version="2" type="success" content="Completed" icon="false"></goa-badge></td>
        <td>Luz Lakin</td>
        <td class="goa-table-number-column">8576953364</td>
      </tr>
      <tr>
        <td><goa-badge version="2" type="information" content="In progress" icon="false"></goa-badge></td>
        <td>Keith McGlynn</td>
        <td class="goa-table-number-column">9846041345</td>
      </tr>
      <tr>
        <td><goa-badge version="2" type="success" content="Completed" icon="false"></goa-badge></td>
        <td>Melody Frami</td>
        <td class="goa-table-number-column">7385256175</td>
      </tr>
      <tr>
        <td><goa-badge version="2" type="important" content="Updated" icon="false"></goa-badge></td>
        <td>Frederick Skiles</td>
        <td class="goa-table-number-column">5807570418</td>
      </tr>
      <tr>
        <td><goa-badge version="2" type="success" content="Completed" icon="false"></goa-badge></td>
        <td>Dana Pfannerstill</td>
        <td class="goa-table-number-column">5736306857</td>
      </tr>
    </tbody>
  </table>
</goa-table>
const [addressLine1, setAddressLine1] = useState("");
  const [addressLine2, setAddressLine2] = useState("");
  const [townCity, setTownCity] = useState("");
  const [province, setProvince] = useState("");
  const [postalCode, setPostalCode] = useState("");
<GoabxLink leadingIcon="arrow-back" size="small" mb="none">
        Back
      </GoabxLink>

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

      <GoabxFormItem label="Address line 1">
        <GoabxInput
          onChange={(event) => setAddressLine1(event.value)}
          value={addressLine1}
          name="address-line-1"
          ariaLabel="Address line 1"
          width="100%"
        />
      </GoabxFormItem>

      <GoabxFormItem label="Address line 2" mt="l">
        <GoabxInput
          onChange={(event) => setAddressLine2(event.value)}
          value={addressLine2}
          name="address-line-2"
          ariaLabel="Address line 2"
          width="100%"
        />
      </GoabxFormItem>

      <GoabxFormItem label="Town or city" mt="l">
        <GoabxInput
          onChange={(event) => setTownCity(event.value)}
          value={townCity}
          name="town-city"
          ariaLabel="Town or city name"
          width="460px"
        />
      </GoabxFormItem>

      <GoabxFormItem label="Province or territory" mt="l" id="provinceLabel">
        <GoabxDropdown
          onChange={(event) => setProvince(event.value ?? "")}
          value={province}
          name="province-territory"
          ariaLabelledBy="provinceLabel"
        >
          <GoabxDropdownItem value="AB" label="Alberta" />
          <GoabxDropdownItem value="BC" label="British Columbia" />
          <GoabxDropdownItem value="MB" label="Manitoba" />
          <GoabxDropdownItem value="NB" label="New Brunswick" />
          <GoabxDropdownItem value="NL" label="Newfoundland and Labrador" />
          <GoabxDropdownItem value="NS" label="Nova Scotia" />
          <GoabxDropdownItem value="ON" label="Ontario" />
          <GoabxDropdownItem value="PE" label="Prince Edward Island" />
          <GoabxDropdownItem value="QC" label="Quebec" />
          <GoabxDropdownItem value="SK" label="Saskatchewan" />
          <GoabxDropdownItem value="NT" label="Northwest Territories" />
          <GoabxDropdownItem value="NU" label="Nunavut" />
          <GoabxDropdownItem value="YT" label="Yukon" />
        </GoabxDropdown>
      </GoabxFormItem>

      <GoabxFormItem label="Postal code" mt="l">
        <GoabxInput
          onChange={(event) => setPostalCode(event.value)}
          value={postalCode}
          name="postal-code"
          width="105px"
        />
      </GoabxFormItem>

      <GoabxButton type="submit" mt="2xl">
        Save and continue
      </GoabxButton>
addressLine1 = "";
  addressLine2 = "";
  townCity = "";
  province = "";
  postalCode = "";

  onAddressLine1Change(value: string): void {
    this.addressLine1 = value;
  }

  onAddressLine2Change(value: string): void {
    this.addressLine2 = value;
  }

  onTownCityChange(value: string): void {
    this.townCity = value;
  }

  onProvinceChange(value: string): void {
    this.province = value;
  }

  onPostalCodeChange(value: string): void {
    this.postalCode = value;
  }

  onSubmit(): void {
    console.log("Form submitted");
  }
<goabx-link leadingIcon="arrow-back" size="small" mb="none">
  Back
</goabx-link>

<goab-text as="h2" mt="xl" mb="m">Your address</goab-text>
<goab-text mt="none" mb="xl">This is the home address of the person applying</goab-text>

<goabx-form-item label="Address line 1">
  <goabx-input
    (onChange)="onAddressLine1Change($event)"
    [value]="addressLine1"
    name="address-line-1"
    ariaLabel="Address line 1"
    width="100%">
  </goabx-input>
</goabx-form-item>

<goabx-form-item label="Address line 2" mt="l">
  <goabx-input
    (onChange)="onAddressLine2Change($event)"
    [value]="addressLine2"
    name="address-line-2"
    ariaLabel="Address line 2"
    width="100%">
  </goabx-input>
</goabx-form-item>

<goabx-form-item label="Town or city" mt="l">
  <goabx-input
    (onChange)="onTownCityChange($event)"
    [value]="townCity"
    name="town-city"
    ariaLabel="Town or city name"
    width="460px">
  </goabx-input>
</goabx-form-item>

<goabx-form-item label="Province or territory" mt="l" id="provinceLabel">
  <goabx-dropdown
    (onChange)="onProvinceChange($event)"
    [value]="province"
    name="province-territory"
    ariaLabelledBy="provinceLabel">
    <goabx-dropdown-item value="AB" label="Alberta"></goabx-dropdown-item>
    <goabx-dropdown-item value="BC" label="British Columbia"></goabx-dropdown-item>
    <goabx-dropdown-item value="MB" label="Manitoba"></goabx-dropdown-item>
    <goabx-dropdown-item value="NB" label="New Brunswick"></goabx-dropdown-item>
    <goabx-dropdown-item value="NL" label="Newfoundland and Labrador"></goabx-dropdown-item>
    <goabx-dropdown-item value="NS" label="Nova Scotia"></goabx-dropdown-item>
    <goabx-dropdown-item value="ON" label="Ontario"></goabx-dropdown-item>
    <goabx-dropdown-item value="PE" label="Prince Edward Island"></goabx-dropdown-item>
    <goabx-dropdown-item value="QC" label="Quebec"></goabx-dropdown-item>
    <goabx-dropdown-item value="SK" label="Saskatchewan"></goabx-dropdown-item>
    <goabx-dropdown-item value="NT" label="Northwest Territories"></goabx-dropdown-item>
    <goabx-dropdown-item value="NU" label="Nunavut"></goabx-dropdown-item>
    <goabx-dropdown-item value="YT" label="Yukon"></goabx-dropdown-item>
  </goabx-dropdown>
</goabx-form-item>

<goabx-form-item label="Postal code" mt="l">
  <goabx-input
    (onChange)="onPostalCodeChange($event)"
    [value]="postalCode"
    name="postal-code"
    width="105px">
  </goabx-input>
</goabx-form-item>

<goabx-button type="submit" mt="2xl" (onClick)="onSubmit()">
  Save and continue
</goabx-button>
document.getElementById("address-line-1").addEventListener("_change", (e) => {
  console.log("Address line 1:", e.detail.value);
});

document.getElementById("province-territory").addEventListener("_change", (e) => {
  console.log("Province:", e.detail.value);
});

document.getElementById("submit-btn").addEventListener("_click", () => {
  console.log("Form submitted");
});
<goa-link leadingicon="arrow-back" size="small" mb="none">
  Back
</goa-link>

<goa-text as="h2" mt="xl" mb="m">Your address</goa-text>
<goa-text mt="none" mb="xl">This is the home address of the person applying</goa-text>

<goa-form-item version="2" label="Address line 1">
  <goa-input version="2"
    id="address-line-1"
    name="address-line-1"
    aria-label="Address line 1"
    width="100%">
  </goa-input>
</goa-form-item>

<goa-form-item version="2" label="Address line 2" mt="l">
  <goa-input version="2"
    id="address-line-2"
    name="address-line-2"
    aria-label="Address line 2"
    width="100%">
  </goa-input>
</goa-form-item>

<goa-form-item version="2" label="Town or city" mt="l">
  <goa-input version="2"
    id="town-city"
    name="town-city"
    aria-label="Town or city name"
    width="460px">
  </goa-input>
</goa-form-item>

<goa-form-item version="2" label="Province or territory" mt="l" id="provinceLabel">
  <goa-dropdown version="2"
    id="province-territory"
    name="province-territory"
    aria-labelledby="provinceLabel">
    <goa-dropdown-item value="AB" label="Alberta"></goa-dropdown-item>
    <goa-dropdown-item value="BC" label="British Columbia"></goa-dropdown-item>
    <goa-dropdown-item value="MB" label="Manitoba"></goa-dropdown-item>
    <goa-dropdown-item value="NB" label="New Brunswick"></goa-dropdown-item>
    <goa-dropdown-item value="NL" label="Newfoundland and Labrador"></goa-dropdown-item>
    <goa-dropdown-item value="NS" label="Nova Scotia"></goa-dropdown-item>
    <goa-dropdown-item value="ON" label="Ontario"></goa-dropdown-item>
    <goa-dropdown-item value="PE" label="Prince Edward Island"></goa-dropdown-item>
    <goa-dropdown-item value="QC" label="Quebec"></goa-dropdown-item>
    <goa-dropdown-item value="SK" label="Saskatchewan"></goa-dropdown-item>
    <goa-dropdown-item value="NT" label="Northwest Territories"></goa-dropdown-item>
    <goa-dropdown-item value="NU" label="Nunavut"></goa-dropdown-item>
    <goa-dropdown-item value="YT" label="Yukon"></goa-dropdown-item>
  </goa-dropdown>
</goa-form-item>

<goa-form-item version="2" label="Postal code" mt="l">
  <goa-input version="2"
    id="postal-code"
    name="postal-code"
    width="105px">
  </goa-input>
</goa-form-item>

<goa-button version="2" type="submit" mt="2xl" id="submit-btn">
  Save and continue
</goa-button>

Question page

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

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

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

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

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

  onAnswerChange(event: { value: string }): void {
    this.answer = event.value;
  }

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

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

<goabx-form-item label="Email address">
  <goabx-input
    name="email"
    type="email"
    [value]="answer"
    (onChange)="onAnswerChange($event)"
    width="100%">
  </goabx-input>
</goabx-form-item>

<goab-button-group alignment="start" mt="2xl">
  <goabx-button type="primary" (onClick)="handleContinue()">
    Continue
  </goabx-button>
</goab-button-group>
const emailInput = document.getElementById("email-input");
const continueBtn = document.getElementById("continue-btn");
let answer = "";

emailInput.addEventListener("_change", (e) => {
  answer = e.detail.value;
});

continueBtn.addEventListener("_click", () => {
  console.log("Answer submitted:", answer);
});
<goa-link leadingicon="arrow-back" size="small" mb="none">
  Back
</goa-link>

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

<goa-form-item version="2" label="Email address">
  <goa-input version="2"
    id="email-input"
    name="email"
    type="email"
    width="100%">
  </goa-input>
</goa-form-item>

<goa-button-group alignment="start" mt="2xl">
  <goa-button version="2" id="continue-btn" type="primary">
    Continue
  </goa-button>
</goa-button-group>

Reveal input based on a selection

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

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

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      contactMethod: [""],
      phoneNumber: [""],
      emailAddress: [""],
      emailContactMethod: [false],
      phoneContactMethod: [false],
      textContactMethod: [false],
    });
  }
<goabx-form-item [formGroup]="form" label="How would you like to be contacted?" helpText="Select one option">
  <goabx-radio-group name="contactMethod" formControlName="contactMethod">
    <goabx-radio-item label="Email" value="email" [reveal]="emailReveal">
      <ng-template #emailReveal>
        <goabx-form-item label="Email address">
          <goabx-input name="email" formControlName="emailAddress"></goabx-input>
        </goabx-form-item>
      </ng-template>
    </goabx-radio-item>
    <goabx-radio-item value="phone" label="Phone" [reveal]="phoneReveal">
      <ng-template #phoneReveal>
        <goabx-form-item label="Phone number">
          <goabx-input name="phone" formControlName="phoneNumber"></goabx-input>
        </goabx-form-item>
      </ng-template>
    </goabx-radio-item>
    <goabx-radio-item value="text" label="Text message" [reveal]="textReveal">
      <ng-template #textReveal>
        <goabx-form-item label="Mobile phone number">
          <goabx-input name="mobile" formControlName="phoneNumber"></goabx-input>
        </goabx-form-item>
      </ng-template>
    </goabx-radio-item>
  </goabx-radio-group>
</goabx-form-item>

<goabx-form-item [formGroup]="form" label="How would you like to be contacted?" mt="xl">
  <goab-checkbox-list name="contactMethods" formControlName="contactMethods">
    <goabx-checkbox name="email" text="Email" value="email" [reveal]="checkEmailReveal">
      <ng-template #checkEmailReveal>
        <goabx-form-item label="Email address">
          <goabx-input name="email" formControlName="emailAddress"></goabx-input>
        </goabx-form-item>
      </ng-template>
    </goabx-checkbox>
    <goabx-checkbox name="phone" text="Phone" value="phone" [reveal]="checkPhoneReveal">
      <ng-template #checkPhoneReveal>
        <goabx-form-item label="Phone number">
          <goabx-input name="phone" formControlName="phoneNumber"></goabx-input>
        </goabx-form-item>
      </ng-template>
    </goabx-checkbox>
    <goabx-checkbox name="text" text="Text message" value="text" [reveal]="checkTextReveal">
      <ng-template #checkTextReveal>
        <goabx-form-item label="Mobile phone number">
          <goabx-input name="mobile" formControlName="phoneNumber"></goabx-input>
        </goabx-form-item>
      </ng-template>
    </goabx-checkbox>
  </goab-checkbox-list>
</goabx-form-item>
<goa-form-item version="2" label="How would you like to be contacted?" helptext="Select one option">
  <goa-radio-group version="2" name="contactMethod" id="radio-contact">
    <goa-radio-item value="email" label="Email">
      <div slot="reveal">
        <goa-form-item version="2" label="Email address">
          <goa-input version="2" name="email"></goa-input>
        </goa-form-item>
      </div>
    </goa-radio-item>
    <goa-radio-item value="phone" label="Phone">
      <div slot="reveal">
        <goa-form-item version="2" label="Phone number">
          <goa-input version="2" name="phone"></goa-input>
        </goa-form-item>
      </div>
    </goa-radio-item>
    <goa-radio-item value="text" label="Text message">
      <div slot="reveal">
        <goa-form-item version="2" label="Mobile phone number">
          <goa-input version="2" name="mobile"></goa-input>
        </goa-form-item>
      </div>
    </goa-radio-item>
  </goa-radio-group>
</goa-form-item>

<goa-form-item version="2" label="How would you like to be contacted?" mt="xl">
  <goa-checkbox-list name="contactMethods">
    <goa-checkbox version="2" name="emailContact" text="Email">
      <div slot="reveal">
        <goa-form-item version="2" label="Email address">
          <goa-input version="2" name="emailInput"></goa-input>
        </goa-form-item>
      </div>
    </goa-checkbox>
    <goa-checkbox version="2" name="phoneContact" text="Phone">
      <div slot="reveal">
        <goa-form-item version="2" label="Phone number">
          <goa-input version="2" name="phoneInput"></goa-input>
        </goa-form-item>
      </div>
    </goa-checkbox>
    <goa-checkbox version="2" name="textContact" text="Text message">
      <div slot="reveal">
        <goa-form-item version="2" label="Mobile phone number">
          <goa-input version="2" name="mobileInput"></goa-input>
        </goa-form-item>
      </div>
    </goa-checkbox>
  </goa-checkbox-list>
</goa-form-item>
const [search, setSearch] = useState("");

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

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      search: [""],
    });
  }

  onClick(): void {
    console.log("search:", this.form.controls["search"].value);
  }
<form [formGroup]="form">
  <goabx-form-item>
    <goab-block gap="xs" direction="row">
      <goabx-input
        type="search"
        name="search"
        formControlName="search"
        leadingIcon="search">
      </goabx-input>
      <goabx-button type="primary" (onClick)="onClick()">Search</goabx-button>
    </goab-block>
  </goabx-form-item>
</form>
const searchInput = document.getElementById("search-input");
const searchBtn = document.getElementById("search-btn");
let searchValue = "";

searchInput.addEventListener("_change", (e) => {
  searchValue = e.detail.value;
});

searchBtn.addEventListener("_click", () => {
  console.log("search:", searchValue);
});
<form>
  <goa-form-item version="2">
    <goa-block gap="xs" direction="row">
      <goa-input version="2"
        id="search-input"
        type="search"
        name="search"
        leadingicon="search">
      </goa-input>
      <goa-button version="2" id="search-btn" type="primary">Search</goa-button>
    </goa-block>
  </goa-form-item>
</form>

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

<GoabxLink leadingIcon="arrow-back" size="small" mb="none">
        Back
      </GoabxLink>

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

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

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

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

      <GoabxButton type="submit" mt="2xl">
        Save and continue
      </GoabxButton>
a.back-link::before {
  content: "";
  display: inline-block;
  width: 42px;
  height: 24px;
  vertical-align: middle;
  background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 2 22 22" fill="none" stroke="%230070C4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>') center center no-repeat;
}

a.back-link:visited::before,
a.back-link:hover::before {
  background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 2 22 22" fill="none" stroke="%23004f84" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>') center center no-repeat;
}

a.back-link {
  margin-top: var(--goa-space-m);
}

h3.section-title {
  margin-bottom: 0;
  color: var(--goa-color-text-secondary);
}

a.back-link + h3 {
  margin-top: var(--goa-space-2xl);
}

h3.section-title + h2 {
  margin-top: var(--goa-space-xs);
}
onChange(event: Event): void {
    const detail = (event as CustomEvent).detail;
    console.log("Value:", detail.value);
  }
<goabx-link leadingIcon="arrow-back" size="small" mb="none">
  Back
</goabx-link>

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

<goabx-form-item label="What is your name?">
  <goabx-input (onChange)="onChange($event)" name="name" ariaLabel="what is your name?" width="50ch"></goabx-input>
</goabx-form-item>

<goabx-form-item label="What is your phone number?" mt="l">
  <goabx-input (onChange)="onChange($event)" name="phone-number" ariaLabel="what is your phone number?">
    <div slot="leadingContent">+1</div>
  </goabx-input>
</goabx-form-item>

<goabx-form-item label="What is your home postal code?" mt="l">
  <goabx-input (onChange)="onChange($event)" name="postal-code" width="14ch" ariaLabel="what is your home postal code"></goabx-input>
</goabx-form-item>

<goabx-button type="submit" mt="2xl">
  Save and continue
</goabx-button>
document.querySelectorAll('goa-input').forEach(input => {
  input.addEventListener('_change', (e) => {
    console.log(`${e.target.name}:`, e.detail.value);
  });
});
<goa-link leadingicon="arrow-back" size="small" mb="none">
  Back
</goa-link>

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

<goa-form-item version="2" label="What is your name?">
  <goa-input version="2" name="name" aria-label="what is your name?" width="50ch"></goa-input>
</goa-form-item>

<goa-form-item version="2" label="What is your phone number?" mt="l">
  <goa-input version="2" name="phone-number" aria-label="what is your phone number?">
    <div slot="leadingContent">+1</div>
  </goa-input>
</goa-form-item>

<goa-form-item version="2" label="What is your home postal code?" mt="l">
  <goa-input version="2" name="postal-code" width="14ch" aria-label="what is your home postal code"></goa-input>
</goa-form-item>

<goa-button version="2" type="submit" mt="2xl">
  Save and continue
</goa-button>

Slotted error text in a form item

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

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

  const errorMessage = (
    <>
      <i>This is </i> slotted <b>error text</b>.
    </>
  );
<GoabxFormItem label="First name" error={errorMessage}>
      <GoabxInput onChange={onChange} value={value} name="item" error={true} />
    </GoabxFormItem>
form: FormGroup;

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      item: [""],
    });
  }
<goabx-form-item label="First name" [formGroup]="form">
  <goabx-input name="item" formControlName="item" [error]="true"></goabx-input>
  <goabx-form-item-slot slot="error">
    <span>This is </span>
    <i>slotted </i>
    <b>error text.</b>
  </goabx-form-item-slot>
</goabx-form-item>
const input = document.querySelector('goa-input[name="item"]');
input.addEventListener("_change", (e) => {
  console.log("Value changed:", e.detail.value);
});
<goa-form-item version="2" label="First name">
  <goa-input version="2" name="item" error="true"></goa-input>
  <div slot="error">
    <span>This is </span>
    <i>slotted </i>
    <b>error text.</b>
  </div>
</goa-form-item>

Slotted helper text in a form item

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

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

  const helpText = (
    <>
      <i>This is </i> slotted <b>help text</b>.
    </>
  );
<GoabxFormItem label="First name" helpText={helpText}>
      <GoabxInput onChange={onChange} value={value} name="item" />
    </GoabxFormItem>
form: FormGroup;

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      item: [""],
    });
  }
<goabx-form-item label="First name" [formGroup]="form">
  <goabx-input name="item" formControlName="item"></goabx-input>
  <goabx-form-item-slot slot="helptext">
    <span>This is </span>
    <i>slotted </i>
    <b>help text</b>.
  </goabx-form-item-slot>
</goabx-form-item>
const input = document.querySelector('goa-input[name="item"]');
input.addEventListener("_change", (e) => {
  console.log("Value changed:", e.detail.value);
});
<goa-form-item version="2" label="First name">
  <goa-input version="2" name="item"></goa-input>
  <div slot="helptext">
    <span>This is </span>
    <i>slotted </i>
    <b>help text</b>.
  </div>
</goa-form-item>

Type to create a new filter

const [typedChips, setTypedChips] = useState<string[]>([]);
  const [inputValue, setInputValue] = useState("");
<GoabxFormItem label="Type to create a chip" mb="m">
        <GoabxInput
          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%"
        />
      </GoabxFormItem>
      <div>
        {typedChips.map((chip, index) => (
          <GoabxFilterChip
            key={index}
            content={chip}
            mb="xs"
            mr="xs"
            onClick={() => setTypedChips(typedChips.filter((c) => c !== chip))}
          />
        ))}
      </div>
typedChips: string[] = [];
  inputValue = "";

  handleInputChange(detail: GoabInputOnChangeDetail): void {
    const newValue = detail.value.trim();
    this.inputValue = newValue;
  }

  handleInputKeyPress(detail: GoabInputOnKeyPressDetail): void {
    const newValue = detail.value.trim();
    if (detail.key === "Enter" && newValue !== "") {
      this.addChip();
    } else if (!this.inputValue && this.typedChips.length > 0 && detail.key === "Backspace") {
      this.typedChips.pop();
    }
  }

  addChip(): void {
    if (this.inputValue.trim()) {
      this.typedChips.push(this.inputValue.trim());
      this.inputValue = "";
    }
  }

  removeTypedChip(chip: string): void {
    this.typedChips = this.typedChips.filter((c) => c !== chip);
  }
<goabx-form-item label="Type to create a chip" mb="m">
  <goabx-input
    [value]="inputValue"
    (onChange)="handleInputChange($event)"
    (onKeyPress)="handleInputKeyPress($event)"
    width="100%"
  >
  </goabx-input>
</goabx-form-item>
@if (typedChips.length > 0) {
<div>
  @for (typedChip of typedChips; track typedChip) {
  <goabx-filter-chip
    [content]="typedChip"
    mb="xs"
    mr="xs"
    (onClick)="removeTypedChip(typedChip)"
  >
  </goabx-filter-chip>
  }
</div>
}
const typedChips = [];
const input = document.getElementById("chip-input");
const addBtn = document.getElementById("add-btn");
const container = document.getElementById("chips-container");
let currentValue = "";

function renderChips() {
  container.innerHTML = "";
  typedChips.forEach((chip) => {
    const chipEl = document.createElement("goa-filter-chip");
    chipEl.setAttribute("version", "2");
    chipEl.setAttribute("content", chip);
    chipEl.setAttribute("mb", "xs");
    chipEl.setAttribute("mr", "xs");
    chipEl.addEventListener("_click", () => removeChip(chip));
    container.appendChild(chipEl);
  });
}

function addChip() {
  if (currentValue.trim()) {
    typedChips.push(currentValue.trim());
    currentValue = "";
    input.setAttribute("value", "");
    renderChips();
  }
}

function removeChip(chip) {
  const index = typedChips.indexOf(chip);
  if (index > -1) {
    typedChips.splice(index, 1);
    renderChips();
  }
}

input.addEventListener("_change", (e) => {
  currentValue = e.detail.value || "";
});

addBtn.addEventListener("_click", () => {
  addChip();
});
<goa-form-item version="2" label="Type to create a chip" mb="m">
  <goa-block gap="xs" direction="row">
    <div style="flex: 1">
      <goa-input version="2" id="chip-input" width="100%"></goa-input>
    </div>
    <goa-button version="2" id="add-btn" type="secondary">Add</goa-button>
  </goa-block>
</goa-form-item>
<div id="chips-container"></div>

States

Submit

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 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.

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.

Types

Use the appropriate input type for your context to give a better experience.
Use a text area to input content longer than a single line, such as descriptions, comments, or feedback.

Content

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 use placeholder text as a label

Sizing

Known input length: Use fixed-width inputs for content with a specific length, such as postal code (7 characters) or year (4 characters).

Unknown input length: If you don’t know how many characters the user will need (like their name), make your text input 100% of the container.

Size text inputs based on the expected content length to help users understand what information is needed.

Capture text input from users in forms.

When to use

Use Input when you need users to enter:

  • Short text answers (names, email addresses)
  • Numbers (phone numbers, quantities)
  • Dates and times
  • Search queries

When not to use

Don’t use Input when:

  • Users need to enter multiple lines of text (use Textarea)
  • Users need to select from predefined options (use Dropdown or Radio)
  • The input requires complex formatting (consider specialized components)

Types

Input supports multiple HTML input types:

  • text - General text entry (default)
  • email - Email addresses with validation
  • password - Masked password entry
  • number - Numeric values with stepper
  • date - Date picker
  • tel - Phone numbers
  • search - Search with clear button

States

  • Default - Normal interactive state
  • Focused - When the input has keyboard focus
  • Error - When validation fails
  • Disabled - When input is not available
  • Read-only - When showing value without editing
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.