Components Forms
FilterChip
StableA filter control showing its name and current value, editable in a popover.
FilterChip packs a filter into one compact control. When inactive it reads "+ Status". When active it shows the selected value and a clear button. Put any editor you like inside the popover, such as checkboxes, a date range or a search list. Combine several in a FilterBar.
Overview
import * as React from 'react';
import { FilterChip } from '@bindshq/stance-react';
const statuses = ['Signed', 'Awaiting signature', 'Expired', 'Draft'];
export function Basic() {
const [selected, setSelected] = React.useState<string[]>(['Signed']);
const summary =
selected.length > 2 ? `${selected[0]} +${selected.length - 1}` : selected.join(', ');
return (
<FilterChip label="Status" value={summary} onClear={() => setSelected([])}>
<fieldset style={{ border: 0, margin: 0, padding: 0, display: 'grid', gap: 8 }}>
<legend style={{ fontWeight: 600, marginBottom: 8 }}>Contract status</legend>
{statuses.map((s) => (
<label key={s} style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
<input
type="checkbox"
checked={selected.includes(s)}
onChange={(e) =>
setSelected((all) => (e.target.checked ? [...all, s] : all.filter((x) => x !== s)))
}
/>
{s}
</label>
))}
</fieldset>
</FilterChip>
);
}Light, dark and high contrast
Every Stance component is designed and tested in all three appearance modes.
Import
import { FilterChip } from '@bindshq/stance-react';Examples
import { FilterChip } from '@bindshq/stance-react';
export function States() {
return (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
<FilterChip label="Owner" />
<FilterChip label="Department" value="Sales" onClear={() => {}} />
<FilterChip label="Created" value="Last 30 days" onClear={() => {}} disabled />
</div>
);
}import { Button, FilterChip } from '@bindshq/stance-react';
export function Sizes() {
return (
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<FilterChip size="sm" label="Template" value="Executive" onClear={() => {}}>
<Button size="sm" variant="outline">
Choose template
</Button>
</FilterChip>
<FilterChip label="Template" value="Executive" onClear={() => {}}>
<Button size="sm" variant="outline">
Choose template
</Button>
</FilterChip>
</div>
);
}API reference
| Prop | Type | Default | Description |
|---|---|---|---|
label* required | string | None | Filter name, e.g. "Status". |
value | ReactNode | None | Summary of the selection; empty means inactive. |
children | ReactNode | None | Editor rendered in the popover. |
onClear | () => void | None | Shows a clear button while active. |
open | boolean | None | Controlled popover state. |
defaultOpen | boolean | None | Initial popover state. |
onOpenChange | (open: boolean) => void | None | Called when the popover opens or closes. |
contentProps | PopoverContentProps | None | Props for the popover surface. |
size | 'sm' | 'md' | 'md' | Control height. |
disabled | boolean | false | Disables the trigger and clear button. |
Accessibility
- The trigger is a button with aria-expanded. Its name includes the filter and value, e.g. "Status: Signed, Pending".
- The popover is a labelled dialog ("Status filter") that returns focus to the trigger on close.
- The clear button is a separate button labelled "Clear Status filter", never nested inside the trigger.
- Inactive filters use a dashed border plus a plus icon, so state is not shown by colour alone.
Keyboard
| Keys | Action |
|---|---|
| Enter Space | Opens the filter editor. |
| Escape | Closes the editor and returns focus. |
| Tab | Moves from the trigger to the clear button. |
Usage guidelines
Do
- Summarise long selections, e.g. "Signed +2".
Don’t
- Apply changes on every keystroke when results are slow; add an Apply button.