Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[New Primitive] Combobox #1342

Open
benoitgrelard opened this issue Apr 25, 2022 · 61 comments
Open

[New Primitive] Combobox #1342

benoitgrelard opened this issue Apr 25, 2022 · 61 comments
Labels
Difficulty: Hard This issue is likely very difficult Type: New Primitive Request for a new primitive

Comments

@benoitgrelard
Copy link
Collaborator

Mentioned in #1270 and #1334

@benoitgrelard benoitgrelard added Type: New Primitive Request for a new primitive Difficulty: Hard This issue is likely very difficult labels Apr 25, 2022
@kieranm
Copy link

kieranm commented May 1, 2022

In our app we've had to implement...

Comboboxes in modals
Screenshot 2022-05-01 at 16 07 51

Comboboxes in menus (right-click context menu and dropdown menus)
Screenshot 2022-05-01 at 16 09 39

Comboboxes in select menus in forms
Screenshot 2022-05-01 at 16 10 11

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!

@yuriburk
Copy link

@kieranm could you provide a example code?

@jordie23
Copy link

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.

@laozhu
Copy link

laozhu commented May 24, 2022

Is combobox on the road? I should make it by myself for now.

@benoitgrelard
Copy link
Collaborator Author

Hey @laozhu, nothing started on Combobox yet.

@asherccohen
Copy link

Same here, building some additional features on top of Select, cases mentioned above are exactly what we have in mind too.

@djalmaaraujo
Copy link

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.

@selfish
Copy link

selfish commented Aug 1, 2022

@kieranm could you share your implementation?
Is it based on Radix's select?

@statusunknown418
Copy link

I'm really upvoting this, since headlessui/react already has an implementation but is broken for now

@asherccohen
Copy link

asherccohen commented Aug 4, 2022 via email

@thiagovilla
Copy link

thiagovilla commented Sep 16, 2022

Please implement this! From the Discord server.

@cabello
Copy link

cabello commented Sep 26, 2022

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: tailwindlabs/headlessui#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.
CleanShot 2022-09-27 at 11 17 33@2x

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.
CleanShot 2022-09-27 at 11 17 54@2x

@Code-Victor
Copy link

is there any workaround with radix UI only?

@dm-camelonta
Copy link

Agree, would be nice to have a combobox in Radix.

@kof
Copy link

kof commented Oct 26, 2022

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.

@oskarrough
Copy link

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.

@djalmaaraujo
Copy link

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.

🤙

@kieranm
Copy link

kieranm commented Jan 1, 2023

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

@joaom00
Copy link
Contributor

joaom00 commented Jan 4, 2023

If anyone wants to know how to use Radix + Cmdk, here an example.

@smol-honk
Copy link

Super interested in getting a primitive for this!

@PeerRich
Copy link

lets get this cake! Is there somehow a way we can sponsor this PR or similar? Expedite the engineering of it?

@wongmrdev
Copy link

Is using DropdownMenu bad practice for multiselect?

@benoitgrelard
Copy link
Collaborator Author

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.

@charrondev
Copy link

charrondev commented Jun 19, 2023

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.

New Input Element

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.

  • If placed inside of the Trigger it will be hidden when unless the combobox is "open".
  • The input will become focused on activation of the trigger.

Multiple Values

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.

Dynamic/Async Content

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.

Filtered Content

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.

Loading indicator

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.

No Results

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.

Example Structure

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>
    );
}

@franzheidl
Copy link

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. 🙏

@iampeter
Copy link

@franzheidl maybe this could help you: https://ui.shadcn.com/docs/components/combobox

@bzenky
Copy link

bzenky commented Aug 1, 2023

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.

@charliematters
Copy link

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

@kieranm
Copy link

kieranm commented Aug 14, 2023

"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.

@t-keshi
Copy link

t-keshi commented Aug 28, 2023

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

@charrondev
Copy link

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.

@Roeefl
Copy link

Roeefl commented Dec 19, 2023

@charrondev Hey man, how are you? any chance to get a status update on how's it going with the ComboBox feature and some ETA for alpha/beta ? Thanks so much!

@Roeefl
Copy link

Roeefl commented Dec 21, 2023

Also if you need any support you're quite welcome to ask for an extra hand with some small tasks or anything 😸

@charrondev
Copy link

A little update. I'm coming back to this but my plan has changed a fair bit. I've made a couple of a short attempts and learned a bit. This is what I've figured out so far.

