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

<GoabxTable 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>
    </GoabxTable>
// No logic required for this static example
<goabx-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>
</goabx-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 as="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 as="span" size="heading-xs" mt="none" mb="none">Email</GoabText>
            <GoabText as="span" size="heading-xs" mt="none" mb="none">Phone</GoabText>
          </GoabBlock>
          <GoabBlock direction="column" gap="m">
            <GoabText as="span" size="body-m" mt="none" mb="none">tracyhero@email.com</GoabText>
            <GoabText as="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={
          <GoabxButton
            type="tertiary"
            size="compact"
            leadingIcon="calendar"
            onClick={handleAddToCalendar}>
            Add to calendar
          </GoabxButton>
        }>
        <GoabxTable 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>
        </GoabxTable>
      </GoabContainer>
onAddToCalendar(): void {
    console.log("Add to calendar clicked");
  }
<goab-container>
  <goab-text as="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 as="span" size="heading-xs" mt="none" mb="none">Email</goab-text>
      <goab-text as="span" size="heading-xs" mt="none" mb="none">Phone</goab-text>
    </goab-block>
    <goab-block direction="column" gap="m">
      <goab-text as="span" size="body-m" mt="none" mb="none">tracyhero&#64;email.com</goab-text>
      <goab-text as="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">
    <goabx-button type="tertiary" size="compact" leadingIcon="calendar" (onClick)="onAddToCalendar()">
      Add to calendar
    </goabx-button>
  </div>
  <goabx-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>
  </goabx-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 && (
            <GoabxButton
              type="secondary"
              size="compact"
              leadingIcon="filter"
              onClick={() => setOpen(true)}
            >
              Filters
            </GoabxButton>
          )}
        </div>

        <GoabxTable 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>
                  <GoabxBadge type="success" content="Completed" />
                </td>
                <td>Gilbert Barton</td>
                <td>24567-9876</td>
                <td>Traffic safety act</td>
              </tr>
              <tr>
                <td>
                  <GoabxBadge type="information" content="New" />
                </td>
                <td>Brynn Hurley</td>
                <td>98765-3456</td>
                <td>Trespass to premises act</td>
              </tr>
              <tr>
                <td>
                  <GoabxBadge type="midtone" content="In review" />
                </td>
                <td>Marco Silva</td>
                <td>34521-7890</td>
                <td>Gaming, liquor, and cannabis act</td>
              </tr>
              <tr>
                <td>
                  <GoabxBadge type="success" content="Completed" />
                </td>
                <td>Dana Chen</td>
                <td>55123-4567</td>
                <td>Traffic safety act</td>
              </tr>
              <tr>
                <td>
                  <GoabxBadge type="information" content="New" />
                </td>
                <td>Amira Hassan</td>
                <td>67890-1234</td>
                <td>Trespass to premises act</td>
              </tr>
            </tbody>
          </table>
        </GoabxTable>
      </div>

      <GoabxPushDrawer
        heading="Filters"
        width="260px"
        open={open}
        onClose={() => setOpen(false)}
      >
        <GoabxFormItem label="Act">
          <GoabxCheckboxList name="act" onChange={() => {}}>
            <GoabxCheckbox name="traffic" text="Traffic safety act" size="compact" />
            <GoabxCheckbox
              name="gaming"
              text="Gaming, liquor, and cannabis act"
              size="compact"
            />
            <GoabxCheckbox
              name="trespass"
              text="Trespass to premises act"
              size="compact"
            />
          </GoabxCheckboxList>
        </GoabxFormItem>
        <GoabxFormItem label="Status" mt="l">
          <GoabxDropdown name="status" onChange={() => {}} value="" size="compact">
            <GoabxDropdownItem value="" label="All statuses" />
            <GoabxDropdownItem value="new" label="New" />
            <GoabxDropdownItem value="in-review" label="In review" />
            <GoabxDropdownItem value="completed" label="Completed" />
          </GoabxDropdown>
        </GoabxFormItem>
      </GoabxPushDrawer>
    </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="midtone" 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]);
