This guide covers changes to the beta FilterBar component. These APIs may
continue to evolve before the stable release.
Overview
Custom filters (type: "custom") can now work and look like the built-in filter types:
emptyValue- The value that means “no filter”. Clear Filters and the drawer count respect it.isEqual- How the filter compares values. Defaults to a structural comparison.formatLabel- Trigger content while the filter is active.keepOpenOnChange- Keeps the popover open between changes whencontrolledFilteringis off.clear- Adds a footer Clear button to the popover.commitOnSelect- Commits each popover change and closes.FilterRenderProps- AddscommittedValue,isDraft,commit,close, andfilter.icon- A leading trigger icon on a custom filter. It replaces the chevron. The drawer does not show it.
id.
The popover for custom, text, and numeric range filters also handles fields with their own menu, such as a SelectField:
- Pressing a label or other non-focusable spot inside the popover no longer closes it.
- Escape and Enter go to an open menu first. Escape then closes the popover without clearing a SelectField inside it.
- With
controlledFiltering, pressing Enter to apply no longer reopens the popover.
Design Rationale
Why compare values structurally?
The popover compared custom values the way it compares select options. Objects with anid matched by id alone, and arrays of those objects matched by their set of ids. Edits to any other field looked unchanged, so Apply ignored them. Arrays of strings threw an error.
id can say so with isEqual.
Why emptyValue?
A custom filter was active whenever its value wasn’t undefined. A filter that clears to [] stayed active, kept Clear Filters visible, and counted toward the drawer badge. emptyValue names the value that means “no filter”, and Clear Filters writes it back.
Why formatLabel?
Showing the selection on the trigger meant rewriting label in onFilterChange. formatLabel derives the trigger content from the committed value, as the built-in filters do.
Why icon on a custom filter?
A custom trigger showed only a chevron. icon lets that trigger show what the filter is for, such as a place for a nearby filter. The drawer doesn’t show the icon, because each drawer cell already has a visible label.
Reference
Filter render props
buttonRender and drawerRender receive the same props:
With
commitOnSelect, the popover has no draft: value is the committed value and isDraft is false.
Migration Guide
Trigger labels
Move label logic out ofonFilterChange and into formatLabel.
Empty values
SetemptyValue instead of mapping an empty value to undefined.
Id-only comparison
PassisEqual when a value is identified by id and other fields shouldn’t count as edits.
Drawer items compared by reference
The drawer edits a copy of the committed value, including nested arrays, plain objects, and Dates. Compare items by a key.Tests that query the drawer cell by label
Each custom drawer cell is a group named by the filter’slabel. A label query for the same text now matches both the group and the control inside it, so query the control by role.
Calling render functions directly
Code that callsbuttonRender or drawerRender itself, such as a unit test, must pass the new required props.
Breaking Changes
The following changes are breaking:- Custom filter values now compare structurally by default. Edits to any field commit, reordering an array counts as a change, and a new value equal to the committed one doesn’t commit. Pass
isEqualto compare byid. - Custom filter drawer drafts now copy nested values. Arrays, plain objects, and Dates in the draft are copies, so compare items by a key rather than by reference.
- Custom drawer cells now render in a group named by the filter’s
label, withdata-idset to the filter’sid. Query the control inside by role. FilterRenderPropsnow requiresisDraft,commit,close, andfilter. Code that callsbuttonRenderordrawerRenderdirectly must pass them.