[CDX-285] Allow default collapsing of individual filter groups#212
[CDX-285] Allow default collapsing of individual filter groups#212Alexey-Pavlov wants to merge 5 commits intomainfrom
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
Adds configurability for default collapsed/expanded behavior of PLP filter groups, supporting global config, per-facet overrides, and facet-metadata-driven collapse, with docs/stories/tests updates.
Changes:
- Introduces
renderCollapsedandcollapsedFacetsoptions inuseFilter/Filters, plusgetIsCollapsedhelper for consistent precedence handling. - Adds
ItemFieldGetters.getIsCollapsedFacetFieldto readfacet.data.cio_render_collapsedmetadata and wires default collapse intoFilterGroup. - Propagates
filterConfigs.renderCollapsedto the Groups filter by default and adds Storybook docs/stories and test coverage.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/itemFieldGetters.ts | Adds facet metadata getter for collapse state. |
| src/types.ts | Extends ItemFieldGetters with getIsCollapsedFacetField. |
| src/hooks/useFilter.ts | Adds new props + getIsCollapsed resolver with precedence rules. |
| src/components/Filters/Filters.tsx | Passes computed defaultCollapsed to each FilterGroup; exposes getIsCollapsed via render props. |
| src/components/Filters/FilterGroup.tsx | Accepts defaultCollapsed and initializes internal collapsed state from it. |
| src/components/CioPlpGrid/CioPlpGrid.tsx | Applies filterConfigs.renderCollapsed to Groups unless groupsConfigs.isCollapsed is set; passes filterConfigs to mobile Filters rendering. |
| src/hooks/useCioPlp.ts | Updates JSDoc for filterConfigs options. |
| src/stories/components/Filters/Filters.stories.tsx | Adds stories demonstrating global/per-facet/metadata collapse. |
| src/stories/hooks/UseFilter/UseFilterReturn.md | Documents new getIsCollapsed return value. |
| src/stories/components/CioPlp/Props.mdx | Documents new ItemFieldGetters entry + filterConfigs options. |
| spec/hooks/useFilter/useFilter.test.js | Adds unit tests for collapse precedence and parsing. |
| spec/components/Filters/Filters.test.jsx | Adds component tests covering collapse behaviors and render-props exposure. |
| spec/components/Filters/Filters.server.test.jsx | Adds SSR tests for global collapse/expand. |
| spec/components/Filters/FilterGroup.test.jsx | Adds unit tests for defaultCollapsed. |
| spec/components/CioPlp/CioPlp.test.jsx | Adds client tests for filterConfigs.renderCollapsed and Groups override behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| | Property | Type | Description | | ||
| | --------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------- | | ||
| | renderCollapsed | `boolean` | When `true`, all filter groups render collapsed by default. When `false`, all render expanded (overrides metadata). | | ||
| | collapsedFacets | `string[] \| string` | List of facet names to render collapsed. Accepts an array or comma-separated string. Takes highest precedence. | | ||
| | sliderStep | `number` | Global slider step for all range facets. | | ||
| | facetSliderSteps| `Record<string, number>` | Per-facet slider step configuration. | |
There was a problem hiding this comment.
This documentation table row is missing a space before the | after facetSliderSteps (| facetSliderSteps| ...). That can break Markdown table rendering in Storybook/MDX; align it with the other rows (| facetSliderSteps | ...).
| | Property | Type | Description | | |
| | --------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------- | | |
| | renderCollapsed | `boolean` | When `true`, all filter groups render collapsed by default. When `false`, all render expanded (overrides metadata). | | |
| | collapsedFacets | `string[] \| string` | List of facet names to render collapsed. Accepts an array or comma-separated string. Takes highest precedence. | | |
| | sliderStep | `number` | Global slider step for all range facets. | | |
| | facetSliderSteps| `Record<string, number>` | Per-facet slider step configuration. | | |
| | Property | Type | Description | | |
| | --------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------- | | |
| | renderCollapsed | `boolean` | When `true`, all filter groups render collapsed by default. When `false`, all render expanded (overrides metadata). | | |
| | collapsedFacets | `string[] \| string` | List of facet names to render collapsed. Accepts an array or comma-separated string. Takes highest precedence. | | |
| | sliderStep | `number` | Global slider step for all range facets. | | |
| | facetSliderSteps | `Record<string, number>` | Per-facet slider step configuration. | |
| ], | ||
| }; | ||
|
|
||
| const mockFacetsWithMetadata = require('../../local_examples/sampleFacetsWithCollapsedMetadata.json'); |
There was a problem hiding this comment.
This test requires ../../local_examples/sampleFacetsWithCollapsedMetadata.json, but that fixture file is not present under spec/local_examples in this branch. Tests will fail at runtime with a module-not-found error; either add the fixture file to spec/local_examples or build the metadata facets inline from sampleFacets.json (similar to how the story does it).
| const mockFacetsWithMetadata = require('../../local_examples/sampleFacetsWithCollapsedMetadata.json'); | |
| const mockFacetsWithMetadata = mockTransformedFacets; |
| }); | ||
|
|
||
| describe('getIsCollapsed', () => { | ||
| const mockFacetsWithMetadata = require('../../local_examples/sampleFacetsWithCollapsedMetadata.json'); |
There was a problem hiding this comment.
This test suite requires ../../local_examples/sampleFacetsWithCollapsedMetadata.json, but that JSON fixture does not exist under spec/local_examples in this branch. The suite will fail to load; add the fixture or generate the metadata facets inline in the test.
| const mockFacetsWithMetadata = require('../../local_examples/sampleFacetsWithCollapsedMetadata.json'); | |
| const mockFacetsWithMetadata = [ | |
| { | |
| name: 'Color', | |
| metadata: { | |
| cio_render_collapsed: true, | |
| }, | |
| }, | |
| { | |
| name: 'Size', | |
| metadata: {}, | |
| }, | |
| ]; |
src/utils/itemFieldGetters.ts
Outdated
| } | ||
|
|
||
| export function getIsCollapsedFacetField(facet: PlpFacet): boolean | undefined { | ||
| return facet?.data?.cio_render_collapsed ? true : undefined; |
There was a problem hiding this comment.
getIsCollapsedFacetField currently returns true when facet.data.cio_render_collapsed is truthy, otherwise undefined. This drops an explicit false value (it becomes undefined), which is inconsistent with the docs ("Defaults to facet.data.cio_render_collapsed") and with getIsHiddenGroupField returning the underlying boolean field. Consider returning the field value directly (or explicitly handling true/false) so consumers can distinguish false from "not provided" if needed.
| return facet?.data?.cio_render_collapsed ? true : undefined; | |
| return facet?.data?.cio_render_collapsed; |
There was a problem hiding this comment.
@Alexey-Pavlov Curious if you have any thoughts on what Copilot is suggesting here
There was a problem hiding this comment.
Yes, that's valid, since we're handling it with !!
ae5f553
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
src/utils/itemFieldGetters.ts
Outdated
| } | ||
|
|
||
| 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.
Code Review Results✅ StrengthsThe feature is well-architected with a clean priority-based collapse system (per-facet > global > metadata), excellent test coverage across all three interfaces, and thorough documentation updates in stories and MDX files. 🚨 Critical IssuesNone.
|
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?