Table

A set of structured data that is easy for a user to scan, examine, and compare.

Props

width
string
Width of the table. By default it will fit the enclosed content.
stickyheader
boolean
When true, the table header sticks to the top when scrolling.
Defaults to false.
striped
boolean
When true, alternates row background colors for improved readability.
Defaults to false.
variant
normal | relaxed
A relaxed variant of the table with more vertical padding for the cells.
Defaults to normal.
testId
string
Sets a data-testid attribute for automated testing.
sortMode
single | multi
Sort mode: "single" allows one column, "multi" allows up to 2 columns.
Defaults to single.
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

onMultisort
(event: Event) => void
_multisort
CustomEvent
onSort
(event: Event) => void
_sort
CustomEvent
Examples

Display numbers in a table so they can be scanned easily

<GoabTable width="100%">
  <thead>
    <tr>
      <th>First name</th>
      <th>Last name</th>
      <th className="goa-table-number-header">ID Number</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Sarah</td>
      <td>Johnson</td>
      <td className="goa-table-number-column">54</td>
    </tr>
    <tr>
      <td>Michael</td>
      <td>Chen</td>
      <td className="goa-table-number-column">4567</td>
    </tr>
    <tr>
      <td>Emily</td>
      <td>Williams</td>
      <td className="goa-table-number-column">892</td>
    </tr>
    <tr>
      <td>David</td>
      <td>Brown</td>
      <td className="goa-table-number-column">12345</td>
    </tr>
    <tr>
      <td>Jennifer</td>
      <td>Martinez</td>
      <td className="goa-table-number-column">7</td>
    </tr>
  </tbody>
</GoabTable>
// No logic required for this static example
<goab-table width="100%">
  <thead>
    <tr>
      <th>First name</th>
      <th>Last name</th>
      <th class="goa-table-number-header">ID Number</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Sarah</td>
      <td>Johnson</td>
      <td class="goa-table-number-column">54</td>
    </tr>
    <tr>
      <td>Michael</td>
      <td>Chen</td>
      <td class="goa-table-number-column">4567</td>
    </tr>
    <tr>
      <td>Emily</td>
      <td>Williams</td>
      <td class="goa-table-number-column">892</td>
    </tr>
    <tr>
      <td>David</td>
      <td>Brown</td>
      <td class="goa-table-number-column">12345</td>
    </tr>
    <tr>
      <td>Jennifer</td>
      <td>Martinez</td>
      <td class="goa-table-number-column">7</td>
    </tr>
  </tbody>
</goab-table>
<goa-table version="2" width="100%">
  <table style="width: 100%">
    <thead>
      <tr>
        <th>First name</th>
        <th>Last name</th>
        <th class="goa-table-number-header">ID Number</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Sarah</td>
        <td>Johnson</td>
        <td class="goa-table-number-column">54</td>
      </tr>
      <tr>
        <td>Michael</td>
        <td>Chen</td>
        <td class="goa-table-number-column">4567</td>
      </tr>
      <tr>
        <td>Emily</td>
        <td>Williams</td>
        <td class="goa-table-number-column">892</td>
      </tr>
      <tr>
        <td>David</td>
        <td>Brown</td>
        <td class="goa-table-number-column">12345</td>
      </tr>
      <tr>
        <td>Jennifer</td>
        <td>Martinez</td>
        <td class="goa-table-number-column">7</td>
      </tr>
    </tbody>
  </table>
</goa-table>

Display user information

const handleAddToCalendar = () => {
  console.log("Add to calendar clicked");
};
<GoabContainer>
  <GoabText tag="span" size="body-m" color="secondary" mt="none" mb="none">
    Housing Advisor
  </GoabText>
  <GoabText size="heading-m" mt="none" mb="s">
    Tracy Hero
  </GoabText>
  <GoabBlock direction="row" gap="s">
    <GoabBlock direction="column" gap="m">
      <GoabText tag="span" size="heading-xs" mt="none" mb="none">
        Email
      </GoabText>
      <GoabText tag="span" size="heading-xs" mt="none" mb="none">
        Phone
      </GoabText>
    </GoabBlock>
    <GoabBlock direction="column" gap="m">
      <GoabText tag="span" size="body-m" mt="none" mb="none">
        tracyhero@email.com
      </GoabText>
      <GoabText tag="span" size="body-m" mt="none" mb="none">
        283-203-4921
      </GoabText>
    </GoabBlock>
  </GoabBlock>
</GoabContainer>

<GoabContainer
  type="non-interactive"
  accent="thick"
  heading="Upcoming important due dates"
  actions={
    <GoabButton
      type="tertiary"
      size="compact"
      leadingIcon="calendar"
      onClick={handleAddToCalendar}
    >
      Add to calendar
    </GoabButton>
  }
>
  <GoabTable width="100%" striped>
    <tbody>
      <tr>
        <td>Business plan submission</td>
        <td style={{ textAlign: "right" }}>June 30, 2024</td>
      </tr>
      <tr>
        <td>Annual review</td>
        <td style={{ textAlign: "right" }}>October 3, 2024</td>
      </tr>
      <tr>
        <td>Application submission</td>
        <td style={{ textAlign: "right" }}>December 20, 2024</td>
      </tr>
      <tr>
        <td>Application review</td>
        <td style={{ textAlign: "right" }}>January 3, 2025</td>
      </tr>
    </tbody>
  </GoabTable>
</GoabContainer>
onAddToCalendar(): void {
  console.log("Add to calendar clicked");
}
<goab-container>
  <goab-text tag="span" size="body-m" color="secondary" mt="none" mb="none"
    >Housing Advisor</goab-text
  >
  <goab-text size="heading-m" mt="none" mb="s">Tracy Hero</goab-text>
  <goab-block direction="row" gap="s">
    <goab-block direction="column" gap="m">
      <goab-text tag="span" size="heading-xs" mt="none" mb="none">Email</goab-text>
      <goab-text tag="span" size="heading-xs" mt="none" mb="none">Phone</goab-text>
    </goab-block>
    <goab-block direction="column" gap="m">
      <goab-text tag="span" size="body-m" mt="none" mb="none"
        >tracyhero&#64;email.com</goab-text
      >
      <goab-text tag="span" size="body-m" mt="none" mb="none">283-203-4921</goab-text>
    </goab-block>
  </goab-block>
</goab-container>

<goab-container type="non-interactive" accent="thick">
  <div slot="title">Upcoming important due dates</div>
  <div slot="actions">
    <goab-button
      type="tertiary"
      size="compact"
      leadingIcon="calendar"
      (onClick)="onAddToCalendar()"
    >
      Add to calendar
    </goab-button>
  </div>
  <goab-table width="100%" [striped]="true">
    <tbody>
      <tr>
        <td>Business plan submission</td>
        <td style="text-align: right">June 30, 2024</td>
      </tr>
      <tr>
        <td>Annual review</td>
        <td style="text-align: right">October 3, 2024</td>
      </tr>
      <tr>
        <td>Application submission</td>
        <td style="text-align: right">December 20, 2024</td>
      </tr>
      <tr>
        <td>Application review</td>
        <td style="text-align: right">January 3, 2025</td>
      </tr>
    </tbody>
  </goab-table>
</goab-container>
const calendarBtn = document.getElementById("calendar-btn");
calendarBtn.addEventListener("_click", () => {
  console.log("Add to calendar clicked");
});
<goa-container>
  <goa-text as="span" size="body-m" color="secondary" mt="none" mb="none"
    >Housing Advisor</goa-text
  >
  <goa-text size="heading-m" mt="none" mb="s">Tracy Hero</goa-text>
  <goa-block direction="row" gap="s">
    <goa-block direction="column" gap="m">
      <goa-text as="span" size="heading-xs" mt="none" mb="none">Email</goa-text>
      <goa-text as="span" size="heading-xs" mt="none" mb="none">Phone</goa-text>
    </goa-block>
    <goa-block direction="column" gap="m">
      <goa-text as="span" size="body-m" mt="none" mb="none">tracyhero@email.com</goa-text>
      <goa-text as="span" size="body-m" mt="none" mb="none">283-203-4921</goa-text>
    </goa-block>
  </goa-block>
</goa-container>

<goa-container type="non-interactive" accent="thick">
  <div slot="title">Upcoming important due dates</div>
  <div slot="actions">
    <goa-button
      version="2"
      id="calendar-btn"
      type="tertiary"
      size="compact"
      leadingicon="calendar"
    >
      Add to calendar
    </goa-button>
  </div>
  <goa-table version="2" width="100%" striped="true">
    <table style="width: 100%">
      <tbody>
        <tr>
          <td>Business plan submission</td>
          <td style="text-align: right">June 30, 2024</td>
        </tr>
        <tr>
          <td>Annual review</td>
          <td style="text-align: right">October 3, 2024</td>
        </tr>
        <tr>
          <td>Application submission</td>
          <td style="text-align: right">December 20, 2024</td>
        </tr>
        <tr>
          <td>Application review</td>
          <td style="text-align: right">January 3, 2025</td>
        </tr>
      </tbody>
    </table>
  </goa-table>
</goa-container>

Filter a list using a push drawer

