Open benoitgrelard opened 2 years ago
In our app we've had to implement...
Comboboxes in modals
Comboboxes in menus (right-click context menu and dropdown menus)
Comboboxes in select
menus in forms
If the team aren't already working on this, we would be happy to contribute back to the library! But it would be good to understand how comboboxes would fit in compatibly with the existing dialogs, selects, context menus, etc.
Would each of these components expose their own combobox functionality (something like ContextMenu.Search
, Select.Search
), or would there be a common combobox component that could just be plugged in to any other component?
It might also be a good idea to think about how this would fit in with the existing type-ahead logic.
Thanks!
@kieranm could you provide a example code?
To add a use case, not sure if this is the right component for it or whether it'd be part of the Select
component, but we basically just want to be able to use Select
with the ability to select multiple options.
Is combobox on the road? I should make it by myself for now.
Hey @laozhu, nothing started on Combobox yet.
Same here, building some additional features on top of Select, cases mentioned above are exactly what we have in mind too.
We are actively replacing our components with radix components in our library; this filtered/searchable component, similar to what react-select does, would be amazing.
@kieranm could you share your implementation?
Is it based on Radix's select
?
I'm really upvoting this, since headlessui/react
already has an implementation but is broken for now
I'm under the same impression, I tried it too and it doesn't feel ready to be used. Also, the fact that their examples are using tailwind doesn't really help for a headless solution.
Kind Regards Asher
From: Alvaro Aquije @.>
Sent: Thursday, August 4, 2022 5:27:36 PM
To: radix-ui/primitives @.>
Cc: Asher Cohen @.>; Comment @.>
Subject: Re: [radix-ui/primitives] [New Primitive] Combobox
(Issue #1342)
I'm really upvoting this, since headlessui/react already has an implementation but is broken for now
β Reply to this email directly, view it on GitHubhttps://nam12.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fradix-ui%2Fprimitives%2Fissues%2F1342%23issuecomment-1205410410&data=05%7C01%7C%7Ca0c542530b96454ee76308da762ddc6e%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637952236599313563%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=3zduzpCdtqQ4vCHr4mzmqCCSZBeVjQEBLSivLhp8WM4%3D&reserved=0, or unsubscribehttps://nam12.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAIIV4EMFNCYZHKWIOFI5WPLVXPOORANCNFSM5UIGGUFA&data=05%7C01%7C%7Ca0c542530b96454ee76308da762ddc6e%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637952236599469787%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=VnlAi1Xa0nHkYr6quYsKgt%2FHhnC3Zo%2Bknr8%2BCGqMv2s%3D&reserved=0. You are receiving this because you commented.Message ID: @.***>
Please implement this! From the Discord server.
I use headlessui combobox for two things: command bar (cmd+k) and emoji autocomplete (when you type : in github, slack). For the second use case it broke my textarea: https://github.com/tailwindlabs/headlessui/issues/1718 I will likely implement this manually. Just sharing the use cases, I think that could help in the event this is implemented.
Screenshots for reference.
A shortcut (cmd+k) makes a dialog with this combobox show up, it can be filtered using fuzzy (outside the concern of the component) and navigated with arrows, enter to select.
Same combobox component, shows up once you hit a threshold :as
(colon followed by at least these many letters), you can navigate up/down, the issue I have right now is that Combobox hijacks left/right arrows so I lose the textarea functionality.
is there any workaround with radix UI only?
Agree, would be nice to have a combobox in Radix.
I have been using downshift in the meantime and it made me realize how hard it is to create a combobox that is truly reusable for all the purposes. Its a weird component in general, it can be used for autocomplete, filtering, search, be always open or as a dropdown etc. All those cases change the behavior for focus management and input update dramatically.
At this point I would even consider that radix shouldn't implement a combobox but rather create a documentation on using radix together with downshift.
Also worth noting: based on the Open UI research on the "select" component, Microsoft and Chromium (afaik) have started work on a new, additional <selectmenu>
element.
We decided to go with Autcomplete components when there are more than 15 options to show. This is somehow a recommendation from our A11Y specialist. We implemented using the UK's autocomplete component and provided a react way using our API's.
Reference: https://vip-design-system-components.netlify.app/?path=/story/form-autocomplete--default
If we want to show less than 15 options, we use a simple browser Select component.
If we need to show more, we are sticking with the pattern of using the Autocomplete.
This combination seems to be the most optimized accessible options to our customers, and with the simpler API.
π€
We have recently moved to CmdK which uses Radix under the hood. It can be used for modals or popovers. https://github.com/pacocoursey/cmdk
If anyone wants to know how to use Radix + Cmdk, here an example.
Super interested in getting a primitive for this!
lets get this cake! Is there somehow a way we can sponsor this PR or similar? Expedite the engineering of it?
Is using DropdownMenu bad practice for multiselect?
Is using DropdownMenu bad practice for multiselect?
As is, yes unless you re-write a bunch of accessibility yourself, they wouldn't carry the same semantic unfortunately.
What do you mean by accessibility?
What does carrying the same semantic mean?
what are our options for multiselect?
The accessibility/semantic of dropdown menu are carrying are those of a "menu button" aria pattern, not a "listbox" pattern. Additionally, menus don't hold a value (or values, in the case of select). Also, they are not form elements, so wouldn't participate in form events like change events bubbling etc.
Ultimately, they may "look" similar, but they are not the same component.
What do you mean by accessibility?
https://developer.mozilla.org/en-US/docs/Learn/Accessibility/What_is_accessibility
What does carrying the same semantic mean?
https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML#good_semantics
Accessibility is one of the main selling points of Radix: https://www.radix-ui.com/docs/primitives/overview/accessibility
@benoitgrelard Thanks for the detailed explanation. It seems like it's not illegal to use the DropdownMenu
if accessibility in my app is not a concern (Thanks @luchsamapparat for the definitions). I can use the DropdownMenu
, modified with added state control, until a MultiSelect
is implementented in radix-ui
.
β¦ if accessibility in my app is not a concern
Accessibility is always a concern of any app, as you do not know who will end up using the app. Perhaps the only case where it could be considered not a concern is if you are developing an app only for your own self to consume and if you are able (any of us could end up with a disability in the future tooβ¦)
Is there a plan for when the Combobox will be available?
Hey @benoitgrelard, is there any interest in working with outside collaborators on this one? I've got a need and would happily help out if y'all still intend to ship this!
Because ... I get it π
i'm out here representing the 415 and showing vivid interest for Combobox
. Everyone loves that Linear feel, it's an undeniable craze...
Aside from that I wanted to ask if there was a typeahead
API or further reading about it's implementation? I see it briefly mentioned as a feature on various component pages. Does typeahead
hold a state that could be accessed? This would help me implement something like showing the typed characters in header-label, without the actual input box.
Great job with Radix btw.
I'm with @chaance in this!
I would love to help in any way possible.
I think that the problem with Combobox is that its a broad concept, that cover a lot of scenarios:
I'm certainly not an accessibility expert, but my impression is that maybe a single abstraction to rule all use cases maybe not the best way to tackle the problem, maybe we should try to split in more abstractions?
Hey @benoitgrelard, is there any interest in working with outside collaborators on this one? I've got a need and would happily help out if y'all still intend to ship this!
Because ... I get it π
Hey @chaance! Yes, Combobox
is definitely something we'd like to add to the library.
I think that the problem with Combobox is that its a broad concept, that cover a lot of scenarios:
I want the search input to be the trigger of the listbox popover I want both the search input and the listbox visible without a popover I want a button trigger that spawn a popover with both search input and listbox Multiple values support Items may come from an async operation (loading state, error state, etc.) Probably I'm missing a lot of other scenarios..
Following what @cloud-walker talks about here, I feel like perhaps a good way to start getting the ball rolling here would be to create some very high-level prototypes of APIs (away from full keyboard/accessibility/etc concerns for now) so we can narrow down our focus. We need to learn a few things so we can decide what we are building, ie. one component supporting multiple use-case, multiple components, etc.
Would that be something you could start looking at @chaance? Feel free to DM me on Discord too btw!
βοΈ
Happy to see things are happening here @benoitgrelard @chaance!
I think some inspiration could be taken from the React Aria ComboBox: https://react-spectrum.adobe.com/react-aria/ComboBox.html
The APG Combobox pattern is also a great source of knowledge, where a few examples are listed. The "Editable Combobox With List Autocomplete Example" is what I would expect of a Radix Combobox primitive, providing some flexibility on when the popover opens. Multi-selection is out of scope IMO β probably deserves its own primitive.
Would be great if you could keep us posted on what you're thinking. Also happy to help out in any way possible!
FWIW, I also think that Ariakit has a good and flexible implementation Combobox component: https://ariakit.org/components/combobox
I'd love the ability to have non-selectable items within the popover that trigger actions instead.
This is useful for something like
"Select a filter (option) or create a new filter (action/link)"
Here's something similar I found in GitLab:
I just requested this feature for the Select here: https://github.com/radix-ui/primitives/issues/2120, but maybe its better suited for the ComboBox (or both)?
To support this, it seems the SelectContent
would render with role="dialog"
and then have an additional component SelectOptionList
or SelectListBox
to outline the role="listbox"
?
<button id="popover-trigger" aria-haspopup="dialog" aria-controls="popover-content">
Filter 1
</button>
<div id="popover-content" role="dialog" aria-modal="true" aria-labelledby="popover-title" aria-describedby="popover-description">
<h2 id="popover-title">Select a filter or create one</h2>
<p id="popover-description">This popover contains a listbox and action items for selecting or creating a filter.</p>
<div role="listbox" aria-label="Select a filter">
<div role="option" aria-selected="true">Filter 1</div>
<div role="option">Filter 2</div>
<div role="option">Filter 3</div>
</div>
<div role="group">
<button type="button" role="button">Create a filter</button>
<button type="button" role="button">Manage filters</button>
</div>
</div>
which I'd imagine would look something like
<ComboBox>
<ComboBox.ButtonTrigger>
<ComboBox.Value />
</ComboBox.ButtonTrigger>
<ComboBox.Content>
<ComboBox.Title>Select a filter or create one</h2>
<ComboBox.Description>This popover contains a listbox and action items for selecting or creating a filter</ComboBox.Description>
<ComboBox.ListBox>
<ComboBox.Option value="filter-1">
Filter 1
</ComboBox.Option>
<ComboBox.Option value="filter-2">
Filter 2
</ComboBox.Option>
</ComboBox.ListBox>
<ComboBox.Separator />
<ComboBox.Group>
<ComboBox.Item onClick={onCreateFilterClick}>
Create a filter
</ComboBox.Item>
<ComboBox.Item asChild>
<a href="/filters/manage">
Manage filters...
</a>
</ComboBox.Item>
</ComboBox.Group>
</ComboBox.Content>
</ComboBox>
I believe the Combobox component by Kobalte for SolidJS is well-designed and highly adaptable. You can find more information about it at: https://kobalte.dev/docs/core/components/combobox
Hi! Are there any updates on this? It would be great to have this primitive. Is there any way the community can help?
In case this helps anyone else, I saw that shadcn has a Combobox component based on Radix and cmdk component by pacocoursey.
It's not exactly what I'm looking for in a combobox, but I'm taking some inspiration from this component composition to see if I can create a multi-select version (similar to downshift multiple selection)
I definitely love the UI / UX that a combobox would be able to provide, there's a lot of similar components in Radix already so it seems achievable, but the range of flexibility required to meet the needs of any UI likely will require something that is highly composable. Love the brainstorming in this thread already.
@landondurnan checkout Fancy Box by @mxkaske for an example of a multi-select based on this cmdk+radix-ui combo. Might be a good inspiration as well.
Experimented with combining Downshift + Tanstack Virtual which in general worked though got more complex trying to get sub commands working the way I want so currently sticking with cmdk in a radix dialog, based on ShadCN π₯
OMG, I'm facing the same challenge. I have to put comboboxes inside of select and dropdown in a very limited time because our designers believe that these kinds of UI/UX are perfect for users (well..., I agree with them too π).
I'm currently trying to use ariakit/react to implement it, but since our project primarily uses Radix UI, I'm really looking forward to seeing the Combobox primitive being released π β€οΈ
I'd be interested in giving this a shot:
I'm envisioning an API similar to <Select />
but with a few key variations and options, following the guidelines here https://www.w3.org/WAI/ARIA/apg/patterns/combobox/
Notably I intend to implement only the "Listbox" popup type described there and not the Grid or tree popups.
It would differ from the <Select />
primitive in a few ways.
There will be an additional required <ComboBox.Input />
component that can be placed wherever desired. This will hold the value the list is being filtered by. Some people may choose to display this in place place of the trigger or inside the Content.
There will be an additional prop on the root allowMultipleValues
. When set a user will be able to select multiple values. The type signatures of the onValueChange
will now require a function signature accepting an array of strings.
Comboboxes are often used with a dynamic list of items that needs to be fetched asynchronously and can't all be displayed at once. To indicate that you will be needing this add the async
prop to the <ComboxBox.Root async />
.
The <ComboBox.Content />
will then accept function children in addition to ReactNode
children of the following signature: (filter: string) => React.ReactNode
. These functions will be called with the current input value on every render to generate additional children.
The <ComboBox.Value>
component will no longer accept empty children as we don't necessarily have an item that can be portalled up here. Instead children
will be required an must be either a ReactNode
or a function with the following signature (for uncontrolled component usage) (values: string[]) => React.ReactNode
. Any asynchronous loading of these values need to be handled by the developer using the component.
The rendered children of <ComboBox.Content />
will be iterated in order and have an additional filtering pass where a case-insensitive String.prototype.includes()
call is performed on the ComboBox.ItemText
value of each one. If the item text does not include the current filter then the item will not be rendered.
If a content function is performing dynamic data fetching it may need a way to indicate that results are loading. In this case, a <ComboBox.LoadingIndicator>
with react children may be rendered. These will be announced in an accessible way to screen readers. The visual appearance and determination of when this should appear will not be controlled by the ComboBox
and instead delegated to the function rendering it.
Since the results are being filtered care it is possible no results will be matching our input filter. An optional <ComboBox.NoResultsMessage>Message Here</ComboBox.NoResultsMessage>
can be placed which will conditionally render when there are no matched results.
const ComboBoxItem = React.forwardRef(({ children, className, ...props }, forwardedRef) => {
return (
<ComboBox.Item className={classnames('ComboBox.Item', className)} {...props} ref={forwardedRef}>
<ComboBox.ItemText>{children}</ComboBox.ItemText>
<ComboBox.ItemIndicator className="ComboBox.ItemIndicator">
<CheckIcon />
</ComboBox.ItemIndicator>
</ComboBox.Item>
);
});
Static Values
function MyComboBox() {
return (
<ComboxBox.Root>
<ComboxBox.Trigger aria-label="Food">
<ComboxBox.Value placeholder="Select a fruitβ¦" />
<ComboxBox.Icon>
<ChevronDownIcon />
</ComboxBox.Icon>
</ComboxBox.Trigger>
<ComboxBox.Portal>
<ComboxBox.Content>
<ComboBox.Input
placeholder="Filter fruits"
aria-label="Filter fruits"
/>
<ComboxBox.ScrollUpButton>
<ChevronUpIcon />
</ComboxBox.ScrollUpButton>
<ComboxBox.Viewport>
<ComboxBox.Group>
<ComboxBox.Label>Fruits</ComboxBox.Label>
<ComboBoxItem value="apple">Apple</ComboBoxItem>
<ComboBoxItem value="banana">Banana</ComboBoxItem>
<ComboBoxItem value="blueberry">
Blueberry
</ComboBoxItem>
<ComboBoxItem value="grapes">Grapes</ComboBoxItem>
<ComboBoxItem value="pineapple">
Pineapple
</ComboBoxItem>
</ComboxBox.Group>
<ComboxBox.Separator />
<ComboxBox.Group>
<ComboxBox.Label>Vegetables</ComboxBox.Label>
<ComboBoxItem value="aubergine">
Aubergine
</ComboBoxItem>
<ComboBoxItem value="broccoli">
Broccoli
</ComboBoxItem>
<ComboBoxItem value="carrot" disabled>
Carrot
</ComboBoxItem>
<ComboBoxItem value="courgette">
Courgette
</ComboBoxItem>
<ComboBoxItem value="leek">Leek</ComboBoxItem>
</ComboxBox.Group>
<ComboxBox.Separator />
<ComboxBox.Group>
<ComboxBox.Label>Meat</ComboxBox.Label>
<ComboBoxItem value="beef">Beef</ComboBoxItem>
<ComboBoxItem value="chicken">Chicken</ComboBoxItem>
<ComboBoxItem value="lamb">Lamb</ComboBoxItem>
<ComboBoxItem value="pork">Pork</ComboBoxItem>
</ComboxBox.Group>
</ComboxBox.Viewport>
<ComboxBox.ScrollDownButton>
<ChevronDownIcon />
</ComboxBox.ScrollDownButton>
</ComboxBox.Content>
</ComboxBox.Portal>
</ComboxBox.Root>
);
}
Dynamic Values
function MyOptionLoader(props: { filter: string }) {
// Not provided by the combobox. This example uses the `react-query` and `axios` libraries to perform data fetching.
const myResults = useQuery<Array<{ label: string; value: string }>>({
queryFn: pDebounce(async () => {
const response = await axios.get(`/api/my-api?query=${props.filter}`);
return response.data;
}, 100),
queryKey: ["autocomplete", props.filter],
});
if (!myResults.data) {
return (
<ComboBox.LoadingIndicator>
<LoadingSpinner />
</ComboBox.LoadingIndicator>
);
}
return (
<>
{myResults.data.map((item, i) => {
return (
<ComboBoxItem key={i} value={item.value}>
{item.label}
</ComboBoxItem>
);
})}
{myResults.isFetching && (
// Even though we might have started fetching a set of data.
<ComboBox.LoadingIndicator>
<LoadingSpinner />
</ComboBox.LoadingIndicator>
)}
</>
);
}
function MyComboBox() {
const [values, setValues] = useState<string[]>([]);
return (
<ComboxBox.Root async value={values} onValueChange={setValues}>
<ComboxBox.Trigger aria-label="Food">
<ComboxBox.Value placeholder="Select a fruitβ¦">
{values.map((value) => (
// One notable pitfal here, we may not have necessarily loaded our "values" from anywhere yet. This will be the responsibility of the end user to reflect some kind of loading indicators here or fetch the data if it is
<span>{value}</span>
))}
</ComboxBox.Value>
<ComboxBox.Icon>
<ChevronDownIcon />
</ComboxBox.Icon>
</ComboxBox.Trigger>
<ComboxBox.Portal>
<ComboxBox.Content>
{(filter: string) => {
return (
<>
<ComboBox.Input placeholder="Filter fruits" aria-label="Filter fruits" />
<ComboxBox.ScrollUpButton>
<ChevronUpIcon />
</ComboxBox.ScrollUpButton>
<ComboxBox.Viewport>
<MyOptionLoader filter={filter} />
</ComboxBox.Viewport>
<ComboxBox.ScrollDownButton>
<ChevronDownIcon />
</ComboxBox.ScrollDownButton>
</>
);
}}
</ComboxBox.Content>
</ComboxBox.Portal>
</ComboxBox.Root>
);
}
Is there any update on this?
We are currently using Radix Select under the hood for the Select component in our design system, and we urgently need combobox functionality.
As a spike, we have included a text input as the first item in Select.Content and simply filter the option values by whatever is being typed in the field, which works reasonably well. However, Radix managing the focus (as desirable as it is for a standard Select) kind of keeps us from progressing here: In order to focus the field, the user has to click into it. We have not yet found a way to focus the input when the Select content opens.
Also, when typing one keystroke into the field, Radix will do what it needs to do (jumping to and focussing the first Select Option beginning with the typed letter) in order to implement standard select behaviour, however this would require the user to click into the input field yet again in order to type in more letters to filter by a string that is longer than a single character. In order to override this beahviour, we would need to have control over the focus, at least as long as there is no ComboBox
in Radix-UI.
I am aware of this issue https://github.com/radix-ui/primitives/issues/1602, and we have found that Radix internally uses utilities called focus-scope and focus-guard, which come with a Read Me discouraging their use outside of the core radix-ui library, but which could β potentially β help us to manage the focus as we need, at least so we can implement a stop-gap version of a combobox / select with an input. We have not yet managed to use these with what we need to achieve, either.
Any hints on what the plans are to move on with ComboBox
if any, or any other info as to how to manage the focus ourselves would be highly appreciated. π
@franzheidl maybe this could help you: https://ui.shadcn.com/docs/components/combobox
Is there any update on this?
We are currently using Radix Select under the hood for the Select component in our design system, and we urgently need combobox functionality.
As a spike, we have included a text input as the first item in Select.Content and simply filter the option values by whatever is being typed in the field, which works reasonably well. However, Radix managing the focus (as desirable as it is for a standard Select) kind of keeps us from progressing here: In order to focus the field, the user has to click into it. We have not yet found a way to focus the input when the Select content opens.
Also, when typing one keystroke into the field, Radix will do what it needs to do (jumping to and focussing the first Select Option beginning with the typed letter) in order to implement standard select behaviour, however this would require the user to click into the input field yet again in order to type in more letters to filter by a string that is longer than a single character. In order to override this beahviour, we would need to have control over the focus, at least as long as there is no
ComboBox
in Radix-UI.I am aware of this issue #1602, and we have found that Radix internally uses utilities called focus-scope and focus-guard, which come with a Read Me discouraging their use outside of the core radix-ui library, but which could β potentially β help us to manage the focus as we need, at least so we can implement a stop-gap version of a combobox / select with an input. We have not yet managed to use these with what we need to achieve, either.
Any hints on what the plans are to move on with
ComboBox
if any, or any other info as to how to manage the focus ourselves would be highly appreciated. π
Same here, just passed by the same situation, comboBox will be a very welcome component.
Personally I down-voted your comment as I felt it didn't add anything positive to the issue. Radix is great, and this feature would be fantastic, but it is fundamentally a free service provided by people we can assume are not paid to do this.
Perhaps I've misread your tone, but I'm not sure what other purpose your message had?
I'm happy with the shadcn alternative which appears to retain the radix and accessibility benefits
"Still waiting", "where is this", "me too" etc are unhelpful comments. I don't know how you meant it, but other people and the maintainers might think you are entitled and are likely to ignore you. It also notifies everyone subscribed and adds nothing we didn't already know. It is important to call it out because, if people don't, it just encourages others to add to the spam (as we can see above!).
Just leave an upvote on the main post like everyone else, please! Everyone here is very friendly (as this whole thread demonstrates) but also we should strive to keep the discussion high quality and it is absolutely right to give feedback with downvotes when people deviate from that.
I understand that my implementation might not be elegant, but it works well for my specific use case.
multi-select-example: https://codesandbox.io/p/sandbox/optimistic-edison-g98wgy
Just a bit of an update since I last proposed an implementation and started working on it. I got a chunk of the way there but it's quite complex and I won't have time to get a draft PR up until Thanksgiving most likely.
Mentioned in #1270 and #1334