Filter chip
Allow the user to enter information, filter content, and make selections.
Props
error
boolean
Shows an error state.
Defaults to
false.
content
string
Text label of the chip.
secondarytext
string
Secondary text displayed in a smaller size before the main content.
leadingIcon
GoAIconType
Icon displayed at the start of the chip.
testId
string
Sets a data-testid attribute for automated testing.
ariaLabel
string
Accessible label for the filter chip. Defaults to content with 'removable' suffix.
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
onClick
(event: Event) => void
_click
CustomEvent
Add a filter chip
const [activeFilters, setActiveFilters] = useState<string[]>([]);
const addFilter = () => {
const randomFilter = `Filter ${Math.floor(Math.random() * 100)}`;
if (!activeFilters.includes(randomFilter)) {
setActiveFilters((prevFilters) => [...prevFilters, randomFilter]);
}
};
const removeFilter = (filter: string) => {
setActiveFilters((prevFilters) => prevFilters.filter((f) => f !== filter));
};<div>
{activeFilters.map((filter) => (
<GoabxFilterChip
key={filter}
content={filter}
onClick={() => removeFilter(filter)}
mr="s"
mb="s"
mt="s"
/>
))}
</div>
<GoabxButton mt="l" onClick={addFilter}>Add Random Filter</GoabxButton>activeFilters: string[] = [];
removeFilter(filter: string): void {
this.activeFilters = this.activeFilters.filter((f) => f !== filter);
}
addFilter(): void {
const randomFilter = "Filter " + Math.floor(Math.random() * 100);
if (!this.activeFilters.includes(randomFilter)) {
this.activeFilters.push(randomFilter);
}
}<div>
@for (filter of activeFilters; track filter) {
<goabx-filter-chip
[content]="filter"
(onClick)="removeFilter(filter)"
mr="s"
mb="s"
mt="s">
</goabx-filter-chip>
}
</div>
<goabx-button mt="l" (onClick)="addFilter()">Add Random Filter</goabx-button>const activeFilters = [];
const container = document.getElementById("filter-container");
const addBtn = document.getElementById("add-filter-btn");
function renderFilters() {
container.innerHTML = "";
activeFilters.forEach((filter) => {
const chip = document.createElement("goa-filter-chip");
chip.setAttribute("version", "2");
chip.setAttribute("content", filter);
chip.setAttribute("mr", "s");
chip.setAttribute("mb", "s");
chip.setAttribute("mt", "s");
chip.addEventListener("_click", () => removeFilter(filter));
container.appendChild(chip);
});
}
function addFilter() {
const randomFilter = "Filter " + Math.floor(Math.random() * 100);
if (!activeFilters.includes(randomFilter)) {
activeFilters.push(randomFilter);
renderFilters();
}
}
function removeFilter(filter) {
const index = activeFilters.indexOf(filter);
if (index > -1) {
activeFilters.splice(index, 1);
renderFilters();
}
}
addBtn.addEventListener("_click", addFilter);<div id="filter-container"></div>
<goa-button version="2" mt="l" id="add-filter-btn">Add Random Filter</goa-button>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>Remove a filter
const [chips, setChips] = useState(["Chip 1", "Chip 2", "Chip 3"]);
const deleteChip = (chip: string) => {
setChips((prevChips) => prevChips.filter((c) => c !== chip));
};<div>
{chips.map((chip) => (
<GoabxFilterChip
key={chip}
content={chip}
onClick={() => deleteChip(chip)}
mr="s"
/>
))}
</div>chips: string[] = ["Chip 1", "Chip 2", "Chip 3"];
deleteChip(chip: string): void {
this.chips = this.chips.filter((c) => c !== chip);
}<div>
@for (chip of chips; track chip) {
<goabx-filter-chip
[content]="chip"
(onClick)="deleteChip(chip)"
mr="s">
</goabx-filter-chip>
}
</div>let chips = ["Chip 1", "Chip 2", "Chip 3"];
const container = document.getElementById("chip-container");
function renderChips() {
container.innerHTML = "";
chips.forEach((chip) => {
const chipEl = document.createElement("goa-filter-chip");
chipEl.setAttribute("version", "2");
chipEl.setAttribute("content", chip);
chipEl.setAttribute("mr", "s");
chipEl.addEventListener("_click", () => deleteChip(chip));
container.appendChild(chipEl);
});
}
function deleteChip(chip) {
chips = chips.filter((c) => c !== chip);
renderChips();
}
renderChips();<div id="chip-container"></div>Type to create a new filter
const [typedChips, setTypedChips] = useState<string[]>([]);
const [inputValue, setInputValue] = useState("");<GoabxFormItem label="Type to create a chip" mb="m">
<GoabxInput
name="chipInput"
value={inputValue}
onChange={(e) => setInputValue(e.value.trim())}
onKeyPress={(e) => {
if (e.key === "Enter" && e.value.trim()) {
setTypedChips([...typedChips, e.value.trim()]);
setTimeout(() => setInputValue(""), 0);
} else if (e.key === "Backspace" && !e.value.trim() && typedChips.length > 0) {
setTypedChips(typedChips.slice(0, -1));
}
}}
width="100%"
/>
</GoabxFormItem>
<div>
{typedChips.map((chip, index) => (
<GoabxFilterChip
key={index}
content={chip}
mb="xs"
mr="xs"
onClick={() => setTypedChips(typedChips.filter((c) => c !== chip))}
/>
))}
</div>typedChips: string[] = [];
inputValue = "";
handleInputChange(detail: GoabInputOnChangeDetail): void {
const newValue = detail.value.trim();
this.inputValue = newValue;
}
handleInputKeyPress(detail: GoabInputOnKeyPressDetail): void {
const newValue = detail.value.trim();
if (detail.key === "Enter" && newValue !== "") {
this.addChip();
} else if (!this.inputValue && this.typedChips.length > 0 && detail.key === "Backspace") {
this.typedChips.pop();
}
}
addChip(): void {
if (this.inputValue.trim()) {
this.typedChips.push(this.inputValue.trim());
this.inputValue = "";
}
}
removeTypedChip(chip: string): void {
this.typedChips = this.typedChips.filter((c) => c !== chip);
}<goabx-form-item label="Type to create a chip" mb="m">
<goabx-input
[value]="inputValue"
(onChange)="handleInputChange($event)"
(onKeyPress)="handleInputKeyPress($event)"
width="100%"
>
</goabx-input>
</goabx-form-item>
@if (typedChips.length > 0) {
<div>
@for (typedChip of typedChips; track typedChip) {
<goabx-filter-chip
[content]="typedChip"
mb="xs"
mr="xs"
(onClick)="removeTypedChip(typedChip)"
>
</goabx-filter-chip>
}
</div>
}const typedChips = [];
const input = document.getElementById("chip-input");
const addBtn = document.getElementById("add-btn");
const container = document.getElementById("chips-container");
let currentValue = "";
function renderChips() {
container.innerHTML = "";
typedChips.forEach((chip) => {
const chipEl = document.createElement("goa-filter-chip");
chipEl.setAttribute("version", "2");
chipEl.setAttribute("content", chip);
chipEl.setAttribute("mb", "xs");
chipEl.setAttribute("mr", "xs");
chipEl.addEventListener("_click", () => removeChip(chip));
container.appendChild(chipEl);
});
}
function addChip() {
if (currentValue.trim()) {
typedChips.push(currentValue.trim());
currentValue = "";
input.setAttribute("value", "");
renderChips();
}
}
function removeChip(chip) {
const index = typedChips.indexOf(chip);
if (index > -1) {
typedChips.splice(index, 1);
renderChips();
}
}
input.addEventListener("_change", (e) => {
currentValue = e.detail.value || "";
});
addBtn.addEventListener("_click", () => {
addChip();
});<goa-form-item version="2" label="Type to create a chip" mb="m">
<goa-block gap="xs" direction="row">
<div style="flex: 1">
<goa-input version="2" id="chip-input" width="100%"></goa-input>
</div>
<goa-button version="2" id="add-btn" type="secondary">Add</goa-button>
</goa-block>
</goa-form-item>
<div id="chips-container"></div>