Skip to content

refactor(Select): migrate from @radix-ui/react-select to @base-ui/react/combobox#599

Open
aammami-ledger wants to merge 33 commits intomainfrom
DLS-609/baseui-select
Open

refactor(Select): migrate from @radix-ui/react-select to @base-ui/react/combobox#599
aammami-ledger wants to merge 33 commits intomainfrom
DLS-609/baseui-select

Conversation

@aammami-ledger
Copy link
Copy Markdown
Collaborator

@aammami-ledger aammami-ledger commented Mar 30, 2026

Omitted Base UI part

Omitted Base UI part Reason
Combobox.Portal Absorbed into SelectContent
Combobox.Positioner Absorbed into SelectContent
Combobox.Popup Absorbed into SelectContent
Combobox.ItemIndicator Absorbed into SelectItem (always renders a check icon)
Combobox.Value Used internally in SelectTrigger — not exported
Combobox.Collection Used internally in SelectList for grouped iteration
Combobox.Label Floating label is built into the default trigger
Combobox.InputGroup Not needed — SelectSearch handles input layout
Combobox.Icon Chevron is built into the default trigger
Combobox.Arrow Not part of the design system
Combobox.Backdrop Not used
Combobox.Chips / Chip / ChipRemove Multi-select not supported (single-select only)
Combobox.Row Grid layout not supported
Combobox.Clear Clear is handled by SearchInput's own clear button
Combobox.Status Screen-reader live region not separately exposed
useFilter / useFilteredItems Filtering managed internally via filter prop on Select

Root props mapping

Prop Base UI Combobox.Root Our Select Decision
value Value | Value[] | null (generic) string | null String-only — no object values, no multi-select
defaultValue Value | Value[] | null string Same simplification
onValueChange (value, eventDetails) => void (value: string | null) => void Dropped eventDetails
onInputValueChange (inputValue, eventDetails) => void (value: string) => void Dropped eventDetails
onOpenChange (open, eventDetails) => void (open: boolean) => void Dropped eventDetails
items readonly Value[] SelectItemData[] | SelectItemGroup[] Opinionated data shape — must have { value, label }
filteredItems Yes Yes Passthrough for async/server filtering
filter (item, inputValue) => boolean | null (item, query) => boolean | null Auto-activates when SelectSearch is mounted
open / defaultOpen Yes Yes Passthrough
disabled boolean boolean Enhanced — integrates with shared DisabledContext
name / required Yes Yes Passthrough
multiple boolean (not exposed) Single-select only
autoHighlight boolean (not exposed) Not surfaced
highlightItemOnHover boolean (not exposed) Not surfaced
autoComplete string (not exposed) Not surfaced
itemToStringLabel (value) => string (not exposed) Not needed — items are { value, label } strings
itemToStringValue (value) => string (not exposed) Not needed
isItemEqualToValue (a, b) => boolean (not exposed) Not needed — values are strings
actionsRef RefObject<Actions> (not exposed) Imperative API not exposed
onItemHighlighted (value, details) => void (not exposed) Not surfaced

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ldls Ready Ready Preview, Comment Apr 3, 2026 2:36pm
ldls-react-native Ready Ready Preview, Comment Apr 3, 2026 2:36pm

Request Review

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

Refactors the Select component in libs/ui-react to replace Radix Select with Base UI Combobox, updating the public API and surrounding docs/examples accordingly.

Changes:

  • Removed @radix-ui/react-select usage and switched Select internals to @base-ui/react/combobox.
  • Introduced items-driven rendering, optional search, and filtering/async-filtering hooks (filter, filteredItems, onInputValueChange).
  • Updated tests, Storybook stories, MDX docs, and Figma Code Connect examples to match the new composition (SelectList, SelectSearch, SelectEmptyState).

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
package.json Removes @radix-ui/react-select dependency from the workspace root.
package-lock.json Removes Radix Select packages; updates workspace metadata (but currently has a peerDependency mismatch noted in comments).
libs/ui-react/vite.config.ts Removes @radix-ui/react-select from Rollup externals.
libs/ui-react/src/lib/Components/Select/types.ts Updates Select public types for Combobox-based API (items, search/filtering props, null selection).
libs/ui-react/src/lib/Components/Select/index.ts Re-exports newly added types.
libs/ui-react/src/lib/Components/Select/SelectContext.tsx Extends context to support search registration and null selected value.
libs/ui-react/src/lib/Components/Select/Select.tsx Re-implements Select using Base UI Combobox; adds list/search/empty-state subcomponents.
libs/ui-react/src/lib/Components/Select/Select.test.tsx Updates and expands tests for the new API and search behavior.
libs/ui-react/src/lib/Components/Select/Select.stories.tsx Updates stories and adds search/custom-filter/async-search examples.
libs/ui-react/src/lib/Components/Select/Select.mdx Updates documentation to the new composition and Base UI foundation.
libs/ui-react/src/lib/Components/Select/Select.figma.tsx Updates Figma Code Connect example to include items + SelectList.
libs/ui-react/src/lib/Components/SearchInput/types.ts Allows containerClassName to be passed through for SelectSearch styling.
libs/ui-react/src/lib/Components/BaseInput/BaseInput.tsx Uses shared useMergedRef utility and slightly reorders props spreading.
libs/ui-react/package.json Removes @radix-ui/react-select from peer dependencies.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@aammami-ledger aammami-ledger marked this pull request as ready for review March 31, 2026 06:29
@aammami-ledger aammami-ledger requested a review from a team as a code owner March 31, 2026 06:29
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

Copilot reviewed 14 out of 15 changed files in this pull request and generated 5 comments.

Comments suppressed due to low confidence (1)

libs/ui-react/src/lib/Components/Select/Select.tsx:453

  • SelectTriggerButton uses a truthy check (selectedValue ? … : …) to decide whether to show the selected content. Since selectedValue is typed as string | null, an empty-string value ('') would be treated as “no selection” even if it’s a valid option value. Prefer an explicit null check (e.g., selectedValue != null) so empty-string values behave correctly.
const SelectTriggerButton = ({
  selectedValue,
  selectedContent,
  label,
  ...props
}: SelectTriggerButtonProps) => (
  <TriggerButton {...props}>
    {selectedValue ? selectedContent : label}
  </TriggerButton>

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
};

export type SelectItemGroup = {
value: string;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[mid]: suggestion to use label instead of value here as it represent the name displayed.
Also I would suggest to add JSDoc to clearly understand the API for future updates, even if this one is an internal

Copy link
Copy Markdown
Collaborator

@gamegee gamegee left a comment

Choose a reason for hiding this comment

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

✅ Ok LGTM
However I see that SonarQube is failing 🤔

If we have time because its lot of code, run the pr-review locally - it might find some additional feedbacks.

This was a big refactor, Good job 👌

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.

Copilot reviewed 22 out of 23 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

consumerName: 'SelectInputTrigger',
contextRequired: true,
});
const hasValue = selectedValue != null && selectedValue !== '';
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

The SelectInputTrigger line 130 checks selectedValue != null && selectedValue !== '', but since selectedValue is now string | null (was changed from string), the !== '' check is redundant. After != null passes, selectedValue is guaranteed to be a non-null string. While this doesn't cause bugs, it could be simplified to just selectedValue != null or selectedValue !== null.

Suggested change
const hasValue = selectedValue != null && selectedValue !== '';
const hasValue = selectedValue != null;

Copilot uses AI. Check for mistakes.
type UseSelectItemsReturn = {
isGrouped: boolean;
groupedItems: SelectItemGroup[] | null;
filteredItemsForRoot: SelectItemData[] | SelectItemGroup[];
Copy link
Copy Markdown
Collaborator

@gamegee gamegee Apr 3, 2026

Choose a reason for hiding this comment

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

[mid]: This pattern is used in multiple places SelectItemData[] | SelectItemGroup[];

Maybe we can expose a type named:

type SelectItemDataUnion =  SelectItemData[] | SelectItemGroup[]

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

actualy only here it's being used

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 3, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
11.5% Duplication on New Code (required ≤ 3%)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants