[CDX-326] Allow blacklisting facets from filters#207
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds functionality to allow facets and facet options to be hidden from the filter UI through three methods: custom functions, metadata flags, or custom field getters. This enables conditional rendering of filters on a per-page basis (e.g., showing a facet on search results but hiding it on category pages).
Changes:
- Added
isHiddenFilterFnandisHiddenFilterOptionFnprops to filter components for custom hiding logic - Introduced
getIsHiddenFilterFieldandgetIsHiddenFilterOptionFieldfield getters to support metadata-based hiding viacio_plp_hiddenflag - Enhanced
useOptionsListhook with recursive filtering capability for nested options in hierarchical structures
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/itemFieldGetters.ts | Added field getter functions for detecting hidden filters and filter options |
| src/types.ts | Updated type definitions to support new field getters and changed data type from object to Record<string, any> |
| src/stories/components/Filters/Filters.stories.tsx | Added story examples demonstrating the three methods for hiding filters |
| src/stories/components/Filters/Code Examples.mdx | Added comprehensive documentation with code examples for the hiding functionality |
| src/hooks/useOptionsList.ts | Added recursive filtering support for nested options via nestedOptionsKey parameter |
| src/hooks/useGroups.ts | Enabled recursive filtering for group children by passing nestedOptionsKey: 'children' |
| src/hooks/useFilter.ts | Implemented facet filtering logic using both custom function and metadata-based approaches |
| src/components/Filters/UseFilterOptionsList.tsx | Added support for hiding individual filter options using custom functions and metadata |
| src/components/Filters/Filters.tsx | Passed through isHiddenFilterOptionFn prop to child components |
| src/components/Filters/FilterGroup.tsx | Passed through isHiddenFilterOptionFn prop to FilterOptionsList |
| spec/hooks/useGroups/useGroups.test.js | Added tests for recursive filtering of nested group children |
| spec/hooks/useFilter/useFilter.test.js | Added comprehensive tests for facet hiding functionality |
| spec/components/Filters/Filters.test.jsx | Added integration tests for filter and filter option hiding |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| displayName: string; | ||
| value: string; | ||
| data: object; | ||
| data: Record<string, any>; |
There was a problem hiding this comment.
Using any bypasses TypeScript's type safety. Consider using unknown instead, which provides type safety while still allowing flexibility: Record<string, unknown>.
| export interface PlpHierarchicalFacetOption extends PlpFacetOption { | ||
| options: Array<PlpHierarchicalFacetOption>; | ||
| data: object & { parentValue: string | null }; | ||
| data: Record<string, any> & { parentValue: string | null }; |
There was a problem hiding this comment.
Using any bypasses TypeScript's type safety. Consider using unknown instead: Record<string, unknown> & { parentValue: string | null }.
| data: Record<string, any> & { parentValue: string | null }; | |
| data: Record<string, unknown> & { parentValue: string | null }; |
| const nestedOptions = (option as Record<string, any>)[nestedKey]; | ||
| if (Array.isArray(nestedOptions) && nestedOptions.length > 0) { | ||
| return { | ||
| ...option, | ||
| [nestedKey]: filterOptionsRecursively(nestedOptions, isHiddenFn, nestedKey), |
There was a problem hiding this comment.
Using any bypasses TypeScript's type safety. Consider using unknown instead and adding proper type guards: (option as Record<string, unknown>)[nestedKey].
| const nestedOptions = (option as Record<string, any>)[nestedKey]; | |
| if (Array.isArray(nestedOptions) && nestedOptions.length > 0) { | |
| return { | |
| ...option, | |
| [nestedKey]: filterOptionsRecursively(nestedOptions, isHiddenFn, nestedKey), | |
| const nestedOptions = (option as Record<string, unknown>)[nestedKey]; | |
| if (Array.isArray(nestedOptions) && nestedOptions.length > 0) { | |
| return { | |
| ...option, | |
| [nestedKey]: filterOptionsRecursively(nestedOptions as Array<T>, isHiddenFn, nestedKey), |
This comment has been minimized.
This comment has been minimized.
Alexey-Pavlov
left a comment
There was a problem hiding this comment.
LGTM! Thanks a lot for handling this!
Mudaafi
left a comment
There was a problem hiding this comment.
lgtm, just a couple of doc suggestions
| ### Method 3: Custom Field Getter | ||
|
|
||
| You can also customize the metadata field used for hiding by providing a custom `itemFieldGetters` to `CioPlpProvider`: |
There was a problem hiding this comment.
Probably important to mention the field/function needs to return a boolean: true -> hide, false -> render
|
|
||
| > **Note:** This functionality is intended for **conditional rendering per page**. For example, showing a facet on search results but hiding it on category pages. If a facet or option should be hidden across your entire site, use the hidden facets feature in the Constructor.io dashboard or API instead. Hidden facets configured through the dashboard are excluded from API responses entirely, which is more efficient than client-side filtering. | ||
|
|
||
| ### Method 1: Custom Function |
There was a problem hiding this comment.
Let's move this to method 3. Generally, this method should be the least used by consumers compared to the other 2 just by virtue of how much customization this requires
Co-authored-by: Ahmad Mudaafi' <ahmad.mudaafi@constructor.io>
Co-authored-by: Ahmad Mudaafi' <ahmad.mudaafi@constructor.io>
Co-authored-by: Ahmad Mudaafi' <ahmad.mudaafi@constructor.io>
…cets-from-filters
This comment has been minimized.
This comment has been minimized.
Code Review Results✅ StrengthsWell-structured feature that extends the existing 🚨 Critical Issues
|
Mudaafi
left a comment
There was a problem hiding this comment.
lgtm, thanks for making the changes
| } = props; | ||
| const { initialNumOptions, modifyRequestMultipleFilter, isCollapsed, isHiddenFilterOptionFn } = | ||
| props; | ||
| const facet = 'facet' in props ? props.facet : props.multipleFacet; |
There was a problem hiding this comment.
strange little accommodation, but I assume it's from merging main
There was a problem hiding this comment.
That one is coming from main to support single select filters
Pull Request Checklist
Before you submit a pull request, please make sure you have to following:
PR Type
What kind of change does this PR introduce?
Clone of #192