<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>

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>
      <GoabxTable mt="l">
        <tbody>
          <tr>
            <td>
              <strong>What was your (the applicant's) relationship to the deceased?</strong>
            </td>
            <td>Other</td>
            <td>
              <GoabxLink>Change</GoabxLink>
            </td>
          </tr>
          <tr>
            <td>
              <strong>My relationship to the deceased was</strong>
            </td>
            <td>Manager</td>
            <td>
              <GoabxLink>Change</GoabxLink>
            </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>
              <GoabxLink>Change</GoabxLink>
            </td>
          </tr>
          <tr>
            <td>
              <strong>Was the deceased a minor?</strong>
            </td>
            <td>No</td>
            <td>
              <GoabxLink>Change</GoabxLink>
            </td>
          </tr>
          <tr>
            <td>
              <strong>What was the deceased's marital status at time of death?</strong>
            </td>
            <td>Married</td>
            <td>
              <GoabxLink>Change</GoabxLink>
            </td>
          </tr>
          <tr>
            <td>
              <strong>Did the deceased have any dependents?</strong>
            </td>
            <td>No</td>
            <td>
              <GoabxLink>Change</GoabxLink>
            </td>
          </tr>
          <tr>
            <td>
              <strong>Was the deceased a sponsored immigrant?</strong>
            </td>
            <td>Yes</td>
            <td>
              <GoabxLink>Change</GoabxLink>
            </td>
          </tr>
        </tbody>
      </GoabxTable>
      <GoabButtonGroup alignment="start" mt="2xl">
        <GoabxButton type="primary">Confirm and continue</GoabxButton>
        <GoabxButton type="tertiary">Back to application overview</GoabxButton>
      </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>
<goabx-table mt="l">
  <tbody>
    <tr>
      <td><strong>What was your (the applicant's) relationship to the deceased?</strong></td>
      <td>Other</td>
      <td><goabx-link>Change</goabx-link></td>
    </tr>
    <tr>
      <td><strong>My relationship to the deceased was</strong></td>
      <td>Manager</td>
      <td><goabx-link>Change</goabx-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><goabx-link>Change</goabx-link></td>
    </tr>
    <tr>
      <td><strong>Was the deceased a minor?</strong></td>
      <td>No</td>
      <td><goabx-link>Change</goabx-link></td>
    </tr>
    <tr>
      <td><strong>What was the deceased's marital status at time of death?</strong></td>
      <td>Married</td>
      <td><goabx-link>Change</goabx-link></td>
    </tr>
    <tr>
      <td><strong>Did the deceased have any dependents?</strong></td>
      <td>No</td>
      <td><goabx-link>Change</goabx-link></td>
    </tr>
    <tr>
      <td><strong>Was the deceased a sponsored immigrant?</strong></td>
      <td>Yes</td>
      <td><goabx-link>Change</goabx-link></td>
    </tr>
  </tbody>
</goabx-table>
<goab-button-group alignment="start" mt="2xl">
  <goabx-button type="primary" (onClick)="onConfirmClick()">Confirm and continue</goabx-button>
  <goabx-button type="tertiary" (onClick)="onBackClick()">Back to application overview</goabx-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];
<GoabxTabs initialTab={2}>
      <GoabTab heading="All">
        <GoabxTable 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>
                  <GoabxBadge type="important" content="Review pending" />
                </td>
                <td>Lorem Ipsum</td>
                <td className="goa-table-number-column">1234567890</td>
                <td>
                  <GoabxButton type="tertiary" size="compact">Action</GoabxButton>
                </td>
              </tr>
            ))}
            {complete.map((i) => (
              <tr key={`complete-${i}`}>
                <td>
                  <GoabxBadge type="information" content="Complete" />
                </td>
                <td>Lorem Ipsum</td>
                <td className="goa-table-number-column">1234567890</td>
                <td>
                  <GoabxButton type="tertiary" size="compact">Action</GoabxButton>
                </td>
              </tr>
            ))}
          </tbody>
        </GoabxTable>
      </GoabTab>
      <GoabTab heading={<>Review pending<GoabxBadge type="important" content="4" icon={false} /></>}>
        <GoabxTable 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>
                  <GoabxBadge type="important" content="Review pending" />
                </td>
                <td>Lorem Ipsum</td>
                <td className="goa-table-number-column">1234567890</td>
                <td>
                  <GoabxButton type="tertiary" size="compact">Action</GoabxButton>
                </td>
              </tr>
            ))}
          </tbody>
        </GoabxTable>
      </GoabTab>
      <GoabTab heading={<>Complete<GoabxBadge type="information" content="338" icon={false} /></>}>
        <GoabxTable 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>
                  <GoabxBadge type="information" content="Complete" />
                </td>
                <td>Lorem Ipsum</td>
                <td className="goa-table-number-column">1234567890</td>
                <td>
                  <GoabxButton type="tertiary" size="compact">Action</GoabxButton>
                </td>
              </tr>
            ))}
          </tbody>
        </GoabxTable>
      </GoabTab>
    </GoabxTabs>
review = [0, 1, 2, 3];
  complete = [0, 1];
