Conversation
✅ No New Circular DependenciesNo new circular dependencies detected. Current count: 0 |
📦 Alpha Package Version PublishedUse Use |
🔍 Visual review for your branch is published 🔍Here are the links to: |
There was a problem hiding this comment.
Pull request overview
Adds collapsible grouped rendering support for F0Select when using a grouped data source, including an option to control which groups start open.
Changes:
- Introduce collapsible-group handling in
F0SelectusinguseGroups+ conditional rendering of group records. - Assign a synthetic
valueto group header virtual rows and passshowOpenChange/open/onOpenChangeintoGroupHeader. - Update the Storybook grouped datasource example to set
defaultOpenGroups: true.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| packages/react/src/components/F0Select/F0Select.tsx | Implements collapsible grouped rendering and default-open group state wiring. |
| packages/react/src/components/F0Select/stories/F0Select.stories.tsx | Updates grouped datasource story to demonstrate defaultOpenGroups. |
| data.groups.map((group) => { | ||
| items.push({ | ||
| height: 30, | ||
| value: `__group__${group.key}`, |
There was a problem hiding this comment.
VirtualItem.value is used by SelectContent to decide whether the list is empty (items.filter((item) => item.value).length === 0). Giving group headers a synthetic value means the empty state (emptyMessage) will never render for grouped results with 0 records (e.g. after searching), because headers now count as “items”. It also introduces a possible collision with real option values (__group__*). Consider extending VirtualItem with a discriminant (e.g. kind: 'item' | 'header' | 'separator') or a dedicated flag so SelectContent can ignore headers for empty/scroll positioning, and keep value reserved for selectable options only.
| value: `__group__${group.key}`, |
| item: ( | ||
| <GroupHeader | ||
| label={group.label} | ||
| itemCount={group.itemCount} | ||
| showOpenChange={collapsible} | ||
| onOpenChange={(open) => setGroupOpen(group.key, open)} |
There was a problem hiding this comment.
With collapsible enabled, group expand/collapse is only wired to onClick inside GroupHeader, which is rendered outside of Radix SelectItems and isn't focusable by default. This makes group toggling inaccessible to keyboard users (and likely unreachable by arrow-key navigation). Suggest rendering the toggle as a real <button type="button"> (or add role="button", tabIndex={0}, and Enter/Space handlers) when showOpenChange is true, so groups can be expanded/collapsed without a pointer device.
| item: ( | |
| <GroupHeader | |
| label={group.label} | |
| itemCount={group.itemCount} | |
| showOpenChange={collapsible} | |
| onOpenChange={(open) => setGroupOpen(group.key, open)} | |
| item: collapsible ? ( | |
| <div | |
| role="button" | |
| tabIndex={0} | |
| aria-expanded={!!openGroups[group.key]} | |
| onClick={() => | |
| setGroupOpen(group.key, !openGroups[group.key]) | |
| } | |
| onKeyDown={(event) => { | |
| if (event.key === "Enter" || event.key === " ") { | |
| event.preventDefault() | |
| setGroupOpen(group.key, !openGroups[group.key]) | |
| } | |
| }} | |
| > | |
| <GroupHeader | |
| label={group.label} | |
| itemCount={group.itemCount} | |
| showOpenChange={collapsible} | |
| open={openGroups[group.key]} | |
| /> | |
| </div> | |
| ) : ( | |
| <GroupHeader | |
| label={group.label} | |
| itemCount={group.itemCount} | |
| showOpenChange={collapsible} |
| @@ -640,21 +641,33 @@ const F0SelectComponent = forwardRef(function Select< | |||
| data.groups.map((group) => { | |||
| items.push({ | |||
| height: 30, | |||
| value: `__group__${group.key}`, | |||
| item: ( | |||
| <GroupHeader | |||
| label={group.label} | |||
| itemCount={group.itemCount} | |||
| showOpenChange={collapsible} | |||
| onOpenChange={(open) => setGroupOpen(group.key, open)} | |||
| open={openGroups[group.key]} | |||
| /> | |||
| ), | |||
| }) | |||
| items.push(...getItems(group.records)) | |||
| if (!collapsible || openGroups[group.key]) { | |||
| items.push(...getItems(group.records)) | |||
| } | |||
There was a problem hiding this comment.
Collapsible grouped rendering (group headers + conditional record rendering + defaultOpenGroups) is new behavior in F0Select, but there are no unit tests covering it. Since packages/react/src/components/F0Select/__tests__/F0Select.test.tsx already exists, please add tests that verify: (1) default open groups render records initially, (2) clicking a group header toggles visibility of its records, and (3) the empty state behaves correctly when grouped search returns no records.
Coverage Report for packages/react
File Coverage
|
||||||||||||||||||||||||||||||||||||||
Description
Screenshots (if applicable)
[Link to Figma Design](Figma URL here)
Implementation details