Skip to content

feat: select grouped collapsable#3627

Draft
laurafg wants to merge 1 commit intomainfrom
fix/f0-select-collapsible-grouping
Draft

feat: select grouped collapsable#3627
laurafg wants to merge 1 commit intomainfrom
fix/f0-select-collapsible-grouping

Conversation

@laurafg
Copy link
Copy Markdown
Contributor

@laurafg laurafg commented Mar 10, 2026

Description

Screenshots (if applicable)

[Link to Figma Design](Figma URL here)

Implementation details

Copilot AI review requested due to automatic review settings March 10, 2026 14:00
@github-actions github-actions Bot added feat react Changes affect packages/react labels Mar 10, 2026
@github-actions
Copy link
Copy Markdown
Contributor

✅ No New Circular Dependencies

No new circular dependencies detected. Current count: 0

@github-actions
Copy link
Copy Markdown
Contributor

📦 Alpha Package Version Published

Use pnpm i github:factorialco/f0#npm/alpha-pr-3627 to install the package

Use pnpm i github:factorialco/f0#b861f31e45761c612554ccc9eb183f66bc1db945 to install this specific commit

@github-actions
Copy link
Copy Markdown
Contributor

🔍 Visual review for your branch is published 🔍

Here are the links to:

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 F0Select using useGroups + conditional rendering of group records.
  • Assign a synthetic value to group header virtual rows and pass showOpenChange/open/onOpenChange into GroupHeader.
  • 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}`,
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
value: `__group__${group.key}`,

Copilot uses AI. Check for mistakes.
Comment on lines 645 to 650
item: (
<GroupHeader
label={group.label}
itemCount={group.itemCount}
showOpenChange={collapsible}
onOpenChange={(open) => setGroupOpen(group.key, open)}
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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}

Copilot uses AI. Check for mistakes.
Comment on lines 603 to +657
@@ -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))
}
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot generated this review using guidance from repository custom instructions.
@github-actions
Copy link
Copy Markdown
Contributor

Coverage Report for packages/react

Status Category Percentage Covered / Total
🔵 Lines 43.71% 9236 / 21127
🔵 Statements 43.08% 9501 / 22052
🔵 Functions 35.58% 2084 / 5857
🔵 Branches 33.89% 5638 / 16633
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/react/src/components/F0Select/F0Select.tsx 75.25% 62.81% 69.56% 75.51% 162, 169, 182-184, 196-198, 238, 261-264, 393-394, 400, 404-405, 425, 439, 457-458, 497, 510-543, 582, 594-598, 640-659, 673, 764, 782-819
Generated in workflow #11599 for commit 9eedfcb by the Vitest Coverage Report Action

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat react Changes affect packages/react

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants