You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Replaces #1210, which was opened from a fork so the required checks couldn't run.
Adds ResourcePicker, a field and popover for choosing one record from a grouped list.
The picker is generic over what it lists. A group is a heading with rows under it, and a row is an id, a label and an optional icon, so the same control serves Data Fabric entities, queues, storage buckets or anything else with that shape. The domain lives in the data a consumer passes, not in the component. Picking a person is the deliberate exception: people carry avatars and a resolved reference of their own, so they belong to a dedicated people picker.
Design decisions
Rows commit on click. One resource is one decision, so there is no Cancel and Select pair, which would add a second click to a choice that is already unambiguous. The footer's two slots hold the ways out of the picker instead, such as a link to create a new record. Passing them as slots keeps product routes out of the design system.
Selection is cyan, hover is neutral. The chosen row takes a tick, the brand tint (brand-subtle) and an accent label, and keeps them under the cursor. The neutral surface-overlay fill means cursor position and nothing else. surface-selected is not used for selection because it resolves to the same value as surface-hover in every theme, so the two states would be indistinguishable.
Ids must be non-empty and unique across groups, labels need not be. The same name can appear under two folders, so the id carries the path. An empty id is the picker's "no value", so a row with one is dropped, with a warning in development.
Structure
Built on Command and Popover, following VariablePicker, with the file layout FolderPicker uses: types.ts for the consumer-facing API and a components/ folder for the row, group header, footer and empty state. The empty state is its own component with its own test, since an empty picker and a search that matched nothing are different things and must not collapse into one message.
The search is Apollo's own CommandInput rather than a bespoke field.
Per-package coverage and bundle size on this PR. New-line coverage = of the source lines this PR adds or changes, the % hit by tests.
Package
Coverage
New-line coverage
Packed (gzip)
Unpacked
vs main
@uipath/apollo-core
—
—
42.02 MB
50.17 MB
±0
@uipath/apollo-react
51.7%
—
8.01 MB
31.60 MB
+126 B
@uipath/apollo-ui-icons
—
—
2.86 MB
6.97 MB
±0
@uipath/apollo-vertex
—
—
—
—
—
@uipath/apollo-wind
79.5%
98.9% (182/184)
803.0 KB
4.64 MB
+15.4 KB
@uipath/ap-chat
85.8%
—
40.37 MB
45.58 MB
±0
"Coverage" is each package's own coverage.include scope (e.g. apollo-core instruments only scripts/). "Packed"/"Unpacked" come from npm pack --dry-run and only cover built packages — "—" means not measured this run (package not affected / not built). "vs main" is the packed (gzipped) delta against the last successful main build (the package-sizes artifact from the Release workflow); "—" there means no main baseline was available this run. The baseline is main's latest build, not this PR's exact merge-base, so it includes any drift since the branch diverged. Packages with no vitest config are omitted.
This also stops Tab from reaching the hosting Radix focus scope. Tabbing from the final footer action therefore bypasses the popover's focus wrap and can move focus outside and dismiss it instead of returning to the search. Allow Tab to bubble alongside Escape, while keeping the list-navigation and activation keys isolated. Add a regression test that tabs from the final footer action back to the search.
A field and popover for choosing one record from a grouped list. The
component is generic over what it lists: a group is a heading with rows
under it, and a row is an id, a label and an optional icon, so the same
control serves Data Fabric entities, queues, storage buckets or anything
else with that shape. The domain lives in the data a consumer passes.
Rows commit on click, so there is no Cancel and Select pair: one
resource is one decision. The footer's two slots hold the ways out of
the picker instead, which keeps product routes out of the component.
Built on Command and Popover, following VariablePicker, with the file
layout FolderPicker uses. Ids must be unique across groups; labels need
not be, since the same name can appear under two folders.
Selection is carried by a tick and a brand tint, kept apart from the
neutral fill that marks the cursor: `surface-selected` and
`surface-hover` resolve to the same value in every theme, so a selected
fill built on them would be indistinguishable from the row under the
pointer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ct empty row ids
Rows stay mounted while the popover animates out, so a click or Enter
could still commit after the picker was closed or disabled. Commits now
require the effective open state.
An empty id is the picker's "no value", so a row with one could never be
told apart from no selection. Such rows are dropped with a dev warning,
and an empty value no longer matches a row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The footer stopped every key but Escape, so Tab from its last action
never reached the popover's focus scope. Focus could leave the
non-modal popover and dismiss it instead of looping back to the
search. Tab now propagates alongside Escape.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
ResourcePicker, a field and popover for choosing one record from a grouped list.The picker is generic over what it lists. A group is a heading with rows under it, and a row is an id, a label and an optional icon, so the same control serves Data Fabric entities, queues, storage buckets or anything else with that shape. The domain lives in the data a consumer passes, not in the component. Picking a person is the deliberate exception: people carry avatars and a resolved reference of their own, so they belong to a dedicated people picker.
Design decisions
Rows commit on click. One resource is one decision, so there is no Cancel and Select pair, which would add a second click to a choice that is already unambiguous. The footer's two slots hold the ways out of the picker instead, such as a link to create a new record. Passing them as slots keeps product routes out of the design system.
Selection is cyan, hover is neutral. The chosen row takes a tick, the brand tint (
brand-subtle) and an accent label, and keeps them under the cursor. The neutralsurface-overlayfill means cursor position and nothing else.surface-selectedis not used for selection because it resolves to the same value assurface-hoverin every theme, so the two states would be indistinguishable.Ids must be non-empty and unique across groups, labels need not be. The same name can appear under two folders, so the id carries the path. An empty id is the picker's "no value", so a row with one is dropped, with a warning in development.
Structure
Built on
CommandandPopover, followingVariablePicker, with the file layoutFolderPickeruses:types.tsfor the consumer-facing API and acomponents/folder for the row, group header, footer and empty state. The empty state is its own component with its own test, since an empty picker and a search that matched nothing are different things and must not collapse into one message.The search is Apollo's own
CommandInputrather than a bespoke field.Stories
Components/Core/Resource Picker: Default, WithDescriptions, CollapsedGroup, WithValue, NotClearable, Disabled, EmptyStates, ContentOnly.Screenshots
Selection in Future Light: the tint follows the theme's brand token.
Empty states: an empty picker and a search that matched nothing say different things.
Checks
Run locally:
biome formatbiome checktsc --noEmitvitest run(resource-picker)npm run buildNote:
src/components/forms/form-plugins.test.tsxfails 26 tests onmainas well, unrelated to this branch.🤖 Generated with Claude Code