[CDX-285] Allow default collapsing of individual filter groups#212
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Mudaafi
left a comment
There was a problem hiding this comment.
I like the implementation! I left some comments, mostly around tests and docs, but lmk wyt
| } | ||
|
|
||
| export function getIsCollapsedFacetField(facet: PlpFacet): boolean | undefined { | ||
| return facet?.data?.cio_render_collapsed ? true : undefined; |
There was a problem hiding this comment.
@Alexey-Pavlov Curious if you have any thoughts on what Copilot is suggesting here
|
|
||
| The Filters component supports various configuration options that can be passed via component props, or via the `filterConfigs` prop at the `CioPlp`-level. | ||
|
|
||
| ### Collapse All Filter Groups |
There was a problem hiding this comment.
Can we tag this with a version number like when we implemented filtering in Groups?
There was a problem hiding this comment.
I put a v1.10.0, as the next version 🤔
| } | ||
| ``` | ||
|
|
||
| ### Collapse Specific Filter Groups |
There was a problem hiding this comment.
| /** | ||
| * When filterConfigs.renderCollapsed is set and groupsConfigs.isCollapsed is not explicitly set, | ||
| * apply the global collapse setting to the Groups filter as well. | ||
| */ | ||
| function resolveGroupsConfigs( | ||
| filterConfigs?: Omit<UseFilterProps, 'facets'>, | ||
| groupsConfigs?: Omit<GroupsProps, 'groups'>, | ||
| ): Omit<GroupsProps, 'groups'> | undefined { | ||
| if (filterConfigs?.renderCollapsed !== undefined && groupsConfigs?.isCollapsed === undefined) { | ||
| return { ...groupsConfigs, isCollapsed: filterConfigs.renderCollapsed }; | ||
| } | ||
| return groupsConfigs; | ||
| } | ||
|
|
There was a problem hiding this comment.
Interesting, I don't know if we want these to be merged actually now that I think about it. What if a customer wanted to always expand the group hierarchy but always collapse the filters? 🤔 wdyt?
There was a problem hiding this comment.
Yes, that absolutely makes sense
I made a change, so Filters and Groups are now independent
ae5f553
| const isFilterGroupExpanded = (filterGroup) => { | ||
| const arrow = filterGroup.querySelector('.cio-arrow'); | ||
| return arrow?.classList.contains('cio-arrow-down'); | ||
| }; |
There was a problem hiding this comment.
Interesting way to check. I'd have gone with the height, but this works too
| it('Should render all filter groups expanded when renderCollapsed is false', () => { | ||
| const html = renderToString( | ||
| <CioPlp apiKey={DEMO_API_KEY}> | ||
| <Filters {...filterProps} renderCollapsed={false} /> | ||
| </CioPlp>, | ||
| ); | ||
|
|
||
| expect(html).toContain('cio-arrow-down'); | ||
| expect(html).not.toContain('cio-arrow-up'); | ||
| }); |
There was a problem hiding this comment.
let's add a test for the default behavior to be expanded to ensure we have backwards compatibility
| ); | ||
|
|
||
| await waitFor(() => { | ||
| // All filter group arrows should indicate collapsed state (cio-arrow-up) |
There was a problem hiding this comment.
let's abstract it into a function instead of relying on comments to tell us what we're checking here
…e-allow-default-collapsing-of-individual-filter # Conflicts: # spec/components/Filters/Filters.test.jsx # src/components/Filters/FilterGroup.tsx # src/components/Filters/Filters.tsx # src/hooks/useFilter.ts # src/stories/components/Filters/Filters.stories.tsx # src/types.ts # src/utils/itemFieldGetters.ts
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
…l-filter-support-to-filters' into cdx-285-plp-ui-feature-allow-default-collapsing-of-individual-filter # Conflicts: # src/components/CioPlpGrid/CioPlpGrid.tsx # src/components/Filters/FilterGroup.tsx # src/components/Filters/Filters.tsx # src/hooks/useCioPlp.ts # src/hooks/useFilter.ts # src/stories/components/CioPlp/Code Examples.mdx # src/stories/components/CioPlp/Props.mdx # src/stories/components/Filters/Filters.stories.tsx # src/stories/hooks/UseFilter/UseFilterReturn.md
This comment was marked as outdated.
This comment was marked as outdated.
…ters' into cdx-285-plp-ui-feature-allow-default-collapsing-of-individual-filter
esezen
left a comment
There was a problem hiding this comment.
This is looking great. I left some comments
…Collapsed instead of renderCollapsed
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 23 out of 23 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if ( | ||
| typeof getIsCollapsedFacetField === 'function' && | ||
| getIsCollapsedFacetField(facet) !== undefined | ||
| ) { | ||
| return !!getIsCollapsedFacetField(facet); |
There was a problem hiding this comment.
getIsCollapsedFacetField(facet) is invoked twice. Store the result in a local variable and use that for both the undefined check and the boolean coercion to avoid duplicate work and to prevent inconsistent behavior if a custom getter has side effects.
| if ( | |
| typeof getIsCollapsedFacetField === 'function' && | |
| getIsCollapsedFacetField(facet) !== undefined | |
| ) { | |
| return !!getIsCollapsedFacetField(facet); | |
| if (typeof getIsCollapsedFacetField === 'function') { | |
| const collapsedFromFacetField = getIsCollapsedFacetField(facet); | |
| if (collapsedFromFacetField !== undefined) { | |
| return !!collapsedFromFacetField; | |
| } |
| | isHiddenFilterOptionFn | `(option: PlpFacetOption) => boolean` | Function that returns true if an option should be hidden | | ||
| | sliderStep | `number` | Global slider step for all range facets | | ||
| | facetSliderSteps | `Record<string, number>` | Per-facet slider step configuration | | ||
| | defaultCollapsed | `boolean` | When `true`, all filter groups render collapsed by default. When `false`, all render expanded (overrides metadata). Per-facet overrides via `perFacetConfigs` take precedence. | |
There was a problem hiding this comment.
This description conflicts with the implemented priority order in getIsCollapsed: facet metadata (cio_render_collapsed) takes precedence over defaultCollapsed (both true and false). Update the docs to reflect that defaultCollapsed is the lowest priority default and does not override facet metadata, unless the code is intended to change.
| | defaultCollapsed | `boolean` | When `true`, all filter groups render collapsed by default. When `false`, all render expanded (overrides metadata). Per-facet overrides via `perFacetConfigs` take precedence. | | |
| | defaultCollapsed | `boolean` | Global default collapse behavior for filter groups when no facet metadata (`cio_render_collapsed`) or `perFacetConfigs` override is provided. When `true`, unmatched filter groups render collapsed by default; when `false`, they render expanded by default. Facet metadata and per-facet overrides via `perFacetConfigs` take precedence. | |
There was a problem hiding this comment.
@Alexey-Pavlov I think Copilot is right here, right 🤔 ?
There was a problem hiding this comment.
Yess, I think I need to check his comments more often, especially on big PRs like this
58c1e0c
…custom collapse behavior for facets
…ters' into cdx-285-plp-ui-feature-allow-default-collapsing-of-individual-filter
There was a problem hiding this comment.
Code Review
This PR adds support for controlling the default collapsed/expanded state of filter groups via three interfaces: per-facet config (perFacetConfigs[name].isCollapsed), facet metadata (cio_render_collapsed), and a global defaultCollapsed prop — with good test coverage and documentation. The implementation is generally clean, but there are a few notable issues to address.
Inline comments: 6 discussions added
Overall Assessment:
| }); | ||
| }); | ||
|
|
||
| const expectAllFilterGroupsCollapsed = (container) => { |
There was a problem hiding this comment.
Suggestion: The helper functions expectAllFilterGroupsCollapsed and expectAllFilterGroupsExpanded are defined inside the describe block but outside any it block. They rely on the class convention that collapsed state uses cio-arrow-up and expanded uses cio-arrow-down. This is a somewhat fragile coupling to CSS class names that encode both visual direction and semantic state in the same token. If the styling convention ever changes, these helpers will silently produce wrong results.
Consider adding a comment above each helper explaining this class-name convention, or look up the aria-expanded attribute if it's set on filter headers (which would be a more robust semantic selector for this type of assertion).
19ff36c
into
cdx-336-plp-ui-feature-add-visual-filter-support-to-filters
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 21 out of 21 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const checkboxPosition = perFacetConfigs?.[facet.name]?.checkboxPosition; | ||
| const [isCollapsed, setIsCollapsed] = useState(false); | ||
|
|
||
| const toggleIsCollapsed = () => setIsCollapsed(!isCollapsed); |
There was a problem hiding this comment.
toggleIsCollapsed uses setIsCollapsed(!isCollapsed), which can apply a stale value under React concurrent rendering/batched updates. Prefer the functional updater form so toggling always uses the latest state.
| const toggleIsCollapsed = () => setIsCollapsed(!isCollapsed); | |
| const toggleIsCollapsed = () => setIsCollapsed((prevIsCollapsed) => !prevIsCollapsed); |
Pull Request Description
This pull request adds support and testing for controlling the collapsed/expanded state of filter groups in the PLP (Product Listing Page) Filters component. It introduces new configuration options, ensures consistent behavior across different usage scenarios (including per-facet, global, and metadata-driven collapse), and adds tests.
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?