<goabx-tabs [initialTab]="2">
  <goab-tab heading="All">
    <goabx-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>
            <goabx-badge type="important" content="Review pending"></goabx-badge>
          </td>
          <td>Lorem Ipsum</td>
          <td class="goa-table-number-column">1234567890</td>
          <td>
            <goabx-button type="tertiary" size="compact">Action</goabx-button>
          </td>
        </tr>
        } @for (i of complete; track $index) {
        <tr>
          <td>
            <goabx-badge type="information" content="Complete"></goabx-badge>
          </td>
          <td>Lorem Ipsum</td>
          <td class="goa-table-number-column">1234567890</td>
          <td>
            <goabx-button type="tertiary" size="compact">Action</goabx-button>
          </td>
        </tr>
        }
      </tbody>
    </goabx-table>
  </goab-tab>
  <goab-tab [heading]="reviewPending">
    <ng-template #reviewPending
      >Review pending<goabx-badge
        type="important"
        content="4"
        [icon]="false"
      ></goabx-badge
    ></ng-template>
    <goabx-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>
            <goabx-badge type="important" content="Review pending"></goabx-badge>
          </td>
          <td>Lorem Ipsum</td>
          <td class="goa-table-number-column">1234567890</td>
          <td>
            <goabx-button type="tertiary" size="compact">Action</goabx-button>
          </td>
        </tr>
        }
      </tbody>
    </goabx-table>
  </goab-tab>
  <goab-tab [heading]="completeTemplate">
    <ng-template #completeTemplate
      >Complete<goabx-badge type="information" content="338" [icon]="false"></goabx-badge
    ></ng-template>
    <goabx-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>
            <goabx-badge type="information" content="Complete"></goabx-badge>
          </td>
          <td>Lorem Ipsum</td>
          <td class="goa-table-number-column">1234567890</td>
          <td>
            <goabx-button type="tertiary" size="compact">Action</goabx-button>
          </td>
        </tr>
        }
      </tbody>
    </goabx-table>
  </goab-tab>
</goabx-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];
<GoabxTabs initialTab={1}>
          <GoabTab heading="All">
            <GoabxTable 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>
                      <GoabxBadge type="important" content="Review pending" />
                    </td>
                    <td>Lorem Ipsum</td>
                    <td className="goa-table-number-column">1234567890</td>
                    <td>
                      <GoabxButton type="tertiary" size="compact">Action</GoabxButton>
                    </td>
                  </tr>
                ))}
                {complete.map((i) => (
                  <tr key={`complete-${i}`}>
                    <td>
                      <GoabxBadge type="information" content="Complete" />
                    </td>
                    <td>Lorem Ipsum</td>
                    <td className="goa-table-number-column">1234567890</td>
                    <td>
                      <GoabxButton type="tertiary" size="compact">Action</GoabxButton>
                    </td>
                  </tr>
                ))}
              </tbody>
            </GoabxTable>
          </GoabTab>
          <GoabTab
            heading={
                Review pending
                <GoabxBadge type="important" content="4" icon={false} />
            }
          >
            <GoabxTable 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>
                      <GoabxBadge type="important" content="Review pending" />
                    </td>
                    <td>Lorem Ipsum</td>
                    <td className="goa-table-number-column">1234567890</td>
                    <td>
                      <GoabxButton type="tertiary" size="compact">Action</GoabxButton>
                    </td>
                  </tr>
                ))}
              </tbody>
            </GoabxTable>
          </GoabTab>
          <GoabTab
            heading={
              <>
                Complete
                <GoabxBadge type="information" content="338" icon={false} />
              </>
            }
          >
            <GoabxTable 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>
                      <GoabxBadge type="information" content="Complete" />
                    </td>
                    <td>Lorem Ipsum</td>
                    <td className="goa-table-number-column">1234567890</td>
                    <td>
                      <GoabxButton type="tertiary" size="compact">Action</GoabxButton>
                    </td>
                  </tr>
                ))}
              </tbody>
            </GoabxTable>
          </GoabTab>
    </GoabxTabs>
reviewItems = [0, 1, 2, 3];
  completeItems = [0, 1];
