Documentation Index
Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
Use this file to discover all available pages before exploring further.
Implementation
FilterBar Props
Boolean Filter Props
Single Select Filter Props
Multi Select Filter Props
Single Date Selection Filter Props
Ranged Date Selection Filter Props
Custom Filter Props
Beta FeatureThis feature is currently in beta, and needs to be imported from @servicetitan/anvil2/beta.While we hope to minimize breaking changes, they may occur due to feedback we receive or other improvements. These will always be documented in the changelog and communicated in Slack.Please reach out in the #ask-designsystem channel with any questions or feedback! Common Examples
Standard Filters
import { FilterBar } from "@servicetitan/anvil2/beta";
function ExampleComponent() {
const filters = [
// See filter objects below
];
return (
<FilterBar
associatedContent="..."
filters={filters}
onFilterChange={(updatedFilters) => console.log(updatedFilters)}
/>
);
}
A filter group is created by passing a filters object array to the FilterBar component. The component takes the object and builds the set of filters for you according to the design patterns. You can create a custom filter if needed, but there are currently 5 available prebuilt filter types to pick from:Boolean
{
type: "boolean",
id: "booleanFilterId",
label: "Boolean Filter",
checked: false,
}
Single Select
The item list for single select is populated and controlled by the implementing team. This includes any filtering that needs to be done when a search field is present.Simple Example{
type: "singleSelect",
id: "singleSelectFilterId",
label: "Single Select Filter",
items: [
{
id: "singleOption1",
label: "Single Option 1"
},
...
],
simpleDrawerVariant
}
With Search ExampleSince population of the item list is controlled by implementing team, the Single Select search handler needs to have an empty search term conditional in it so the filter drawer draft state can properly reset the displayed items after selection but before applying the batch update.{
type: "singleSelect",
id: "singleSelectFilterId",
label: "Single Select Filter",
items: [
{
id: "singleOption1",
label: "Single Option 1"
},
...
],
hasSearch: true,
onSearch: (searchTerm) => {
if (!searchTerm) {
setSingleSelectOptions(statusOptions);
} else {
const filtered = statusOptions.filter((option) =>
option.label.toLowerCase().includes(searchTerm.toLowerCase()));
setSingleSelectOptions(filtered);
}
},
onSearchClear: handleMultiselectSearchClear,
}
Multi Select
The item list for single select is populated and controlled by the implementing team. This includes any filtering that needs to be done when a search field is present.Simple Example{
type: "multiSelect",
id: "multiSelectFilterId",
label: "Multi Select Filter",
items: [
{
id: "multiOption1",
label: "Multi Option 1"
},
...
],
simpleDrawerVariant
}
With Search ExampleSince population of the item list is controlled by implementing team, the Single Select search handler needs to have an empty search term conditional in it so the filter drawer draft state can properly reset the displayed items after selection but before applying the batch update.{
type: "multiSelect",
id: "multiSelectFilterId",
label: "Multi Select Filter",
items: [
{
id: "multiOption1",
label: "Multi Option 1"
},
...
],
selectedItems: [],
hasSearch: true,
onSearch: (searchTerm) => {
if (!searchTerm) {
setMultiSelectOptions(statusOptions);
} else {
const filtered = statusOptions.filter((option) =>
option.label.toLowerCase().includes(searchTerm.toLowerCase()));
setMultiSelectOptions(filtered);
}
},,
onSearchClear: handleMultiselectSearchClear,
}
Single Date Selection
{
type: "date",
id: "dateFilterId",
label: "Date Filter",
mode: "mm/dd/yyyy",
}
Range Date Selection
{
type: "dateRange",
id: "dateRangeFilterId",
label: "Date Range Filter",
mode: "mm/dd/yyyy",
}
These filter types come with preset filter drawer variants.Custom Filters
import { FilterBar } from "@servicetitan/anvil2/beta";
function ExampleComponent() {
const [labelText, setLabelText] = useState("");
const buttonComponent = ({ value, onChange }) => (
<YourComponentHere value={value} onChange={onChange} />
))};
const drawerComponent = ({ value, onChange }) => (
<YourComponentHere value={value} onChange={onChange} />
))};
const filters = [
{
type: "custom",
id: "customFilterId",
label: labelText,
buttonRender: buttonComponent,
drawerRender: renderComponent
}
];
return(
<FilterBar
associatedContent="..."
filters={filters}
onFilterChange={
(updateFilters) => {
console.log(updateFilters);
setLabelText(....);
}
}
/>
)
}
Provide a buttonRender, drawerRender, and custom label to create a custom filter. Label’s for custom filters can either be a string or a string with a chip count. Custom filters can be used by themselves or as part of a combined filter array.Filters with Apply/Cancel Triggers
import { FilterBar } from "@servicetitan/anvil2/beta";
function ExampleComponent() {
const filters = [
...
];
return (
<FilterBar
associatedContent="..."
filters={filters}
onFilterChange={
(updatedFilters) => console.log(updatedFilters)
}
controlledFiltering
/>
)
}
By default, filters update as soon as a selection is made in the filter button. When controlledFiltering is set to true, Apply and Cancel buttons appear at the bottom of single select, multi select, single date selection, range date selection, and custom filter button popovers to control updating. This setting has no impact on the Filter Drawer, which always submits as a batch update upon clicking Apply.Filters with Search Field
import { FilterBar } from "@servicetitan/anvil2/beta";
import { Toolbar } from "@servicetitan/anvil2/beta";
import { Flex } from "@servicetitan/anvil2";
function ExampleComponent() {
const filters = [
...
];
const handleToolbarSearch = (e) => {
...
}
const handleToolbarSearchClear = () => {
...
}
return (
<Flex direction="column" gap={2}>
<Toolbar associatedContent="...">
<Toolbar.Search
placeholder="Search..."
onChange={handleToolbarSearch}
onClear={handleToolbarSearchClear}
/>
</Toolbar>
<FilterBar
associatedContent="..."
filters={filters}
onFilterChange={
(updatedFilters) => console.log(updatedFilters)
}
/>
</Flex>
)
}
Use a Toolbar.Search alongside FilterBar to include a search input with your filters. The search bar works independently from the filters themselves and has its own search props.import { FilterBar } from "@servicetitan/anvil2/beta";
import { Toolbar } from "@servicetitan/anvil2/beta";
import { Flex } from "@servicetitan/anvil2";
function ExampleComponent() {
const filters = [
...
];
return (
<Flex direction="column" gap={2}>
<FilterBar
associatedContent="..."
filters={filters}
onFilterChange={
(updatedFilters) => console.log(updatedFilters)
}
/>
<Toolbar associatedContent="...">
<Toolbar.ControlGroup>
<Toolbar.Button
icon={{ before: AttachFile }}
aria-label="File button"
...
/>
// other base toolbar controls
</Toolbar.ControlGroup>
</Toolbar>
</Flex>
)
}
Use FilterBar alongside a Toolbar with Toolbar control options to have additional actions. The FilterBar and Toolbar are rendered as siblings and can be composed with Flex for layout.Overflow Modes
import { FilterBar } from "@servicetitan/anvil2/beta";
function ExampleComponent() {
const filters = [
...
];
return (
<>
// Default wrap mode
<FilterBar
associatedContent="..."
filters={filters}
onFilterChange={
(updatedFilters) => console.log(updatedFilters)
}
/>
// Collapsed overflow mode
<FilterBar
associatedContent="..."
overflow="collapse"
filters={filters}
onFilterChange={
(updatedFilters) => console.log(updatedFilters)
}
/>
</>
)
}
The FilterBar overflow mode defaults to wrap. Set overflow="collapse" to collapse filters that do not fit within the available space.In collapse mode, a hidden filter does not get added to an overflow menu, it is only removed from the screen. All filters remain accessible through the filter drawer in any mode.React Accessibility
Aria Labels
Add an associatedContent prop to the FilterBar for proper group identification for screen readers. The associatedContent value provides a11y context, generating labels like “Filters for ”.import { FilterBar } from "@servicetitan/anvil2/beta";
function ExampleComponent() {
return (
<FilterBar
associatedContent="..."
filters={filters}
controlledFiltering={false}
onFilterChange={(filters) => console.log(filters)}
/>
); }
FilterBar Props
Describes the content being filtered for accessibility context. Used to generate aria-labels.
Union type of all available filter objects.
onFilterChange
(filters: Filter[]) => void
required
Callback function for when filters change.
When enabled, filter button submission is controlled via an apply button.
overflow
"wrap" | "collapse"
default:"wrap"
Controls how filters behave when they exceed the available horizontal space.
size
"xsmall" | "small" | "medium" | "large"
default:"xsmall"
Controls the size of the filter bar and its filter buttons.
{
type: "boolean",
id: "booleanFilterId",
label: "Boolean Filter",
checked: false,
}
Boolean Filter Props
To have a boolean filter, you must have type: "boolean" in the filter object in the array.To have a boolean filter, this type must be in the object.
Selected state of the filter
Unique identifier for filter
{
type: "singleSelect",
id: "singleSelectFilterId",
label: "Single Select Filter",
items: [{ id: "option1", label: "Option 1" }],
hasSearch: false,
simpleDrawerVariant: false,
}
Single Select Filter Props
To have a single select filter, you must have type: "singleSelect" in the filter object in the array.Identifies this as a single select filter
Unique identifier for the filter
Display label for the filter
Array of items to select from. The Item type defaults to { id: string; label: string } but can be extended with custom properties as needed.
Whether to show the search field
onSearch
(searchTerm: string) => void
Callback for search functionality (only used when hasSearch is true)
Callback when search is cleared (only used when hasSearch is true)
Whether to use simplified drawer rendering (not available when hasSearch is
true)
{
type: "multiSelect",
id: "multiSelectFilterId",
label: "Multi Select Filter",
items: [{ id: "option1", label: "Option 1" }],
selectedItems: [],
hasSearch: false,
simpleDrawerVariant: false,
}
Multi Select Filter Props
To have a multi select filter, you must have type: "multiSelect" in the filter object in the array.Identifies this as a multi select filter
Unique identifier for the filter
Display label for the filter
Array of items to select from. The Item type defaults to { id: string; label: string } but can be extended with custom properties as needed.
Currently selected items array
Whether to show the search field
onSearch
(searchTerm: string) => void
Callback for search functionality (only used when hasSearch is true)
Callback when search is cleared (only used when hasSearch is true)
Whether to use simplified drawer rendering (not available when hasSearch is
true)
{
type: "date",
id: "dateFilterId",
label: "Date Filter",
mode: "mm/dd/yyyy",
value: null,
}
Single Date Selection Filter Props
To have a single date selection filter, you must have type: "date" in the filter object in the array.Identifies this as a single date filter
Unique identifier for the filter
Display label for the filter
mode
"mm/dd/yyyy" | "dd/mm/yyyy"
default:"mm/dd/yyyy"
Date format for the input.
value
Date | null
default:"null"
Currently selected date value.
{
type: "dateRange",
id: "dateRangeFilterId",
label: "Date Range Filter",
mode: "mm/dd/yyyy",
value: { start: null, end: null },
}
Ranged Date Selection Filter Props
To have a ranged date selection filter, you must have type: "dateRange" in the filter object in the array.Identifies this as a date range filter
Unique identifier for the filter
Display label for the filter
mode
"mm/dd/yyyy" | "dd/mm/yyyy"
default:"mm/dd/yyyy"
Date format for the inputs.
value
{ start: Date | null; end: Date | null }
default:"{ start: null, end: null }"
Currently selected date range.
{
type: "custom",
id: "customFilterId",
label: "Custom Filter",
buttonRender: ({ value, onChange }) => <Component />,
drawerRender: ({ value, onChange }) => <Component />,
value: null,
labelChipCount: 0,
}
Custom Filter Props
To have a custom filter, you must have type: "custom" in the filter object in the array.Identifies this as a custom filter
Unique identifier for the filter
Display label for the filter
buttonRender
(props: { value?: T; onChange: (value?: T) => void }) => ReactNode
Function to render the filter content in the button/popover
drawerRender
(props: { value?: T; onChange: (value?: T) => void }) => ReactNode
Function to render the filter content in the drawer
Optional number to display in a label chip
Current value of the custom filter