Initially I was attempting to make a separate component implementation altogether, but was finding a lot of logic needed to be shared with the Select. I abandoned this attempt as their was too much logic being duplicated. This was informative for me in figuring out a fair bit of the existing logic.

My second attempt tried to add additional functionality to the Select. This attempt ended up being a failure due to an incompatibility between focusing mechanism the two implementations.

Notably:

  • The existing select implementation has a lot of logic in event handlers and effects to keep actual DOM focus on currently selected item and move it between 3 locations
    1. The trigger
    2. The content
    3. The selected item inside the content.
  • A true combobox implementation requires you to keep focus within the input (trigger) at all times. Instead of moving focus onto selected items, focus is indicated using the aria-activedescendant property.

With all of this learned I will be making a third attempt, this time of a separate ComboBox component, although I will try to re-use the content/position logic from Select.

@diegohaz
Copy link

If this is useful to anyone, here's an implementation of Radix's Select component that includes a search input (combobox). You can achieve this by combining Radix with Ariakit (just the part necessary to render the combobox):

Radix Select with Combobox

@matt-miro
Copy link

Heya @charrondev - really appreciate the time and effort you're putting into this. We were wondering if you could share any timelines about the combobox and how we could potentially help to drive it forwards. We're hoping to introduce a combobox into one of our features, and we'd love to use one of Radix their primitives as opposed to composing one ourselves

github-merge-queue bot pushed a commit to tldraw/tldraw that referenced this issue Feb 5, 2024
Reworks search to not be a page and instead to be inline dropdown.

<img width="763" alt="Screenshot 2024-02-05 at 13 22 58"
src="https://github.com/tldraw/tldraw/assets/469604/4e5a8076-62cd-44bb-b8e7-7f5ecdc4af24">


- rework search completely
- rm Search Results css
- uses Ariakit and add appropriate hooks / styling
- I couldn't use Radix unfortunately since they're still working on
adding a Combox: radix-ui/primitives#1342
- I'm open to other suggestions but Ariakit plays nicely with Radix and
keeps things open to migrate to Radix in the future
- fixes bug with not scrolling to right place when having a direct link
- adds categories in the search results - examples / reference / learn
- and adds category icons. Let me know if there's a better policy for
adding new SVG icons cc @steveruizok

### Change Type

- [x] `minor` — New feature

### Test Plan

1. Test searches using normal method for each type (examples, docs,
refs)
2. Test searches using AI for each type (ditto)

### Release Notes

- Docs: rework the search to be an inline dropdown.
@charrondev
Copy link

I'd made some mention of an attempt at this but I lost steam to do it in my personal time, and the work project that we'll need it for keeps getting pushed back (currently my have multiple existing implementations that we'd like to replace including a really janky custom one, react-select, and @react/combobox).

If someone else wants to take a crack at it your welcome to.

@Powerplex
Copy link

Hello :) So we needed a Dropdown AND a Combobox for our design system that manages both single and multiple selection.

At the same time, our whole design system is based on Radix and we want to keep the props cohesive across all our components.