const [open, setOpen] = useState(false);
<div style={{ display: "flex", minHeight: "480px" }}>
  <div style={{ flex: 1, minWidth: 0 }}>
    <div
      style={{
        display: "flex",
        alignItems: "center",
        gap: "1rem",
        marginBottom: "1rem",
      }}
    >
      <h3 style={{ flex: 1, margin: 0 }}>All cases</h3>
      {!open && (
        <GoabButton
          type="secondary"
          size="compact"
          leadingIcon="filter"
          onClick={() => setOpen(true)}
        >
          Filters
        </GoabButton>
      )}
    </div>

    <GoabTable width="100%">
      <table width="100%">
        <thead>
          <tr>
            <th>Status</th>
            <th>Name</th>
            <th>File number</th>
            <th>Act</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>
              <GoabBadge type="success" content="Completed" />
            </td>
            <td>Gilbert Barton</td>
            <td>24567-9876</td>
            <td>Traffic safety act</td>
          </tr>
          <tr>
            <td>
              <GoabBadge type="information" content="New" />
            </td>
            <td>Brynn Hurley</td>
            <td>98765-3456</td>
            <td>Trespass to premises act</td>
          </tr>
          <tr>
            <td>
              <GoabBadge type="default" content="In review" />
            </td>
            <td>Marco Silva</td>
            <td>34521-7890</td>
            <td>Gaming, liquor, and cannabis act</td>
          </tr>
          <tr>
            <td>
              <GoabBadge type="success" content="Completed" />
            </td>
            <td>Dana Chen</td>
            <td>55123-4567</td>
            <td>Traffic safety act</td>
          </tr>
          <tr>
            <td>
              <GoabBadge type="information" content="New" />
            </td>
            <td>Amira Hassan</td>
            <td>67890-1234</td>
            <td>Trespass to premises act</td>
          </tr>
        </tbody>
      </table>
    </GoabTable>
  </div>

  <GoabPushDrawer
    heading="Filters"
    width="260px"
    open={open}
    onClose={() => setOpen(false)}
  >
    <GoabFormItem label="Act">
      <GoabCheckboxList name="act" onChange={() => {}}>
        <GoabCheckbox name="traffic" text="Traffic safety act" size="compact" />
        <GoabCheckbox
          name="gaming"
          text="Gaming, liquor, and cannabis act"
          size="compact"
        />
        <GoabCheckbox
          name="trespass"
          text="Trespass to premises act"
          size="compact"
        />
      </GoabCheckboxList>
    </GoabFormItem>
    <GoabFormItem label="Status" mt="l">
      <GoabDropdown name="status" onChange={() => {}} value="" size="compact">
        <GoabDropdownItem value="" label="All statuses" />
        <GoabDropdownItem value="new" label="New" />
        <GoabDropdownItem value="in-review" label="In review" />
        <GoabDropdownItem value="completed" label="Completed" />
      </GoabDropdown>
    </GoabFormItem>
  </GoabPushDrawer>
</div>
open = false;

openFilters(): void {
  this.open = true;
}

closeFilters(): void {
  this.open = false;
}

onCheckboxChange(event: GoabCheckboxOnChangeDetail): void {
  console.log(event);
}

onDropdownChange(event: GoabDropdownOnChangeDetail): void {
  console.log(event);
}
<div style="display: flex; min-height: 480px">
  <div style="flex: 1; min-width: 0">
    <div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1rem">
      <h3 style="flex: 1; margin: 0">All cases</h3>
      @if (!open) {
      <goab-button
        type="secondary"
        size="compact"
        leadingIcon="filter"
        (onClick)="openFilters()"
        >Filters</goab-button
      >
      }
    </div>

    <goab-table width="100%">
      <table width="100%">
        <thead>
          <tr>
            <th>Status</th>
            <th>Name</th>
            <th>File number</th>
            <th>Act</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td><goab-badge type="success" content="Completed"></goab-badge></td>
            <td>Gilbert Barton</td>
            <td>24567-9876</td>
            <td>Traffic safety act</td>
          </tr>
          <tr>
            <td><goab-badge type="information" content="New"></goab-badge></td>
            <td>Brynn Hurley</td>
            <td>98765-3456</td>
            <td>Trespass to premises act</td>
          </tr>
          <tr>
            <td><goab-badge type="default" content="In review"></goab-badge></td>
            <td>Marco Silva</td>
            <td>34521-7890</td>
            <td>Gaming, liquor, and cannabis act</td>
          </tr>
          <tr>
            <td><goab-badge type="success" content="Completed"></goab-badge></td>
            <td>Dana Chen</td>
            <td>55123-4567</td>
            <td>Traffic safety act</td>
          </tr>
          <tr>
            <td><goab-badge type="information" content="New"></goab-badge></td>
            <td>Amira Hassan</td>
            <td>67890-1234</td>
            <td>Trespass to premises act</td>
          </tr>
        </tbody>
      </table>
    </goab-table>
  </div>

  <goab-push-drawer
    heading="Filters"
    width="260px"
    [open]="open"
    (onClose)="closeFilters()"
  >
    <goab-form-item label="Act">
      <goab-checkbox-list name="act" (onChange)="onCheckboxChange($event)">
        <goab-checkbox
          name="traffic"
          text="Traffic safety act"
          size="compact"
        ></goab-checkbox>
        <goab-checkbox
          name="gaming"
          text="Gaming, liquor, and cannabis act"
          size="compact"
        ></goab-checkbox>
        <goab-checkbox
          name="trespass"
          text="Trespass to premises act"
          size="compact"
        ></goab-checkbox>
      </goab-checkbox-list>
    </goab-form-item>
    <goab-form-item label="Status" mt="l">
      <goab-dropdown
        name="status"
        (onChange)="onDropdownChange($event)"
        value=""
        size="compact"
      >
        <goab-dropdown-item value="" label="All statuses"></goab-dropdown-item>
        <goab-dropdown-item value="new" label="New"></goab-dropdown-item>
        <goab-dropdown-item value="in-review" label="In review"></goab-dropdown-item>
        <goab-dropdown-item value="completed" label="Completed"></goab-dropdown-item>
      </goab-dropdown>
    </goab-form-item>
  </goab-push-drawer>
</div>
const filtersDrawer = document.getElementById("filters-drawer");
const openFiltersBtn = document.getElementById("open-filters-btn");

openFiltersBtn.addEventListener("_click", () => {
  filtersDrawer.setAttribute("open", "true");
  openFiltersBtn.style.display = "none";
});

filtersDrawer.addEventListener("_close", () => {
  filtersDrawer.removeAttribute("open");
  openFiltersBtn.style.display = "";
});
<div style="display: flex; min-height: 480px">
  <div style="flex: 1; min-width: 0">
    <div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1rem">
      <h3 style="flex: 1; margin: 0">All cases</h3>
      <goa-button
        version="2"
        id="open-filters-btn"
        type="secondary"
        size="compact"
        leadingicon="filter"
        >Filters</goa-button
      >
    </div>

    <goa-table version="2" width="100%">
      <table width="100%">
        <thead>
          <tr>
            <th>Status</th>
            <th>Name</th>
            <th>File number</th>
            <th>Act</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>
              <goa-badge version="2" type="success" content="Completed"></goa-badge>
            </td>
            <td>Gilbert Barton</td>
            <td>24567-9876</td>
            <td>Traffic safety act</td>
          </tr>
          <tr>
            <td><goa-badge version="2" type="information" content="New"></goa-badge></td>
            <td>Brynn Hurley</td>
            <td>98765-3456</td>
            <td>Trespass to premises act</td>
          </tr>
          <tr>
            <td>
              <goa-badge version="2" type="default" content="In review"></goa-badge>
            </td>
            <td>Marco Silva</td>
            <td>34521-7890</td>
            <td>Gaming, liquor, and cannabis act</td>
          </tr>
          <tr>
            <td>
              <goa-badge version="2" type="success" content="Completed"></goa-badge>
            </td>
            <td>Dana Chen</td>
            <td>55123-4567</td>
            <td>Traffic safety act</td>
          </tr>
          <tr>
            <td><goa-badge version="2" type="information" content="New"></goa-badge></td>
            <td>Amira Hassan</td>
            <td>67890-1234</td>
            <td>Trespass to premises act</td>
          </tr>
        </tbody>
      </table>
    </goa-table>
  </div>

  <goa-push-drawer version="2" id="filters-drawer" heading="Filters" width="260px">
    <goa-form-item version="2" label="Act">
      <goa-checkbox-list name="act">
        <goa-checkbox
          version="2"
          name="traffic"
          text="Traffic safety act"
          size="compact"
        ></goa-checkbox>
        <goa-checkbox
          version="2"
          name="gaming"
          text="Gaming, liquor, and cannabis act"
          size="compact"
        ></goa-checkbox>
        <goa-checkbox
          version="2"
          name="trespass"
          text="Trespass to premises act"
          size="compact"
        ></goa-checkbox>
      </goa-checkbox-list>
    </goa-form-item>
    <goa-form-item version="2" label="Status" mt="l">
      <goa-dropdown version="2" name="status" size="compact">
        <goa-dropdown-item value="" label="All statuses"></goa-dropdown-item>
        <goa-dropdown-item value="new" label="New"></goa-dropdown-item>
        <goa-dropdown-item value="in-review" label="In review"></goa-dropdown-item>
        <goa-dropdown-item value="completed" label="Completed"></goa-dropdown-item>
      </goa-dropdown>
    </goa-form-item>
  </goa-push-drawer>
</div>

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

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

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

{dataFiltered.length === 0 && data.length > 0 && (
  <GoabBlock mt="l" mb="l">
    No results found
  </GoabBlock>
)}
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()),
  );
}
<goab-form-item id="filterChipInput" [error]="inputError" mb="m">
  <goab-block gap="xs" direction="row" alignment="start" width="100%">
    <div style="flex: 1">
      <goab-input
        name="filterChipInput"
        aria-labelledby="filterChipInput"
        [value]="inputValue"
        leadingIcon="search"
        width="100%"
        (onChange)="handleInputChange($event)"
        (onKeyPress)="handleInputKeyPress($event)"
      >
      </goab-input>
    </div>
    <goab-button type="secondary" (onClick)="applyFilter()" leadingIcon="filter">
      Filter
    </goab-button>
  </goab-block>