<goabx-tabs>
  <goab-tab heading="All">
    <goabx-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><goabx-badge type="important" content="Review pending"></goabx-badge></td>
          <td>Lorem ipsum</td>
          <td class="goa-table-number-column">1234567890</td>
          <td><goabx-button type="tertiary" size="compact">Action</goabx-button></td>
        </tr>
        }
        @for (i of completeItems; track $index) {
        <tr>
          <td><goabx-badge type="information" content="Complete"></goabx-badge></td>
          <td>Lorem Ipsum</td>
          <td class="goa-table-number-column">1234567890</td>
          <td><goabx-button type="tertiary" size="compact">Action</goabx-button></td>
        </tr>
        }
      </tbody>
    </goabx-table>
  </goab-tab>
  <goab-tab [heading]="reviewPending">
    <ng-template #reviewPending>Review pending<goabx-badge type="important" content="4" [icon]="false"></goabx-badge></ng-template>
    <goabx-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><goabx-badge type="important" content="Review pending"></goabx-badge></td>
          <td>Lorem ipsum</td>
          <td class="goa-table-number-column">1234567890</td>
          <td><goabx-button type="tertiary" size="compact">Action</goabx-button></td>
        </tr>
        }
      </tbody>
    </goabx-table>
  </goab-tab>
  <goab-tab [heading]="completeTab">
    <ng-template #completeTab>Complete<goabx-badge type="information" content="338" [icon]="false"></goabx-badge></ng-template>
    <goabx-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><goabx-badge type="information" content="Complete"></goabx-badge></td>
          <td>Lorem Ipsum</td>
          <td class="goa-table-number-column">1234567890</td>
          <td><goabx-button type="tertiary" size="compact">Action</goabx-button></td>
        </tr>
        }
      </tbody>
    </goabx-table>
  </goab-tab>
</goabx-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: "dark", 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 },
  ];
<GoabxTable 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>
              <GoabxBadge
                type={row.status as "information" | "dark" | "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>
    </GoabxTable>
rows: TableRow[] = [
    { status: "information", statusText: "In progress", name: "Darlene Robertson", id: 45904 },
    { status: "dark", 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 },
  ];
<goabx-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>
        <goabx-badge [type]="row.status" [content]="row.statusText" [icon]="false"></goabx-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>
</goabx-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="dark" 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, setUsers] = useState<User[]>([]);
  const [pageUsers, setPageUsers] = useState<User[]>([]);
  const [page, setPage] = useState<number>(1);
  const [perPage, setPerPage] = useState<number>(10);

  useEffect(() => {
    // Generate sample data
    const firstNames = ["Emma", "Liam", "Olivia", "Noah", "Ava", "James", "Sophia", "William", "Isabella", "Oliver", "Mia", "Benjamin", "Charlotte", "Elijah", "Amelia", "Lucas", "Harper", "Mason", "Evelyn", "Logan"];
    const lastNames = ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez", "Wilson", "Anderson", "Taylor", "Thomas", "Moore", "Jackson", "Martin", "Lee", "Thompson", "White"];
    const _users: User[] = [];
    for (let i = 1; i <= 100; i++) {
      _users.push({
        id: `user-${i}`,
        firstName: firstNames[(i - 1) % firstNames.length],
        lastName: lastNames[(i - 1) % lastNames.length],
        age: 20 + (i % 40),
      });
    }
    setUsers(_users);
    setPageUsers(_users.slice(0, perPage));
  }, [perPage]);

  function changePage(newPage: number) {
    const offset = (newPage - 1) * perPage;
    const _users = users.slice(offset, offset + perPage);
    setPage(newPage);
    setPageUsers(_users);
  }

  function handlePerPageCountChangeEvent(event: GoabDropdownOnChangeDetail) {
    const perPageValue = parseInt(event.value || "10");
    setPage(1);
    setPerPage(perPageValue);
    const _users = users.slice(0, perPageValue);
    setPageUsers(_users);
  }
<GoabxTable 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>
      </GoabxTable>

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

  constructor() {
    this.pageUsers = this.prepareUsers().slice(0, this.perPage);
  }

  prepareUsers(): 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"];
    for (let i = 1; i <= this.total; i++) {
      this.users.push({
        id: `user-${i}`,
        firstName: firstNames[(i - 1) % firstNames.length],
        lastName: lastNames[(i - 1) % lastNames.length],
        age: 20 + (i % 40),
      });
    }
    return this.users;
  }

  handlePageChange(event: GoabPaginationOnChangeDetail): void {
    this.page = event.page;
    const offset = (this.page - 1) * this.perPage;
    this.pageUsers = this.users.slice(offset, offset + this.perPage);
  }

  handlePerPageCountChangeEvent(event: GoabDropdownOnChangeDetail): void {
    this.page = 1;
    this.perPage = Number(event.value);
    this.pageUsers = this.users.slice(0, this.perPage);
  }