What we did was implementing a compound pattern architecture for our combobox that sticks as much as possible with the "Radix way", but relying on Downshift for all the internals (expect the Popover part which comes from Radix's Popover).

We were asked that our Combobox covers:

  • single AND multiple selection
  • leading icon (optional)
  • clear button (optional)
  • disclosure button (optional)
  • items (listbox) can be rendered inside a Popover OR inline/standalone (for example to build a combobox inside a dialog where you don't need the Combobox.Popover part of the compound)
  • multiple selection display "chips" of selected items next to the typing area of the input (with full keyboard nav support).
  • each chip can be individually dismissed to unselect an item
  • multiple selection mode expands by default when it has too many chips inside, but can also be restricted to fit on a single line (for example if your combobox is inside a sticky navbar)
  • the markup of any item inside the combobox can be customized (must remain non-interactive elements inside items for a11y)
  • can be controlled/uncontrolled (input value, open state AND selected item(s))
  • items can be grouped, each group has its own label
  • optional ItemIndicator to display in each item when selected
  • loading state (spinner), for example when loading new items using an autoSuggest API
  • disabled and readOnly states
  • success/error/alert states
  • auto filtering (not case sensitive by default), but you can opt-out and implement your own filtering logic.
  • can be combined with our own FormField component to wrap as a fieldset and attach optional parts: label, helper message, error message, characters counter, etc.
  • allow custom input values that do not match any item in the list (optional)

This is just for v1, so YES this component is incredibly difficult to get right and cover all needs AND follow the a11y requirements (https://www.w3.org/WAI/ARIA/apg/patterns/combobox/). We are still missing many key features such as autoSelect (highlight the first item matching what the user types), allow more customisation of individual selected item chips, etc

Our implementation is too opinionated to end-up being a proposal for Radix, but feel free to have a look: https://github.com/adevinta/spark/tree/main/packages/components/combobox

Demo: https://sparkui.vercel.app/?path=/docs/experimental-combobox--docs#default

Maybe an audit of the a11y provided by Downshift hooks could be done (I think it is very good) and using Downshift inside a Radix implementation (adding Downshift as a Radix dependency) could help make things easier to implement in Radix for a V1/Draft (it did for us).

@sersavan
Copy link

I've assembled a multi-select component using the native shadcn's components. It's fully in line with design and integrates seamlessly into shadcn's ecosystem. Please, try it out and share your thoughts.
https://shadcn-multi-select-component.vercel.app/

Screenshot 2024-04-11 at 00 50 06

@therealsuji
Copy link

I've assembled a multi-select component using the native shadcn's components. It's fully in line with design and integrates seamlessly into shadcn's ecosystem. Please, try it out and share your thoughts. https://shadcn-multi-select-component.vercel.app/

Screenshot 2024-04-11 at 00 50 06

Any way you could link a gist for this

@sersavan
Copy link

I've assembled a multi-select component using the native shadcn's components. It's fully in line with design and integrates seamlessly into shadcn's ecosystem. Please, try it out and share your thoughts. https://shadcn-multi-select-component.vercel.app/

Any way you could link a gist for this

https://github.com/sersavan/shadcn-multi-select-component

@joaopedrodcf
Copy link

Hello :) So we needed a Dropdown AND a Combobox for our design system that manages both single and multiple selection.

At the same time, our whole design system is based on Radix and we want to keep the props cohesive across all our components.

What we did was implementing a compound pattern architecture for our combobox that sticks as much as possible with the "Radix way", but relying on Downshift for all the internals (expect the Popover part which comes from Radix's Popover).

We were asked that our Combobox covers:

  • single AND multiple selection
  • leading icon (optional)
  • clear button (optional)
  • disclosure button (optional)
  • items (listbox) can be rendered inside a Popover OR inline/standalone (for example to build a combobox inside a dialog where you don't need the Combobox.Popover part of the compound)
  • multiple selection display "chips" of selected items next to the typing area of the input (with full keyboard nav support).
  • each chip can be individually dismissed to unselect an item
  • multiple selection mode expands by default when it has too many chips inside, but can also be restricted to fit on a single line (for example if your combobox is inside a sticky navbar)
  • the markup of any item inside the combobox can be customized (must remain non-interactive elements inside items for a11y)
  • can be controlled/uncontrolled (input value, open state AND selected item(s))
  • items can be grouped, each group has its own label
  • optional ItemIndicator to display in each item when selected
  • loading state (spinner), for example when loading new items using an autoSuggest API
  • disabled and readOnly states
  • success/error/alert states
  • auto filtering (not case sensitive by default), but you can opt-out and implement your own filtering logic.
  • can be combined with our own FormField component to wrap as a fieldset and attach optional parts: label, helper message, error message, characters counter, etc.
  • allow custom input values that do not match any item in the list (optional)

This is just for v1, so YES this component is incredibly difficult to get right and cover all needs AND follow the a11y requirements (https://www.w3.org/WAI/ARIA/apg/patterns/combobox/). We are still missing many key features such as autoSelect (highlight the first item matching what the user types), allow more customisation of individual selected item chips, etc

Our implementation is too opinionated to end-up being a proposal for Radix, but feel free to have a look: https://github.com/adevinta/spark/tree/main/packages/components/combobox

Demo: https://sparkui.vercel.app/?path=/docs/experimental-combobox--docs#default

Maybe an audit of the a11y provided by Downshift hooks could be done (I think it is very good) and using Downshift inside a Radix implementation (adding Downshift as a Radix dependency) could help make things easier to implement in Radix for a V1/Draft (it did for us).

This is really fantastic great job with that @Powerplex 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Difficulty: Hard This issue is likely very difficult Type: New Primitive Request for a new primitive
Projects
None yet
Development

No branches or pull requests