</goab-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) {
  <goab-filter-chip
    [content]="typedChip"
    mb="xs"
    mr="xs"
    (onClick)="removeTypedChip(typedChip)"
  >
  </goab-filter-chip>
  }
  <goab-button type="tertiary" size="compact" mb="xs" (onClick)="removeAllTypedChips()">
    Clear all
  </goab-button>
</ng-container>
}

<goab-table width="100%">
  <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>
        <goab-badge
          [type]="item.status.type"
          [content]="item.status.text"
          [icon]="false"
        ></goab-badge>
      </td>
      <td>{{ item.name }}</td>
      <td class="goa-table-number-column">{{ item.id }}</td>
    </tr>
    }
  </tbody>
</goab-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 as="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>

Review page

      <GoabText size="heading-l" mt="none" mb="none">
        Review your answers
      </GoabText>
      <GoabText size="heading-s" color="secondary" mt="l" mb="none">
        Your situation
      </GoabText>
      <GoabTable mt="l">
        <tbody>
          <tr>
            <td>
              <strong>
                What was your (the applicant's) relationship to the deceased?
              </strong>
            </td>
            <td>Other</td>
            <td>
              <GoabLink>Change</GoabLink>
            </td>
          </tr>
          <tr>
            <td>
              <strong>My relationship to the deceased was</strong>
            </td>
            <td>Manager</td>
            <td>
              <GoabLink>Change</GoabLink>
            </td>
          </tr>
          <tr>
            <td>
              <strong>
                Was the deceased part of a household that was receiving Assured Income for
                the Severely Handicapped (AISH) or Income Support?
              </strong>
            </td>
            <td>No</td>
            <td>
              <GoabLink>Change</GoabLink>
            </td>
          </tr>
          <tr>
            <td>
              <strong>Was the deceased a minor?</strong>
            </td>
            <td>No</td>
            <td>
              <GoabLink>Change</GoabLink>
            </td>
          </tr>
          <tr>
            <td>
              <strong>What was the deceased's marital status at time of death?</strong>
            </td>
            <td>Married</td>
            <td>
              <GoabLink>Change</GoabLink>
            </td>
          </tr>
          <tr>
            <td>
              <strong>Did the deceased have any dependents?</strong>
            </td>
            <td>No</td>
            <td>
              <GoabLink>Change</GoabLink>
            </td>
          </tr>
          <tr>
            <td>
              <strong>Was the deceased a sponsored immigrant?</strong>
            </td>
            <td>Yes</td>
            <td>
              <GoabLink>Change</GoabLink>
            </td>
          </tr>
        </tbody>
      </GoabTable>
      <GoabButtonGroup alignment="start" mt="2xl">
        <GoabButton type="primary">Confirm and continue</GoabButton>
        <GoabButton type="tertiary">Back to application overview</GoabButton>
      </GoabButtonGroup>
  );
}
onChangeClick(): void {
  console.log("Change clicked");
}

onConfirmClick(): void {
  console.log("Confirm clicked");
}

