Dynamic Filters System
The dynamic filters system provides a flexible way to handle filtering of data based on attributes passed from the backend. It supports multiple filter types and automatically generates the appropriate UI components.
Core Components
1. DynamicFilters Component
The main component that orchestrates the filter system. It accepts:
attributes: Object containing filterable attributesdimensions: Current dimension values for the filtersonChange: Callback when filters changeinitialValues: Initial filter valuesmanual: Boolean to control if validation is manual or automatic
<DynamicFilters
attributes={attributes}
dimensions={dimensions}
onChange={handleFiltersChanged}
initialValues={initialFilters}
manual={false}
/>2. Filter Types
The system supports two main types of filters:
Select-like Filters
- ButtonGroup
- Select
- Toggle
- ComboBox
- Input
- Checkbox
- CheckboxList
Range Filters
- RangeSlider
- NumberComparison
3. AdaptiveNumericFilter Component
The AdaptiveNumericFilter component intelligently switches between different numeric filter UI components based on available dimension data:
- RangeSlider: Used when dimensions provide min/max bounds, allowing users to select a range within known limits
- NumberComparison: Used when dimension data is unavailable or insufficient, providing comparison operators (equals, greater than, less than, etc.)
<AdaptiveNumericFilter
attribute={attribute}
dimensions={dimensions}
fieldAtom={fieldAtom}
/>The component automatically determines which UI to render based on the presence and validity of dimension values.
Feature Flag Integration
Attributes can be conditionally displayed based on feature flags. Each attribute can have an optional featureFlag property that controls its visibility:
interface Attribute {
name: string
type: string
featureFlag?: string // Optional feature flag name
// ... other properties
}The filter system checks feature flags using the pattern:
!attribute.featureFlag || hasVariant(attribute.featureFlag)This means:
- If no
featureFlagis specified, the filter is always shown - If a
featureFlagis specified, the filter is only shown when that feature flag is enabled (viahasVariant)
This allows for gradual rollout of new filter capabilities or A/B testing of filter options.
Data Flow
-
Backend to Frontend
- Backend provides attributes with their types and possible values
- Dimensions contain current possible values and counts
- Initial values can be provided to pre-populate filters
-
Filter State Management
- Uses Jotai for state management
filterAtom: Stores current filter valuesfieldAtomsAtom: Maintains form field states
-
Filter Generation
// Convert attributes to filter format attributesToFilters( attributesWithNames, dimensions, values, touchedFields )
Key Hooks
useFilterableAttributes
Filters out unsupported attributes and returns a tuple of attribute name and attribute.
const filteredAttributes = useFilterableAttributes(attributes)useDynamicFormAtom
Generates a form atom based on the filterable attributes.
const formAtom = useDynamicFormAtom(attributesWithNames)useFilters
Converts form values to backend-compatible filters.
const filters = useFilters(
attributesWithNames,
dimensions,
formAtom
)useAttributeInfo
Retrieves attribute metadata including display name, type, and configuration options.
const { displayName, type, options } = useAttributeInfo(attributeKey, attributes)useFilterValueTransform
Transforms filter values between internal format and display format. Handles value normalization and conversion for different filter types.
const { toDisplay, toInternal } = useFilterValueTransform(attribute)useScreenSize
Detects current screen size for responsive filter layouts. Returns breakpoint information used to adapt filter presentation on mobile vs desktop.
const { isMobile, isTablet, isDesktop } = useScreenSize()Filter Conversion
Select-like Filters
Converted to enum filters with OR operations:
{
[attributeName]: {
op: "OR",
values: ["value1", "value2"]
}
}Range Filters
Converted to range filters with min/max values:
{
[attributeName]: {
min: number,
max: number
}
}Styling
The system supports various style customizations:
interface DynamicFiltersStyleProps {
layout?: 'vertical' | 'horizontal'
filterLayout?: 'vertical' | 'horizontal'
separator?: boolean
compact?: boolean
fixedWidth?: boolean
}Usage Example
const MyComponent = () => {
const handleFiltersChanged = (filters: Filters) => {
// Handle filter changes
}
return (
<DynamicFilters
attributes={myAttributes}
dimensions={myDimensions}
onChange={handleFiltersChanged}
dynamicFilters={{
layout: 'vertical',
filterLayout: 'horizontal',
separator: true
}}
/>
)
}Best Practices
- Always provide dimensions when available to show counts
- Use manual mode when you need to control when filters are applied
- Consider mobile responsiveness when choosing layouts
Error Handling
The system handles several edge cases:
- Missing dimensions
- Invalid filter values
- Uninitialized form fields
- Range values outside bounds
Remember to validate filter outputs before sending to the backend.