<goabx-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>
</goabx-table>

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

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

  <goabx-pagination
    [itemCount]="users.length"
    [perPageCount]="perPage"
    [pageNumber]="page"
    (onChange)="handlePageChange($event)">
  </goabx-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: "midtone", content: "Closed" },
    { key: 6, type: "success", content: "Complete" },
  ];

  const handleClick = () => {
    console.log("clicked");
  };
<GoabxTable 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>
              <GoabxBadge 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>
              <GoabxButton size="compact" type="tertiary" onClick={handleClick}>
                Assign
              </GoabxButton>
            </td>
          </tr>
        ))}
      </tbody>
    </GoabxTable>
badgeValues: BadgeValue[] = [
    { type: "important", content: "Pending" },
    { type: "emergency", content: "Failed" },
    { type: "success", content: "Complete" },
    { type: "information", content: "In progress" },
    { type: "midtone", content: "Closed" },
    { type: "success", content: "Complete" },
  ];

  onClick(): void {
    console.log("clicked");
  }
<goabx-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>
        <goabx-badge [type]="badge.type" [content]="badge.content" [icon]="false"></goabx-badge>
      </td>
      <td>Lorem ipsum dolor sit amet consectetur.</td>
      <td class="goa-table-number-column">1234567890</td>
      <td>
        <goabx-button size="compact" type="tertiary" (onClick)="onClick()">Assign</goabx-button>
      </td>
    </tr>
    }
  </tbody>
</goabx-table>
const badgeValues = [
  { type: "important", content: "Pending" },
  { type: "emergency", content: "Failed" },
  { type: "success", content: "Complete" },
  { type: "information", content: "In progress" },
  { type: "midtone", 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) => {
<GoabxTable onSort={sortData} width="100%">
      <thead>
        <tr>
          <th>
            <GoabxTableSortHeader name="firstName">First name</GoabxTableSortHeader>
          </th>
          <th>
            <GoabxTableSortHeader name="lastName">Last name</GoabxTableSortHeader>
          </th>
          <th>
            <GoabxTableSortHeader name="age" direction="asc">
              Age
            </GoabxTableSortHeader>
          </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>
    </GoabxTable>
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
    );
  }
<goabx-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>
</goabx-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 as="h1" mt="none">Apply for a service</GoabText>
      <GoabxCallout
        type="important"
        emphasis="low"
        size="medium"
        heading="Application incomplete"
        mb="2xl"
        mt="xl"
        maxWidth="360px"
      >
        You have completed 1 of 3 sections.
      </GoabxCallout>

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

      <GoabText as="h2">2. Prepare application</GoabText>
      <GoabxTable width="100%" mb="2xl" mt="l">
        <tbody>
          <tr>
            <td>
              <a href="#">Your contact details</a>
            </td>
            <td className="goa-table-number-column">
              <GoabxBadge 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">
              <GoabxBadge 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">
              <GoabxBadge type="information" content="Not started" ariaLabel="not started" icon={false} />
            </td>
          </tr>
        </tbody>
      </GoabxTable>

      <GoabText as="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>
      <GoabxTable width="100%" mt="l" mb="3xl">
        <tbody>
          <tr>
            <td>Receive email confirmation</td>
            <td className="goa-table-number-column">
              <GoabxBadge type="light" content="Cannot start yet" ariaLabel="cannot start yet" icon={false} />
            </td>
          </tr>
          <tr>
            <td>Pay service fee</td>
            <td className="goa-table-number-column">
              <GoabxBadge type="light" content="Cannot start yet" ariaLabel="cannot start yet" icon={false} />
            </td>
          </tr>
        </tbody>
      </GoabxTable>
<goab-text as="h1" mt="none">Apply for a service</goab-text>
<goabx-callout type="important" emphasis="low" size="medium" heading="Application incomplete" mb="2xl" mt="xl" maxWidth="360px">
  You have completed 1 of 3 sections.
</goabx-callout>

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

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

<goab-text as="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>
<goabx-table width="100%" mt="l" mb="3xl">
  <tbody>
    <tr>
      <td>Receive email confirmation</td>
      <td class="goa-table-number-column">
        <goabx-badge type="light" content="Cannot start yet" ariaLabel="cannot start yet" [icon]="false"></goabx-badge>
      </td>
    </tr>
    <tr>
      <td>Pay service fee</td>
      <td class="goa-table-number-column">
        <goabx-badge type="light" content="Cannot start yet" ariaLabel="cannot start yet" [icon]="false"></goabx-badge>
      </td>
    </tr>
  </tbody>
</goabx-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="light" 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="light" 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

No usage guidelines have been documented for this component yet.

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.