onBackClick(): void {
  console.log("Back clicked");
}
<goab-text size="heading-l" mt="none" mb="none">Review your answers</goab-text>
<goab-text size="heading-s" color="secondary" mt="l" mb="none">Your situation</goab-text>
<goab-table mt="l">
  <tbody>
    <tr>
      <td>
        <strong>What was your (the applicant's) relationship to the deceased?</strong>
      </td>
      <td>Other</td>
      <td><goab-link>Change</goab-link></td>
    </tr>
    <tr>
      <td><strong>My relationship to the deceased was</strong></td>
      <td>Manager</td>
      <td><goab-link>Change</goab-link></td>
    </tr>
    <tr>
      <td>
        <strong
          >Was the deceased part of a household that was receiving Assured Income for the
          Severely Handicapped (AISH) or Income Support?</strong
        >
      </td>
      <td>No</td>
      <td><goab-link>Change</goab-link></td>
    </tr>
    <tr>
      <td><strong>Was the deceased a minor?</strong></td>
      <td>No</td>
      <td><goab-link>Change</goab-link></td>
    </tr>
    <tr>
      <td><strong>What was the deceased's marital status at time of death?</strong></td>
      <td>Married</td>
      <td><goab-link>Change</goab-link></td>
    </tr>
    <tr>
      <td><strong>Did the deceased have any dependents?</strong></td>
      <td>No</td>
      <td><goab-link>Change</goab-link></td>
    </tr>
    <tr>
      <td><strong>Was the deceased a sponsored immigrant?</strong></td>
      <td>Yes</td>
      <td><goab-link>Change</goab-link></td>
    </tr>
  </tbody>
</goab-table>
<goab-button-group alignment="start" mt="2xl">
  <goab-button type="primary" (onClick)="onConfirmClick()"
    >Confirm and continue</goab-button
  >
  <goab-button type="tertiary" (onClick)="onBackClick()"
    >Back to application overview</goab-button
  >
</goab-button-group>
document.getElementById("confirm-btn").addEventListener("_click", () => {
  console.log("Confirm clicked");
});
<goa-text size="heading-l" mt="none" mb="none">Review your answers</goa-text>
<goa-text size="heading-s" color="secondary" mt="l" mb="none">Your situation</goa-text>
<goa-table version="2" mt="l">
  <table>
    <tbody>
      <tr>
        <td>
          <strong>What was your (the applicant's) relationship to the deceased?</strong>
        </td>
        <td>Other</td>
        <td><goa-link>Change</goa-link></td>
      </tr>
      <tr>
        <td><strong>My relationship to the deceased was</strong></td>
        <td>Manager</td>
        <td><goa-link>Change</goa-link></td>
      </tr>
      <tr>
        <td>
          <strong
            >Was the deceased part of a household that was receiving Assured Income for
            the Severely Handicapped (AISH) or Income Support?</strong
          >
        </td>
        <td>No</td>
        <td><goa-link>Change</goa-link></td>
      </tr>
      <tr>
        <td><strong>Was the deceased a minor?</strong></td>
        <td>No</td>
        <td><goa-link>Change</goa-link></td>
      </tr>
      <tr>
        <td><strong>What was the deceased's marital status at time of death?</strong></td>
        <td>Married</td>
        <td><goa-link>Change</goa-link></td>
      </tr>
      <tr>
        <td><strong>Did the deceased have any dependents?</strong></td>
        <td>No</td>
        <td><goa-link>Change</goa-link></td>
      </tr>
      <tr>
        <td><strong>Was the deceased a sponsored immigrant?</strong></td>
        <td>Yes</td>
        <td><goa-link>Change</goa-link></td>
      </tr>
    </tbody>
  </table>
</goa-table>
<goa-button-group alignment="start" mt="2xl">
  <goa-button version="2" type="primary" id="confirm-btn"
    >Confirm and continue</goa-button
  >
  <goa-button version="2" type="tertiary" id="back-btn"
    >Back to application overview</goa-button
  >
</goa-button-group>

Set a specific tab to be active

const review = [0, 1, 2, 3];
const complete = [0, 1];
<GoabTabs initialTab={2}>
  <GoabTab heading="All">
    <GoabTable width="100%">
      <thead>
        <tr>
          <th>Status</th>
          <th>Text</th>
          <th className="goa-table-number-header">Number</th>
          <th style={{ width: "1%", whiteSpace: "nowrap" }}>Action</th>
        </tr>
      </thead>
      <tbody>
        {review.map((i) => (
          <tr key={`review-${i}`}>
            <td>
              <GoabBadge type="important" content="Review pending" />
            </td>
            <td>Lorem Ipsum</td>
            <td className="goa-table-number-column">1234567890</td>
            <td>
              <GoabButton type="tertiary" size="compact">
                Action
              </GoabButton>
            </td>
          </tr>
        ))}
        {complete.map((i) => (
          <tr key={`complete-${i}`}>
            <td>
              <GoabBadge type="information" content="Complete" />
            </td>
            <td>Lorem Ipsum</td>
            <td className="goa-table-number-column">1234567890</td>
            <td>
              <GoabButton type="tertiary" size="compact">
                Action
              </GoabButton>
            </td>
          </tr>
        ))}
      </tbody>
    </GoabTable>
  </GoabTab>
  <GoabTab
    heading={
        Review pending
        <GoabBadge type="important" content="4" icon={false} />
    }
  >
    <GoabTable width="100%">
      <thead>
        <tr>
          <th>Status</th>
          <th>Text</th>
          <th className="goa-table-number-header">Number</th>
          <th style={{ width: "1%", whiteSpace: "nowrap" }}>Action</th>
        </tr>
      </thead>
      <tbody>
        {review.map((i) => (
          <tr key={i}>
            <td>
              <GoabBadge type="important" content="Review pending" />
            </td>
            <td>Lorem Ipsum</td>
            <td className="goa-table-number-column">1234567890</td>
            <td>
              <GoabButton type="tertiary" size="compact">
                Action
              </GoabButton>
            </td>
          </tr>
        ))}
      </tbody>
    </GoabTable>
  </GoabTab>
  <GoabTab
    heading={
      <>
        Complete
        <GoabBadge type="information" content="338" icon={false} />
      </>
    }
  >
    <GoabTable width="100%">
      <thead>
        <tr>
          <th>Status</th>
          <th>Text</th>
          <th className="goa-table-number-header">Number</th>
          <th style={{ width: "1%", whiteSpace: "nowrap" }}>Action</th>
        </tr>
      </thead>
      <tbody>
        {complete.map((i) => (
          <tr key={i}>
            <td>
              <GoabBadge type="information" content="Complete" />
            </td>
            <td>Lorem Ipsum</td>
            <td className="goa-table-number-column">1234567890</td>
            <td>
              <GoabButton type="tertiary" size="compact">
                Action
              </GoabButton>
            </td>
          </tr>
        ))}
      </tbody>
    </GoabTable>
  </GoabTab>
</GoabTabs>
review = [0, 1, 2, 3];
complete = [0, 1];
<goab-tabs [initialTab]="2">
  <goab-tab heading="All">
    <goab-table width="100%">
      <thead>
        <tr>
          <th>Status</th>
          <th>Text</th>
          <th class="goa-table-number-header">Number</th>
          <th style="width: 1%; white-space: nowrap">Action</th>
        </tr>
      </thead>
      <tbody>
        @for (i of review; track $index) {
        <tr>
          <td>
            <goab-badge type="important" content="Review pending"></goab-badge>
          </td>
          <td>Lorem Ipsum</td>
          <td class="goa-table-number-column">1234567890</td>
          <td>
            <goab-button type="tertiary" size="compact">Action</goab-button>
          </td>
        </tr>
        } @for (i of complete; track $index) {
        <tr>
          <td>
            <goab-badge type="information" content="Complete"></goab-badge>
          </td>
          <td>Lorem Ipsum</td>
          <td class="goa-table-number-column">1234567890</td>
          <td>
            <goab-button type="tertiary" size="compact">Action</goab-button>
          </td>
        </tr>
        }
      </tbody>
    </goab-table>
  </goab-tab>
  <goab-tab [heading]="reviewPending">
    <ng-template #reviewPending
      >Review pending<goab-badge type="important" content="4" [icon]="false"></goab-badge
    ></ng-template>
    <goab-table width="100%">
      <thead>
        <tr>
          <th>Status</th>
          <th>Text</th>
          <th class="goa-table-number-header">Number</th>
          <th style="width: 1%; white-space: nowrap">Action</th>
        </tr>
      </thead>
      <tbody>
        @for (i of review; track $index) {
        <tr>
          <td>
            <goab-badge type="important" content="Review pending"></goab-badge>
          </td>
          <td>Lorem Ipsum</td>
          <td class="goa-table-number-column">1234567890</td>
          <td>
            <goab-button type="tertiary" size="compact">Action</goab-button>
          </td>
        </tr>
        }
      </tbody>
    </goab-table>
  </goab-tab>
  <goab-tab [heading]="completeTemplate">
    <ng-template #completeTemplate
      >Complete<goab-badge type="information" content="338" [icon]="false"></goab-badge
    ></ng-template>
    <goab-table width="100%">
      <thead>
        <tr>
          <th>Status</th>
          <th>Text</th>
          <th class="goa-table-number-header">Number</th>
          <th style="width: 1%; white-space: nowrap">Action</th>
        </tr>
      </thead>
      <tbody>
        @for (i of complete; track $index) {
        <tr>
          <td>
            <goab-badge type="information" content="Complete"></goab-badge>
          </td>
          <td>Lorem Ipsum</td>
          <td class="goa-table-number-column">1234567890</td>
          <td>
            <goab-button type="tertiary" size="compact">Action</goab-button>
          </td>
        </tr>
        }
      </tbody>
    </goab-table>
  </goab-tab>
</goab-tabs>
<goa-tabs version="2" initialtab="2">
  <goa-tab>
    <div slot="heading">All</div>
    <goa-table version="2" width="100%">
      <table width="100%">
        <thead>
          <tr>
            <th>Status</th>
            <th>Text</th>
            <th class="goa-table-number-header">Number</th>
            <th style="width: 1%; white-space: nowrap">Action</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>
              <goa-badge
                version="2"
                type="important"
                content="Review pending"
              ></goa-badge>
            </td>
            <td>Lorem Ipsum</td>
            <td class="goa-table-number-column">1234567890</td>
            <td>
              <goa-button version="2" type="tertiary" size="compact">Action</goa-button>
            </td>
          </tr>
          <tr>
            <td>
              <goa-badge
                version="2"
                type="important"
                content="Review pending"
              ></goa-badge>
            </td>
            <td>Lorem Ipsum</td>
            <td class="goa-table-number-column">1234567890</td>
            <td>
              <goa-button version="2" type="tertiary" size="compact">Action</goa-button>
            </td>
          </tr>
          <tr>
            <td>
              <goa-badge version="2" type="information" content="Complete"></goa-badge>
            </td>
            <td>Lorem Ipsum</td>
            <td class="goa-table-number-column">1234567890</td>
            <td>
              <goa-button version="2" type="tertiary" size="compact">Action</goa-button>
            </td>
          </tr>
        </tbody>
      </table>
    </goa-table>
  </goa-tab>
  <goa-tab>
    <div slot="heading">
      Review pending<goa-badge
        version="2"
        type="important"
        content="4"
        icon="false"
      ></goa-badge>
    </div>
    <goa-table version="2" width="100%">
      <table width="100%">
        <thead>
          <tr>
            <th>Status</th>
            <th>Text</th>
            <th class="goa-table-number-header">Number</th>
            <th style="width: 1%; white-space: nowrap">Action</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>
              <goa-badge
                version="2"
                type="important"
                content="Review pending"
              ></goa-badge>
            </td>
            <td>Lorem Ipsum</td>
            <td class="goa-table-number-column">1234567890</td>
            <td>
              <goa-button version="2" type="tertiary" size="compact">Action</goa-button>
            </td>
          </tr>
          <tr>
            <td>
              <goa-badge
                version="2"
                type="important"
                content="Review pending"
              ></goa-badge>
            </td>
            <td>Lorem Ipsum</td>
            <td class="goa-table-number-column">1234567890</td>
            <td>
              <goa-button version="2" type="tertiary" size="compact">Action</goa-button>
            </td>
          </tr>
        </tbody>
      </table>
    </goa-table>
  </goa-tab>
  <goa-tab>
    <div slot="heading">
      Complete<goa-badge
        version="2"
        type="information"
        content="338"
        icon="false"
      ></goa-badge>
    </div>
    <goa-table version="2" width="100%">
      <table width="100%">
        <thead>
          <tr>
            <th>Status</th>
            <th>Text</th>
            <th class="goa-table-number-header">Number</th>
            <th style="width: 1%; white-space: nowrap">Action</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>
              <goa-badge version="2" type="information" content="Complete"></goa-badge>
            </td>
            <td>Lorem Ipsum</td>
            <td class="goa-table-number-column">1234567890</td>
            <td>
              <goa-button version="2" type="tertiary" size="compact">Action</goa-button>
            </td>
          </tr>
          <tr>
            <td>
              <goa-badge version="2" type="information" content="Complete"></goa-badge>
            </td>
            <td>Lorem Ipsum</td>
            <td class="goa-table-number-column">1234567890</td>
            <td>
              <goa-button version="2" type="tertiary" size="compact">Action</goa-button>
            </td>
          </tr>
        </tbody>
      </table>
    </goa-table>
  </goa-tab>
</goa-tabs>

Show different views of data in a table

const review = [0, 1, 2, 3];
const complete = [0, 1];
<GoabTabs initialTab={1}>
  <GoabTab heading="All">
    <GoabTable width="100%">
      <thead>
        <tr>
          <th>Status</th>
          <th>Text</th>
          <th className="goa-table-number-header">Number</th>
          <th style={{ width: "1%", whiteSpace: "nowrap" }}>Action</th>
        </tr>
      </thead>
      <tbody>
        {review.map((i) => (
          <tr key={`review-${i}`}>
            <td>
              <GoabBadge type="important" content="Review pending" />
            </td>
            <td>Lorem Ipsum</td>
            <td className="goa-table-number-column">1234567890</td>
            <td>
              <GoabButton type="tertiary" size="compact">
                Action
              </GoabButton>
            </td>
          </tr>
        ))}
        {complete.map((i) => (
          <tr key={`complete-${i}`}>
            <td>
              <GoabBadge type="information" content="Complete" />
            </td>
            <td>Lorem Ipsum</td>
            <td className="goa-table-number-column">1234567890</td>
            <td>
              <GoabButton type="tertiary" size="compact">
                Action
              </GoabButton>
            </td>
          </tr>
        ))}
      </tbody>
    </GoabTable>
  </GoabTab>
  <GoabTab
    heading={
        Review pending
        <GoabBadge type="important" content="4" icon={false} />
    }
  >
    <GoabTable width="100%">
      <thead>
        <tr>
          <th>Status</th>
          <th>Text</th>
          <th className="goa-table-number-header">Number</th>
          <th style={{ width: "1%", whiteSpace: "nowrap" }}>Action</th>
        </tr>
      </thead>
      <tbody>
        {review.map((i) => (
          <tr key={i}>
            <td>
              <GoabBadge type="important" content="Review pending" />
            </td>
            <td>Lorem Ipsum</td>
            <td className="goa-table-number-column">1234567890</td>
            <td>
              <GoabButton type="tertiary" size="compact">
                Action
              </GoabButton>
            </td>
          </tr>
        ))}
      </tbody>
    </GoabTable>
  </GoabTab>
  <GoabTab
    heading={
      <>
        Complete
        <GoabBadge type="information" content="338" icon={false} />
      </>
    }
  >
    <GoabTable width="100%">
      <thead>
        <tr>
          <th>Status</th>
          <th>Text</th>
          <th className="goa-table-number-header">Number</th>
          <th style={{ width: "1%", whiteSpace: "nowrap" }}>Action</th>
        </tr>
      </thead>
      <tbody>
        {complete.map((i) => (
          <tr key={i}>
            <td>
              <GoabBadge type="information" content="Complete" />
            </td>
            <td>Lorem Ipsum</td>
            <td className="goa-table-number-column">1234567890</td>
            <td>
              <GoabButton type="tertiary" size="compact">
                Action
              </GoabButton>
            </td>
          </tr>
        ))}
      </tbody>
    </GoabTable>
  </GoabTab>
</GoabTabs>
reviewItems = [0, 1, 2, 3];
completeItems = [0, 1];
<goab-tabs>
  <goab-tab heading="All">
    <goab-table width="100%">
      <thead>
        <tr>
          <th>Status</th>
          <th>Text</th>
          <th class="goa-table-number-header">Number</th>
          <th style="width: 1%; white-space: nowrap">Action</th>
        </tr>
      </thead>
      <tbody>
        @for (i of reviewItems; track $index) {
        <tr>
          <td><goab-badge type="important" content="Review pending"></goab-badge></td>
          <td>Lorem ipsum</td>
          <td class="goa-table-number-column">1234567890</td>
          <td><goab-button type="tertiary" size="compact">Action</goab-button></td>
        </tr>
        } @for (i of completeItems; track $index) {
        <tr>
          <td><goab-badge type="information" content="Complete"></goab-badge></td>
          <td>Lorem Ipsum</td>
          <td class="goa-table-number-column">1234567890</td>
          <td><goab-button type="tertiary" size="compact">Action</goab-button></td>
        </tr>
        }
      </tbody>
    </goab-table>
  </goab-tab>
  <goab-tab [heading]="reviewPending">
    <ng-template #reviewPending
      >Review pending<goab-badge type="important" content="4" [icon]="false"></goab-badge
    ></ng-template>
    <goab-table width="100%">
      <thead>
        <tr>
          <th>Status</th>
          <th>Text</th>
          <th class="goa-table-number-header">Number</th>
          <th style="width: 1%; white-space: nowrap">Action</th>
        </tr>
      </thead>
      <tbody>
        @for (i of reviewItems; track $index) {
        <tr>
          <td><goab-badge type="important" content="Review pending"></goab-badge></td>
          <td>Lorem ipsum</td>
          <td class="goa-table-number-column">1234567890</td>
          <td><goab-button type="tertiary" size="compact">Action</goab-button></td>
        </tr>
        }
      </tbody>
    </goab-table>
  </goab-tab>
  <goab-tab [heading]="completeTab">
    <ng-template #completeTab
      >Complete<goab-badge type="information" content="338" [icon]="false"></goab-badge
    ></ng-template>
    <goab-table width="100%">
      <thead>
        <tr>
          <th>Status</th>
          <th>Text</th>
          <th class="goa-table-number-header">Number</th>
          <th style="width: 1%; white-space: nowrap">Action</th>
        </tr>
      </thead>
      <tbody>
        @for (i of completeItems; track $index) {
        <tr>
          <td><goab-badge type="information" content="Complete"></goab-badge></td>
          <td>Lorem Ipsum</td>
          <td class="goa-table-number-column">1234567890</td>
          <td><goab-button type="tertiary" size="compact">Action</goab-button></td>
        </tr>
        }
      </tbody>
    </goab-table>
  </goab-tab>
</goab-tabs>
<goa-tabs version="2">
  <goa-tab>
    <div slot="heading">All</div>
    <goa-table version="2" width="100%">
      <table width="100%">
        <thead>
          <tr>
            <th>Status</th>
            <th>Text</th>
            <th class="goa-table-number-header">Number</th>
            <th style="width: 1%; white-space: nowrap">Action</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>
              <goa-badge
                version="2"
                type="important"
                content="Review pending"
              ></goa-badge>
            </td>
            <td>Lorem ipsum</td>
            <td class="goa-table-number-column">1234567890</td>
            <td>
              <goa-button version="2" type="tertiary" size="compact">Action</goa-button>
            </td>
          </tr>
          <tr>
            <td>
              <goa-badge
                version="2"
                type="important"
                content="Review pending"
              ></goa-badge>
            </td>
            <td>Lorem ipsum</td>
            <td class="goa-table-number-column">1234567890</td>
            <td>
              <goa-button version="2" type="tertiary" size="compact">Action</goa-button>
            </td>
          </tr>
          <tr>
            <td>
              <goa-badge version="2" type="information" content="Complete"></goa-badge>
            </td>
            <td>Lorem Ipsum</td>
            <td class="goa-table-number-column">1234567890</td>
            <td>
              <goa-button version="2" type="tertiary" size="compact">Action</goa-button>
            </td>
          </tr>
        </tbody>
      </table>
    </goa-table>
  </goa-tab>
  <goa-tab>
    <div slot="heading">
      Review pending<goa-badge
        version="2"
        type="important"
        content="4"
        icon="false"
      ></goa-badge>
    </div>
    <goa-table version="2" width="100%">
      <table width="100%">
        <thead>
          <tr>
            <th>Status</th>
            <th>Text</th>
            <th class="goa-table-number-header">Number</th>
            <th style="width: 1%; white-space: nowrap">Action</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>
              <goa-badge
                version="2"
                type="important"
                content="Review pending"
              ></goa-badge>
            </td>
            <td>Lorem ipsum</td>
            <td class="goa-table-number-column">1234567890</td>
            <td>
              <goa-button version="2" type="tertiary" size="compact">Action</goa-button>
            </td>
          </tr>
          <tr>
            <td>
              <goa-badge
                version="2"
                type="important"
                content="Review pending"
              ></goa-badge>
            </td>
            <td>Lorem ipsum</td>
            <td class="goa-table-number-column">1234567890</td>
            <td>
              <goa-button version="2" type="tertiary" size="compact">Action</goa-button>
            </td>
          </tr>
        </tbody>
      </table>
    </goa-table>
  </goa-tab>
  <goa-tab>
    <div slot="heading">
      Complete<goa-badge
        version="2"
        type="information"
        content="338"
        icon="false"
      ></goa-badge>
    </div>
    <goa-table version="2" width="100%">
      <table width="100%">
        <thead>
          <tr>
            <th>Status</th>
            <th>Text</th>
            <th class="goa-table-number-header">Number</th>
            <th style="width: 1%; white-space: nowrap">Action</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>
              <goa-badge version="2" type="information" content="Complete"></goa-badge>
            </td>
            <td>Lorem Ipsum</td>
            <td class="goa-table-number-column">1234567890</td>
            <td>
              <goa-button version="2" type="tertiary" size="compact">Action</goa-button>
            </td>
          </tr>
        </tbody>
      </table>
    </goa-table>
  </goa-tab>
</goa-tabs>

Show multiple actions in a compact table

const rows = [
  {
    status: "information",
    statusText: "In progress",
    name: "Darlene Robertson",
    id: 45904,
  },
  { status: "default", statusText: "Inactive", name: "Floyd Miles", id: 47838 },
  { status: "success", statusText: "Active", name: "Kathryn Murphy", id: 34343 },
  { status: "important", statusText: "Recent", name: "Annette Black", id: 89897 },
  { status: "success", statusText: "Active", name: "Esther Howard", id: 12323 },
  { status: "success", statusText: "Active", name: "Jane Cooper", id: 56565 },
];
<GoabTable width="100%">
  <thead>
    <tr>
      <th>Status</th>
      <th>Name</th>
      <th style={{ textAlign: "right" }}>Id Number</th>
      <th style={{ width: "1%", whiteSpace: "nowrap" }}>Edit | Flag | Send</th>
    </tr>
  </thead>
  <tbody>
    {rows.map((row) => (
      <tr key={row.id}>
        <td>
          <GoabBadge
            type={row.status as "information" | "default" | "success" | "important"}
            content={row.statusText}
            icon={false}
          />
        </td>
        <td>{row.name}</td>
        <td className="goa-table-number-column">{row.id}</td>
        <td>
          <GoabBlock>
            <GoabIconButton size="small" icon="pencil" ariaLabel="Edit" />
            <GoabIconButton size="small" icon="flag" ariaLabel="Flag" />
            <GoabIconButton size="small" icon="mail" ariaLabel="Send" />
          </GoabBlock>
        </td>
      </tr>
    ))}
  </tbody>
</GoabTable>
rows: TableRow[] = [
  {
    status: "information",
    statusText: "In progress",
    name: "Darlene Robertson",
    id: 45904,
  },
  { status: "default", statusText: "Inactive", name: "Floyd Miles", id: 47838 },
  { status: "success", statusText: "Active", name: "Kathryn Murphy", id: 34343 },
  { status: "important", statusText: "Recent", name: "Annette Black", id: 89897 },
  { status: "success", statusText: "Active", name: "Esther Howard", id: 12323 },
  { status: "success", statusText: "Active", name: "Jane Cooper", id: 56565 },
];
<goab-table width="100%">
  <thead>
    <tr>
      <th>Status</th>
      <th>Name</th>
      <th style="text-align: right">Id Number</th>
      <th style="width: 1%; white-space: nowrap">Edit | Flag | Send</th>
    </tr>
  </thead>
  <tbody>
    @for (row of rows; track row.id) {
    <tr>
      <td>
        <goab-badge
          [type]="row.status"
          [content]="row.statusText"
          [icon]="false"
        ></goab-badge>
      </td>
      <td>{{ row.name }}</td>
      <td class="goa-table-number-column">{{ row.id }}</td>
      <td>
        <goab-block>
          <goab-icon-button
            size="small"
            icon="pencil"
            ariaLabel="Edit"
          ></goab-icon-button>
          <goab-icon-button size="small" icon="flag" ariaLabel="Flag"></goab-icon-button>
          <goab-icon-button size="small" icon="mail" ariaLabel="Send"></goab-icon-button>
        </goab-block>
      </td>
    </tr>
    }
  </tbody>
</goab-table>
<goa-table version="2" width="100%">
  <table width="100%">
    <thead>
      <tr>
        <th>Status</th>
        <th>Name</th>
        <th style="text-align: right">Id Number</th>
        <th style="width: 1%; white-space: nowrap">Edit | Flag | Send</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>
          <goa-badge
            version="2"
            type="information"
            content="In progress"
            icon="false"
          ></goa-badge>
        </td>
        <td>Darlene Robertson</td>
        <td class="goa-table-number-column">45904</td>
        <td>
          <goa-block>
            <goa-icon-button
              size="small"
              icon="pencil"
              arialabel="Edit"
            ></goa-icon-button>
            <goa-icon-button size="small" icon="flag" arialabel="Flag"></goa-icon-button>
            <goa-icon-button size="small" icon="mail" arialabel="Send"></goa-icon-button>
          </goa-block>
        </td>
      </tr>
      <tr>
        <td>
          <goa-badge
            version="2"
            type="default"
            content="Inactive"
            icon="false"
          ></goa-badge>
        </td>
        <td>Floyd Miles</td>
        <td class="goa-table-number-column">47838</td>
        <td>
          <goa-block>
            <goa-icon-button
              size="small"
              icon="pencil"
              arialabel="Edit"
            ></goa-icon-button>
            <goa-icon-button size="small" icon="flag" arialabel="Flag"></goa-icon-button>
            <goa-icon-button size="small" icon="mail" arialabel="Send"></goa-icon-button>
          </goa-block>
        </td>
      </tr>
      <tr>
        <td>
          <goa-badge version="2" type="success" content="Active" icon="false"></goa-badge>
        </td>
        <td>Kathryn Murphy</td>
        <td class="goa-table-number-column">34343</td>
        <td>
          <goa-block>
            <goa-icon-button
              size="small"
              icon="pencil"
              arialabel="Edit"
            ></goa-icon-button>
            <goa-icon-button size="small" icon="flag" arialabel="Flag"></goa-icon-button>
            <goa-icon-button size="small" icon="mail" arialabel="Send"></goa-icon-button>
          </goa-block>
        </td>
      </tr>
      <tr>
        <td>
          <goa-badge
            version="2"
            type="important"
            content="Recent"
            icon="false"
          ></goa-badge>
        </td>
        <td>Annette Black</td>
        <td class="goa-table-number-column">89897</td>
        <td>
          <goa-block>
            <goa-icon-button
              size="small"
              icon="pencil"
              arialabel="Edit"
            ></goa-icon-button>
            <goa-icon-button size="small" icon="flag" arialabel="Flag"></goa-icon-button>
            <goa-icon-button size="small" icon="mail" arialabel="Send"></goa-icon-button>
          </goa-block>
        </td>
      </tr>
      <tr>
        <td>
          <goa-badge version="2" type="success" content="Active" icon="false"></goa-badge>
        </td>
        <td>Esther Howard</td>
        <td class="goa-table-number-column">12323</td>
        <td>
          <goa-block>
            <goa-icon-button
              size="small"
              icon="pencil"
              arialabel="Edit"
            ></goa-icon-button>
            <goa-icon-button size="small" icon="flag" arialabel="Flag"></goa-icon-button>
            <goa-icon-button size="small" icon="mail" arialabel="Send"></goa-icon-button>
          </goa-block>
        </td>
      </tr>
      <tr>
        <td>
          <goa-badge version="2" type="success" content="Active" icon="false"></goa-badge>
        </td>
        <td>Jane Cooper</td>
        <td class="goa-table-number-column">56565</td>
        <td>
          <goa-block>
            <goa-icon-button
              size="small"
              icon="pencil"
              arialabel="Edit"
            ></goa-icon-button>
            <goa-icon-button size="small" icon="flag" arialabel="Flag"></goa-icon-button>
            <goa-icon-button size="small" icon="mail" arialabel="Send"></goa-icon-button>
          </goa-block>
        </td>
      </tr>
    </tbody>
  </table>
</goa-table>

Show number of results per page

interface User {
  id: string;
  firstName: string;
  lastName: string;
  age: number;
}

const [users] = useState<User[]>(() => generateUsers());
const [page, setPage] = useState<number>(1);
const [perPage, setPerPage] = useState<number>(10);

const offset = (page - 1) * perPage;
const pageUsers = users.slice(offset, offset + perPage);

function changePage(newPage: number) {
  setPage(newPage);
}

function handlePerPageCountChangeEvent(event: GoabDropdownOnChangeDetail) {
  setPage(1);
  setPerPage(parseInt(event.value || "10"));
}
<GoabTable width="100%" mb="xl">
  <thead>
    <tr>
      <th>First name</th>
      <th>Last name</th>
      <th>Age</th>
    </tr>
  </thead>
  <tbody>
    {pageUsers.map((u) => (
      <tr key={u.id}>
        <td>{u.firstName}</td>
        <td>{u.lastName}</td>
        <td>{u.age}</td>
      </tr>
    ))}
  </tbody>
</GoabTable>

<GoabBlock alignment="center" width="100%">
  <GoabBlock mb="m" alignment="center">
    Show
    <GoabDropdown
      onChange={handlePerPageCountChangeEvent}
      value={perPage.toString()}
      width="9ch"
    >
      <GoabDropdownItem value="10" label="10" />
      <GoabDropdownItem value="20" label="20" />
      <GoabDropdownItem value="30" label="30" />
    </GoabDropdown>
    <span style={{ width: "75px" }}>per page</span>
  </GoabBlock>
  <GoabSpacer hSpacing="fill" />
  <GoabPagination
    itemCount={users.length}
    perPageCount={perPage}
    pageNumber={page}
    onChange={(event) => changePage(event.page)}
  />
</GoabBlock>
  users: User[] = generateUsers();
  page = 1;
  perPage = 10;

  get pageUsers(): User[] {
    const offset = (this.page - 1) * this.perPage;
    return this.users.slice(offset, offset + this.perPage);
  }

  handlePageChange(event: GoabPaginationOnChangeDetail): void {
    this.page = event.page;
  }

  handlePerPageCountChangeEvent(event: GoabDropdownOnChangeDetail): void {
    this.page = 1;
    this.perPage = Number(event.value);
  }
}

function generateUsers(): User[] {
  const firstNames = [
    "Emma",
    "Liam",
    "Olivia",
    "Noah",
    "Ava",
    "James",
    "Sophia",
    "William",
    "Isabella",
    "Oliver",
    "Mia",
    "Benjamin",
    "Charlotte",
    "Elijah",
    "Amelia",
    "Lucas",
    "Harper",
    "Mason",
    "Evelyn",
    "Logan",
  ];
  const lastNames = [
    "Smith",
    "Johnson",
    "Williams",
    "Brown",
    "Jones",
    "Garcia",
    "Miller",
    "Davis",
    "Rodriguez",
    "Martinez",
    "Wilson",
    "Anderson",
    "Taylor",
    "Thomas",
    "Moore",
    "Jackson",
    "Martin",
    "Lee",
    "Thompson",
    "White",
  ];
  const users: User[] = [];
  for (let i = 1; i <= 100; i++) {
    users.push({
      id: `user-${i}`,
      firstName: firstNames[(i - 1) % firstNames.length],
      lastName: lastNames[(i - 1) % lastNames.length],
      age: 20 + (i % 40),
    });
  }
  return users;
<goab-table width="100%" mb="xl">
  <thead>
    <tr>
      <th>First name</th>
      <th>Last name</th>
      <th>Age</th>
    </tr>
  </thead>
  <tbody>
    @for (user of pageUsers; track $index) {
    <tr>
      <td>{{ user.firstName }}</td>
      <td>{{ user.lastName }}</td>
      <td>{{ user.age }}</td>
    </tr>
    }
  </tbody>
</goab-table>

<goab-block alignment="center" width="100%">
  <goab-block mb="m" alignment="center">
    Show
    <goab-dropdown
      (onChange)="handlePerPageCountChangeEvent($event)"
      [value]="perPage.toString()"
      width="9ch"
    >
      <goab-dropdown-item value="10" label="10"></goab-dropdown-item>
      <goab-dropdown-item value="20" label="20"></goab-dropdown-item>
      <goab-dropdown-item value="30" label="30"></goab-dropdown-item>
    </goab-dropdown>
    <span style="width: 75px">per page</span>
  </goab-block>

  <goab-spacer hSpacing="fill"></goab-spacer>

  <goab-pagination
    [itemCount]="users.length"
    [perPageCount]="perPage"
    [pageNumber]="page"
    (onChange)="handlePageChange($event)"
  >
  </goab-pagination>
</goab-block>
const firstNames = [
  "Emma",
  "Liam",
  "Olivia",
  "Noah",
  "Ava",
  "James",
  "Sophia",
  "William",
  "Isabella",
  "Oliver",
  "Mia",
  "Benjamin",
  "Charlotte",
  "Elijah",
  "Amelia",
  "Lucas",
  "Harper",
  "Mason",
  "Evelyn",
  "Logan",
];
const lastNames = [
  "Smith",
  "Johnson",
  "Williams",
  "Brown",
  "Jones",
  "Garcia",
  "Miller",
  "Davis",
  "Rodriguez",
  "Martinez",
  "Wilson",
  "Anderson",
  "Taylor",
  "Thomas",
  "Moore",
  "Jackson",
  "Martin",
  "Lee",
  "Thompson",
  "White",
];
const users = [];
for (let i = 1; i <= 100; i++) {
  users.push({
    id: "user-" + i,
    firstName: firstNames[(i - 1) % firstNames.length],
    lastName: lastNames[(i - 1) % lastNames.length],
    age: 20 + (i % 40),
  });
}

let page = 1;
let perPage = 10;

const tableBody = document.getElementById("table-body");
const pagination = document.getElementById("pagination");
const dropdown = document.getElementById("per-page-dropdown");

function renderTable() {
  const offset = (page - 1) * perPage;
  const pageUsers = users.slice(offset, offset + perPage);

  tableBody.innerHTML = pageUsers
    .map(
      (u) => `
      <tr>
        <td>${u.firstName}</td>
        <td>${u.lastName}</td>
        <td>${u.age}</td>
      </tr>
    `,
    )
    .join("");
}

pagination.addEventListener("_change", (e) => {
  page = e.detail.page;
  renderTable();
});

dropdown.addEventListener("_change", (e) => {
  perPage = parseInt(e.detail.value);
  page = 1;
  pagination.setAttribute("perpagecount", perPage);
  pagination.setAttribute("pagenumber", "1");
  renderTable();
});

renderTable();
<goa-table version="2" width="100%" mb="xl">
  <table width="100%">
    <thead>
      <tr>
        <th>First name</th>
        <th>Last name</th>
        <th>Age</th>
      </tr>
    </thead>
    <tbody id="table-body">
      <!-- Rows populated by JavaScript -->
    </tbody>
  </table>
</goa-table>

<goa-block alignment="center" width="100%">
  <goa-block mb="m" alignment="center">
    Show
    <goa-dropdown version="2" id="per-page-dropdown" value="10" width="9ch">
      <goa-dropdown-item value="10" label="10"></goa-dropdown-item>
      <goa-dropdown-item value="20" label="20"></goa-dropdown-item>
      <goa-dropdown-item value="30" label="30"></goa-dropdown-item>
    </goa-dropdown>
    <span style="width: 75px">per page</span>
  </goa-block>

  <goa-spacer hspacing="fill"></goa-spacer>

  <goa-pagination
    version="2"
    id="pagination"
    itemcount="100"
    perpagecount="10"
    pagenumber="1"
  >
  </goa-pagination>
</goa-block>

Show status in a table

interface BadgeValue {
  key: number;
  type: GoabBadgeType;
  content: string;
}

const badgeValues: BadgeValue[] = [
  { key: 1, type: "important", content: "Pending" },
  { key: 2, type: "emergency", content: "Failed" },
  { key: 3, type: "success", content: "Complete" },
  { key: 4, type: "information", content: "In progress" },
  { key: 5, type: "default", content: "Closed" },
  { key: 6, type: "success", content: "Complete" },
];

const handleClick = () => {
  console.log("clicked");
};
<GoabTable width="100%">
  <thead>
    <tr>
      <th>Status</th>
      <th>Name</th>
      <th className="goa-table-number-header">File number</th>
      <th style={{ width: "1%", whiteSpace: "nowrap" }}></th>
    </tr>
  </thead>
  <tbody>
    {badgeValues.map((badge) => (
      <tr key={badge.key}>
        <td>
          <GoabBadge type={badge.type} content={badge.content} icon={false} />
        </td>
        <td>Lorem ipsum dolor sit amet consectetur</td>
        <td className="goa-table-number-column">1234567890</td>
        <td>
          <GoabButton size="compact" type="tertiary" onClick={handleClick}>
            Assign
          </GoabButton>
        </td>
      </tr>
    ))}
  </tbody>
</GoabTable>
badgeValues: BadgeValue[] = [
  { type: "important", content: "Pending" },
  { type: "emergency", content: "Failed" },
  { type: "success", content: "Complete" },
  { type: "information", content: "In progress" },
  { type: "default", content: "Closed" },
  { type: "success", content: "Complete" },
];

onClick(): void {
  console.log("clicked");
}
<goab-table width="100%">
  <thead>
    <tr>
      <th>Status</th>
      <th>Name</th>
      <th class="goa-table-number-header">File number</th>
      <th style="width: 1%; white-space: nowrap"></th>
    </tr>
  </thead>
  <tbody>
    @for (badge of badgeValues; track $index) {
    <tr>
      <td>
        <goab-badge
          [type]="badge.type"
          [content]="badge.content"
          [icon]="false"
        ></goab-badge>
      </td>
      <td>Lorem ipsum dolor sit amet consectetur.</td>
      <td class="goa-table-number-column">1234567890</td>
      <td>
        <goab-button size="compact" type="tertiary" (onClick)="onClick()"
          >Assign</goab-button
        >
      </td>
    </tr>
    }
  </tbody>
</goab-table>
const badgeValues = [
  { type: "important", content: "Pending" },
  { type: "emergency", content: "Failed" },
  { type: "success", content: "Complete" },
  { type: "information", content: "In progress" },
  { type: "default", content: "Closed" },
  { type: "success", content: "Complete" },
];

const tbody = document.getElementById("table-body");

badgeValues.forEach((badge) => {
  const row = document.createElement("tr");
  row.innerHTML = `
    <td><goa-badge version="2" type="${badge.type}" content="${badge.content}" icon="false"></goa-badge></td>
    <td>Lorem ipsum dolor sit amet consectetur</td>
    <td class="goa-table-number-column">1234567890</td>
    <td><goa-button version="2" size="compact" type="tertiary">Assign</goa-button></td>
  `;
  const button = row.querySelector("goa-button");
  button.addEventListener("_click", () => console.log("clicked"));
  tbody.appendChild(row);
});
<goa-table version="2" width="100%" id="status-table">
  <table width="100%">
    <thead>
      <tr>
        <th>Status</th>
        <th>Name</th>
        <th class="goa-table-number-header">File number</th>
        <th style="width: 1%; white-space: nowrap"></th>
      </tr>
    </thead>
    <tbody id="table-body"></tbody>
  </table>
</goa-table>

Sort data in a table

interface User {
  firstName: string;
  lastName: string;
  age: number;
}

const [users, setUsers] = useState<User[]>([]);

useEffect(() => {
  const _users: User[] = [
    { firstName: "Christian", lastName: "Batz", age: 18 },
    { firstName: "Brain", lastName: "Wisozk", age: 19 },
    { firstName: "Neha", lastName: "Jones", age: 23 },
    { firstName: "Tristin", lastName: "Buckridge", age: 31 },
  ];
  setUsers(_users);
}, []);

function sortData(event: GoabTableOnSortDetail) {
  const _users = [...users];
  _users.sort((a: any, b: any) => {
<GoabTable onSort={sortData} width="100%">
  <thead>
    <tr>
      <th>
        <GoabTableSortHeader name="firstName">First name</GoabTableSortHeader>
      </th>
      <th>
        <GoabTableSortHeader name="lastName">Last name</GoabTableSortHeader>
      </th>
      <th>
        <GoabTableSortHeader name="age" direction="asc">
          Age
        </GoabTableSortHeader>
      </th>
    </tr>
  </thead>
  <tbody>
    {users.map((user) => (
      <tr key={user.firstName}>
        <td>{user.firstName}</td>
        <td>{user.lastName}</td>
        <td>{user.age}</td>
      </tr>
    ))}
  </tbody>
</GoabTable>
users: User[] = [
  { firstName: "Christian", lastName: "Batz", age: 18 },
  { firstName: "Brain", lastName: "Wisozk", age: 19 },
  { firstName: "Neha", lastName: "Jones", age: 23 },
  { firstName: "Tristin", lastName: "Buckridge", age: 31 },
];

handleSort(event: GoabTableOnSortDetail): void {
  const { sortBy, sortDir } = event;
  this.users.sort((a: any, b: any) => (a[sortBy] > b[sortBy] ? 1 : -1) * sortDir);
}
<goab-table width="100%" mb="xl" (onSort)="handleSort($event)">
  <thead>
    <tr>
      <th>
        <goab-table-sort-header name="firstName">First name</goab-table-sort-header>
      </th>
      <th>
        <goab-table-sort-header name="lastName">Last name</goab-table-sort-header>
      </th>
      <th>
        <goab-table-sort-header name="age" direction="asc">Age</goab-table-sort-header>
      </th>
    </tr>
  </thead>
  <tbody>
    @for (user of users; track $index) {
    <tr>
      <td>{{ user.firstName }}</td>
      <td>{{ user.lastName }}</td>
      <td>{{ user.age }}</td>
    </tr>
    }
  </tbody>
</goab-table>
let users = [
  { firstName: "Christian", lastName: "Batz", age: 18 },
  { firstName: "Brain", lastName: "Wisozk", age: 19 },
  { firstName: "Neha", lastName: "Jones", age: 23 },
  { firstName: "Tristin", lastName: "Buckridge", age: 31 },
];

const tbody = document.getElementById("table-body");
let sortColumn = "age";
let sortDirection = 1; // 1 = asc, -1 = desc

function renderTable() {
  tbody.innerHTML = "";
  users.forEach((user) => {
    const row = document.createElement("tr");
    row.innerHTML = `
      <td>${user.firstName}</td>
      <td>${user.lastName}</td>
      <td>${user.age}</td>
    `;
    tbody.appendChild(row);
  });
}

function sortTable(column) {
  // Toggle direction if same column, otherwise start ascending
  if (column === sortColumn) {
    sortDirection = sortDirection * -1;
  } else {
    sortColumn = column;
    sortDirection = 1;
  }

  // Update header visual states
  document.querySelectorAll("goa-table-sort-header").forEach((header) => {
    const name = header.getAttribute("name");
    if (name === sortColumn) {
      header.setAttribute("direction", sortDirection === 1 ? "asc" : "desc");
    } else {
      header.setAttribute("direction", "none");
    }
  });

  // Sort the data
  users.sort((a, b) => (a[sortColumn] > b[sortColumn] ? 1 : -1) * sortDirection);
  renderTable();
}

// Attach click handlers directly to sort headers
// (Table's built-in slot-based wiring doesn't work with innerHTML)
document.querySelectorAll("goa-table-sort-header").forEach((header) => {
  header.addEventListener("click", () => {
    const column = header.getAttribute("name");
    sortTable(column);
  });
});

// Initial sort by age ascending
users.sort((a, b) => (a.age > b.age ? 1 : -1));
renderTable();
<goa-table version="2" width="100%" mb="xl">
  <table width="100%">
    <thead>
      <tr>
        <th>
          <goa-table-sort-header name="firstName">First name</goa-table-sort-header>
        </th>
        <th>
          <goa-table-sort-header name="lastName">Last name</goa-table-sort-header>
        </th>
        <th>
          <goa-table-sort-header name="age" direction="asc">Age</goa-table-sort-header>
        </th>
      </tr>
    </thead>
    <tbody id="table-body"></tbody>
  </table>
</goa-table>

Task list page

<GoabText tag="h1" mt="none">
  Apply for a service
</GoabText>
<GoabCallout
  type="important"
  emphasis="low"
  size="medium"
  heading="Application incomplete"
  mb="2xl"
  mt="xl"
  maxWidth="360px"
>
  You have completed 1 of 3 sections.
</GoabCallout>

<GoabText tag="h2">1. Before you start</GoabText>
<GoabTable width="100%" mb="2xl" mt="l">
  <tbody>
    <tr>
      <td>
        <a href="#">Read terms of use</a>
      </td>
      <td className="goa-table-number-column">
        <GoabBadge
          type="success"
          content="Completed"
          ariaLabel="completed"
          icon={false}
        />
      </td>
    </tr>
  </tbody>
</GoabTable>

<GoabText tag="h2">2. Prepare application</GoabText>
<GoabTable width="100%" mb="2xl" mt="l">
  <tbody>
    <tr>
      <td>
        <a href="#">Your contact details</a>
      </td>
      <td className="goa-table-number-column">
        <GoabBadge
          type="information"
          content="Not started"
          ariaLabel="not started"
          icon={false}
        />
      </td>
    </tr>
    <tr>
      <td>
        <a href="#">Your family</a>
      </td>
      <td className="goa-table-number-column">
        <GoabBadge
          type="information"
          content="Not started"
          ariaLabel="not started"
          icon={false}
        />
      </td>
    </tr>
    <tr>
      <td>
        <a href="#">Verify your identity</a>
      </td>
      <td className="goa-table-number-column">
        <GoabBadge
          type="information"
          content="Not started"
          ariaLabel="not started"
          icon={false}
        />
      </td>
    </tr>
  </tbody>
</GoabTable>

<GoabText tag="h2" mb="s">
  3. Schedule service
</GoabText>
<GoabText size="body-s" color="secondary" mt="2xs">
  You need to complete the previous section before you can start this task.
</GoabText>
<GoabTable width="100%" mt="l" mb="3xl">
  <tbody>
    <tr>
      <td>Receive email confirmation</td>
      <td className="goa-table-number-column">
        <GoabBadge
          type="default"
          content="Cannot start yet"
          ariaLabel="cannot start yet"
          icon={false}
        />
      </td>
    </tr>
    <tr>
      <td>Pay service fee</td>
      <td className="goa-table-number-column">
        <GoabBadge
          type="default"
          content="Cannot start yet"
          ariaLabel="cannot start yet"
          icon={false}
        />
      </td>
    </tr>
  </tbody>
</GoabTable>
<goab-text tag="h1" mt="none">Apply for a service</goab-text>
<goab-callout
  type="important"
  emphasis="low"
  size="medium"
  heading="Application incomplete"
  mb="2xl"
  mt="xl"
  maxWidth="360px"
>
  You have completed 1 of 3 sections.
</goab-callout>

<goab-text tag="h2">1. Before you start</goab-text>
<goab-table width="100%" mb="2xl" mt="l">
  <tbody>
    <tr>
      <td><a href="#">Read terms of use</a></td>
      <td class="goa-table-number-column">
        <goab-badge
          type="success"
          content="Completed"
          ariaLabel="completed"
          [icon]="false"
        ></goab-badge>
      </td>
    </tr>
  </tbody>
</goab-table>

<goab-text tag="h2">2. Prepare application</goab-text>
<goab-table width="100%" mb="2xl" mt="l">
  <tbody>
    <tr>
      <td><a href="#">Your contact details</a></td>
      <td class="goa-table-number-column">
        <goab-badge
          type="information"
          content="Not started"
          ariaLabel="not started"
          [icon]="false"
        ></goab-badge>
      </td>
    </tr>
    <tr>
      <td><a href="#">Your family</a></td>
      <td class="goa-table-number-column">
        <goab-badge
          type="information"
          content="Not started"
          ariaLabel="not started"
          [icon]="false"
        ></goab-badge>
      </td>
    </tr>
    <tr>
      <td><a href="#">Verify your identity</a></td>
      <td class="goa-table-number-column">
        <goab-badge
          type="information"
          content="Not started"
          ariaLabel="not started"
          [icon]="false"
        ></goab-badge>
      </td>
    </tr>
  </tbody>
</goab-table>

<goab-text tag="h2" mb="s">3. Schedule service</goab-text>
<goab-text size="body-s" color="secondary" mt="2xs"
  >You need to complete the previous section before you can start this task.</goab-text
>
<goab-table width="100%" mt="l" mb="3xl">
  <tbody>
    <tr>
      <td>Receive email confirmation</td>
      <td class="goa-table-number-column">
        <goab-badge
          type="default"
          content="Cannot start yet"
          ariaLabel="cannot start yet"
          [icon]="false"
        ></goab-badge>
      </td>
    </tr>
    <tr>
      <td>Pay service fee</td>
      <td class="goa-table-number-column">
        <goab-badge
          type="default"
          content="Cannot start yet"
          ariaLabel="cannot start yet"
          [icon]="false"
        ></goab-badge>
      </td>
    </tr>
  </tbody>
</goab-table>
<goa-text as="h1" mt="none">Apply for a service</goa-text>
<goa-callout
  version="2"
  type="important"
  emphasis="low"
  size="medium"
  heading="Application incomplete"
  mb="2xl"
  mt="xl"
  maxwidth="360px"
>
  You have completed 1 of 3 sections.
</goa-callout>

<goa-text as="h2">1. Before you start</goa-text>
<goa-table version="2" width="100%" mb="2xl" mt="l">
  <table style="width: 100%">
    <tbody>
      <tr>
        <td><a href="#">Read terms of use</a></td>
        <td class="goa-table-number-column">
          <goa-badge
            version="2"
            type="success"
            content="Completed"
            aria-label="completed"
            icon="false"
          ></goa-badge>
        </td>
      </tr>
    </tbody>
  </table>
</goa-table>

<goa-text as="h2">2. Prepare application</goa-text>
<goa-table version="2" width="100%" mb="2xl" mt="l">
  <table style="width: 100%">
    <tbody>
      <tr>
        <td><a href="#">Your contact details</a></td>
        <td class="goa-table-number-column">
          <goa-badge
            version="2"
            type="information"
            content="Not started"
            aria-label="not started"
            icon="false"
          ></goa-badge>
        </td>
      </tr>
      <tr>
        <td><a href="#">Your family</a></td>
        <td class="goa-table-number-column">
          <goa-badge
            version="2"
            type="information"
            content="Not started"
            aria-label="not started"
            icon="false"
          ></goa-badge>
        </td>
      </tr>
      <tr>
        <td><a href="#">Verify your identity</a></td>
        <td class="goa-table-number-column">
          <goa-badge
            version="2"
            type="information"
            content="Not started"
            aria-label="not started"
            icon="false"
          ></goa-badge>
        </td>
      </tr>
    </tbody>
  </table>
</goa-table>

<goa-text as="h2" mb="s">3. Schedule service</goa-text>
<goa-text size="body-s" color="secondary" mt="2xs"
  >You need to complete the previous section before you can start this task.</goa-text
>
<goa-table version="2" width="100%" mt="l" mb="3xl">
  <table style="width: 100%">
    <tbody>
      <tr>
        <td>Receive email confirmation</td>
        <td class="goa-table-number-column">
          <goa-badge
            version="2"
            type="default"
            content="Cannot start yet"
            aria-label="cannot start yet"
            icon="false"
          ></goa-badge>
        </td>
      </tr>
      <tr>
        <td>Pay service fee</td>
        <td class="goa-table-number-column">
          <goa-badge
            version="2"
            type="default"
            content="Cannot start yet"
            aria-label="cannot start yet"
            icon="false"
          ></goa-badge>
        </td>
      </tr>
    </tbody>
  </table>
</goa-table>

Workspace

Preview not available
No React code available

Types

Use variant='relaxed' for tables with longer content that needs more breathing room between rows.
All GoA Design System components are built to meet WCAG 2.2 AA standards. The following guidelines provide additional context for accessible implementation.

Screen Readers

NameDateStatusJohn SmithJan 15, 2024ActiveJane DoeFeb 20, 2024Pending
Use proper semantic HTML table structure (thead, tbody, tr, th, td) for accessibility.
View old component docs