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 component: ListboxControl #65801

Open
jameskoster opened this issue Oct 1, 2024 · 21 comments
Open

New component: ListboxControl #65801

jameskoster opened this issue Oct 1, 2024 · 21 comments
Labels
Design System Issues related to the system of combining components according to best practices. Needs Design Feedback Needs general design feedback. [Package] Components /packages/components [Type] Enhancement A suggestion for improvement.

Comments

@jameskoster
Copy link
Contributor

jameskoster commented Oct 1, 2024

A listbox widget presents a list of options and allows a user to select one or more of them. A listbox that allows a single option to be chosen is a single-select listbox; one that allows multiple options to be selected is a multi-select listbox.
w3.org

Listboxes can be used as controls in their own right, and also power more complex components like Comboboxes and Autocomplete inputs.

Listbox Item

Listbox Item

This is the atomic element that makes up the majority of the Listbox component. It can appear as either a checkbox for multi-select listboxes or a radio for single-select listboxes. In the multi-select variant the checkbox is not a checkbox input, merely an element styled to look like one.

Listbox

Listbox

Listbox is comprised of several elements; notably:

  • A scrollable container
  • Listbox Items
  • Listbox Item groups
  • Listbox group headers

ListboxControl

ListboxControl

ListboxControl is the consumable component. It follows the same format as similar components by providing layout and optional elements like a label and help text. Here's a video demonstration of the assembled component:

listbox.mp4
@jameskoster jameskoster added [Type] Enhancement A suggestion for improvement. Needs Design Feedback Needs general design feedback. [Package] Components /packages/components Design System Issues related to the system of combining components according to best practices. labels Oct 1, 2024
@ciampo
Copy link
Contributor

ciampo commented Oct 1, 2024

Thank you for working on this!

In the case of listbox widgets, there is no such thing as "checkbox" or "radio" items . Rather, there is only one type of listbox options — and there can be none, one, or multiple selected at the same time.

To avoid the confusion with radios and checkboxes, I wonder if we should consider a different visual indicator for the selected items (for example, ariakit uses a tick in their multiple selection combobox example)

cc @WordPress/gutenberg-components

@DaniGuardiola
Copy link
Contributor

DaniGuardiola commented Oct 1, 2024

I wonder if we could conceptualize this as a "MenuList", as that'd allow us to, instead of creating a new component, make the existing menu component cover more use cases by making it more open. This mimic the Ariakit structure (see MenuList) and, since we already use it for the menu, it'd make this easier to implement. Overall, I think it'd greatly lower the design, maintenance, education, and support overhead. It'd just be one more Storybook story in that component.

To be clear, what I am proposing is to add the possibility to use our current menu (DropdownMenuV2) without a popover or a trigger. This is already supported by Ariakit and valid in terms of a11y, and the design looks pretty similar to the screencaps in this issue description:

image image

@ciampo's suggestion of using a tick is also covered by this.

@ciampo
Copy link
Contributor

ciampo commented Oct 2, 2024

Listboxes are more generic and can be the foundation of components like CustomSelectControl, ComboboxControl and FormTokenFieldmenus are more specific and have their different roles and semantics. I'm not sure they are compatible to the point of sharing the same underlying implementation.

We could share a lot of the styles, though.

@hanneslsm
Copy link

I like the design of both the checkbox and the radio, but - as pointed out by @ciampo and @DaniGuardiola - they both look like there are from different design languages.
Either the Radio is missing a border or the Checkbox shouldn't have one as in the DropdownMenu2
UX wise, I'd go with a design that shows the border even when not selected. At least on hover.

Another thing is the color blue. I like it.
But currently, most components use black as indicator for being selected.

@hanneslsm
Copy link

hanneslsm commented Oct 2, 2024

Maybe we also should consider "half ticked" (or how would you say that?) the indeterminate boolean property
CleanShot 2024-10-02 at 12 54 37@2x
CleanShot 2024-10-02 at 13 00 05@2x

@ciampo
Copy link
Contributor

ciampo commented Oct 2, 2024

Maybe we also should consider "half ticked" (or how would you say that?) the indeterminate boolean property

As mentioned above, if we're sticking with listbox semantics, there is no such thing as checkbox semantics, and therefore I don't think that indeterminate state would apply.

At most, that could be added to places where we support menu semantics (like DrodownMenu and DropdownMenuV2), since those semantics support menuitemcheckbox roles

@DaniGuardiola
Copy link
Contributor

On my phone right now so can't check, but I think it's worth looking into the semantics that Ariakit has for MenuList. Wouldn't surprise me if it just used plain listbox semantics. The name of Ariakit components does not always correlate to the corresponding semantics.

@jameskoster
Copy link
Contributor Author

there is no such thing as "checkbox" or "radio" items

@ciampo I appreciate they are not semantically checkbox or radio items, but isn't it correct that in terms of behavior listboxes can be configured either as single-select (similar to a select) or multi-select (kind of like a group of checkboxes). Different style treatment can help users identify the behavior, even if it's not semantically accurate.

I suppose I should have noted that the design would not utilise actual checkboxes, they would be elements styled to look like checkboxes. This is a fairly common pattern in design systems; there's a similar example in github issue label assignment UI:

@mirka
Copy link
Member

mirka commented Oct 2, 2024

I'm trying to think what the usage guidelines for this would look like. Specifically, when should one use this Listbox over other similar controls, like:

Single-select

  • SelectControl
  • ComboboxControl (note that in the v2, we'll be able to add groupings/headings)
  • RadioControl

Multi-select

  • CustomSelectControlV2 multi-select mode
  • FormTokenField
  • CheckboxControl

I'm not sure you'd need a separate Listbox component unless it is for a feature that is not covered in the alternatives, like making the items sortable (as in #64686). And if the main feature we want to provide is sortability, then it might make more sense to provide it as a lower-level, headless API (e.g. SortableJS) so consumers can add sortability to a broader variety of designs.

@ciampo
Copy link
Contributor

ciampo commented Oct 2, 2024

I think it's worth looking into the semantics that Ariakit has for MenuList. Wouldn't surprise me if it just used plain listbox semantics.

@DaniGuardiola I believe that they apply role="menu". But even if that wasn't the case, having a checkbox or a radio item just doesn't make sense in a listbox widget at the standard level, right?

I appreciate they are not semantically checkbox or radio items, but isn't it correct that in terms of behavior listboxes can be configured either as single-select (similar to a select) or multi-select (kind of like a group of checkboxes). Different style treatment can help users identify the behavior, even if it's not semantically accurate.

I suppose I should have noted that the design would not utilise actual checkboxes, they would be elements styled to look like checkboxes. This is a fairly common pattern in design systems; there's a similar example in github issue label assignment UI:

@jameskoster I see your point — let me try to rephrase my feedback:

"radio" and "checkbox" are concepts that don't apply in the context of a listbox, and I'm afraid that using them even as just visual cues may set the wrong expectation to the user.

Basically, we should just pick one style (probably checkbox-like), instead of using both checkbox-like and radio-like

I'm trying to think what the usage guidelines for this would look like

@mirka I am seeing this component as a very low-level component that we can leverage to implement all of the other components that you mentioned? Similarly to Composite`

@jameskoster
Copy link
Contributor Author

Basically, we should just pick one style (probably checkbox-like), instead of using both checkbox-like and radio-like

Don't you think there's value in the user understanding at a glance whether they can select one or many options? For example consider the the Status filter in the Pages data view. Currently the UI gives no indication whether the list is multi-select or single-select which leaves questions about the behavior:

If you see empty checkboxes then it's much clearer that you can select multiple options.

@mirka
Copy link
Member

mirka commented Oct 2, 2024

I am seeing this component as a very low-level component that we can leverage to implement all of the other components that you mentioned?

Of the components I listed, those that could possibly use this is ComboboxControl/FormTokenField (which will eventually be a unified as ComboboxControlV2) and CustomSelectControlV2. Ariakit Select and Combobox already have their own subcomponents for these things. So in that case, it would be more natural to just share/reuse the styles, not entire components, because Ariakit is already providing them (behavior + semantics).

The only reason we would want a standalone Listbox component (styles + behavior + semantics) is if we want to use it independently, or in a non-Ariakit-based component. And I'm struggling to imagine such a use case (aside from the sortable case).

@ciampo
Copy link
Contributor

ciampo commented Oct 2, 2024

The only reason we would want a standalone Listbox component (styles + behavior + semantics) is if we want to use it independently, or in a non-Ariakit-based component. And I'm struggling to imagine such a use case (aside from the sortable case).

The example that Jat posted above ( the dataviews filter) could be such a use case?

@mirka
Copy link
Member

mirka commented Oct 3, 2024

The example that Jat posted above ( the dataviews filter) could be such a use case?

I think I would prefer just making CustomSelectControlV2 flexible enough to support that design. Right now, renderSelectedValue doesn't allow that level of customization, but we should if design requires it.

@ciampo
Copy link
Contributor

ciampo commented Oct 4, 2024

The example that Jat posted above ( the dataviews filter) could be such a use case?

I think I would prefer just making CustomSelectControlV2 flexible enough to support that design. Right now, renderSelectedValue doesn't allow that level of customization, but we should if design requires it.

Definitely something to consider. We'd also need to make sure we can offer the listbox itself separate from the popover / dropdown part, though, because this UI pattern can also happen:

Image

@jarekmorawski
Copy link

Great discussion! I'm so glad we're talking about changes to the select component because it's likely the second most-used component in the admin experience (including screens added by plugins). I don't want to stir the pot too much, but I'd love to see us have just one select component with all the necessary features.

Here's my wishlist based on my experience designing admin pages. Most of these features focus on the multi-select variant, but some would also apply to the single-select version.


Support for nested groups

There are many cases where users need to browse items organized in hierarchies. The component should support displaying items not just in groups with individual headings (like in Jay's example), but also in a tree-like view. This would allow users to browse items contained within other items. These groups could be displayed as accordions, enabling users to expand and collapse groups, and to select them with a single click.

  • If a parent is selected, all items inside it are selected.
  • If only some items inside are selected, the parent is displayed in an intermediate state.
  • Clicking again would select or deselect all items inside.

Use cases:

  • Adding categories and subcategories when publishing a post
  • Choosing supported languages in the WPML plugin
  • Selecting accepted currencies in WooCommerce

Image


"Contained" display mode

I agree that the ListboxControl component should work independently of a dropdown or select component. The container should mimic the styling of a standard select field, including border size, color, and radius.

Use cases:

  • Adding a new page and choosing its placement in the menu structure
  • Adding a new category and choosing its parent category

Image


Support for select/deselect all

It would be helpful to allow users to select or deselect all items in the list. Selecting all could be done with a dedicated button at the top of the list (not fixed), while clearing the selection could be handled via an "X" button on the right-hand side of the field. This is a standard feature in many established design systems.

Use cases:

  • Choosing fields in a data sync plugin
  • Selecting recommended linked products in WooCommerce

Image


Display selected items as tags

Currently, the multi-select variant of the CustomSelectControl v2 component provides limited feedback to users about their selections.

Image

Could we display selected items as tags instead and merge this component with FormTokenField? Both serve similar purposes, and showing selections as tags would make it easier for users to reorder or remove items.

Use cases:

  • Adding and reordering tags in a post
  • Adding products displayed in WooCommerce blocks
  • Managing individual filter values in a data table

Image


Support creating new items

Select components often allow users to create new items directly. While we currently support this functionality in some editor workflows using separate buttons, would it be feasible to integrate it into the listbox component?

For instance, a "create new item" option could appear fixed at the bottom of the listbox, opening a modal when activated by clicking or pressing Enter. This feature could also work when searching, allowing users to create new items with the search term set as the default label in the creation interface.

Use cases:

  • Assigning a post to new categories
  • Adding tags to a product in WooCommerce

Image

Hope this helps! cc @brookewp @WordPress/gutenberg-design #66173

@ciampo
Copy link
Contributor

ciampo commented Dec 9, 2024

Hey @jarekmorawski , thank you for sharing your feedback!

Here are some thoughts after reading your feedback — looking forward to more feedback from the rest of the folks involved

I don't want to stir the pot too much, but I'd love to see us have just one select component with all the necessary features.

I'm not sure that's a good approach, as it would incur the risk of blurring the lines too much and mering components and widgets that are semantically different.

While, for example, I agree that we could look at "merging" FormTokenField into ComboboxControl, I don't think that SelectControl and ComboboxControl should be merged under the same component.

I'm also noticing that every example design in your list is showing a combobox, instead of a listbox. A listbox can be part of a combobox widget, but a combobox widget could also use a grid, tree or dialog instead of a listbox for its popup.

Support for nested groups

What you're describing here is a Tree View — and in your example, you're using the treeview as part of a combobox widget. Given than a tree view is a separate widget, with different semantics and affordances from a listbox, I suggest that we look at it and implement it as a separate component. And only if we find strong similarities with listbox, we consider merging the functionalities with the right APIs to differentiate between the two.

As of today, the @wordpress/components package offers the TreeSelect component, but at a quick glance it doesn't support all the required features (and I'd need a deeper look to understand if it's worth improving the existing component vs creating a new one from scratch).

I will also note that building such component can be tricky, especially in terms of adherence to the WAI-aria specs and the accessibility requirements, and that as of today, most libraries (including ariakit) don't offer a TreeView base component.

"Contained" display mode

I agree with you on this one. Exposing an "inline" list as an alternative to the "popover-based" version is something that we intend to do with a few components (Menu, SelectControl, etc..)

Support for select/deselect all

  • Regarding a "select all" item as the first item in the list, we'd need to understand if it can be done in a semantically valid way, since technically that wouldn't be a valid option item, but more of a button.
  • The "deselect all" button shouldn't be an issue for the combobox case. In the "pure" select or tree view case it may need to be a separate button, since the dropdown toggle is already a button in itself;

Since you mentioned that "This is a standard feature in many established design systems", it would be great if you could point to UI libraries or products implementing these patterns, which can help us find inspiration on how to implement it on our side.

Display selected items as tags

  • The CustomSelectControlV2 component is still at its very inception (and not actively being worked on). We don't recommend folks to use it in the code or even as a reference for the time being;
  • In any case, as also explained above, it wouldn't make sense to merge FormTokenField into CustomSelectControl, given how they implement different underlying semantics (listbox vs combobox);
  • Having said that, we could still look alternative ways to represent the selected item(s) when the popover is closed. Although, as explained above, CustomSelectControl's popover trigger is a button, and as such it can't have interactive elements as children components (such as individually selectable and deletable tags).

Support creating new items

  • Your example shows a combobox widget — which is probably the best-suited widget for this UX, since it already displays an input field to type the new item. However, I can still think of a few challenges:
    • while the combobox widget natively allows the user to input a value that doesn't match any suggestion, I'm not sure it is ok to add a new "option" to the list — and if that was the case, we'd probably need to communicate this change to assistive technology users;
    • for non-combobox widgets (eg. listbox), we'd have to display an additional text field for the user to enter the new item — which, again, feels quite bespoke and something we'd need to make sure we implement correctly for assistive technology users;
  • This poses a similar challenge to the "Select all" item: we'd need to find a way to render an additional button that is not actually part of the list of items in the popover, without breaking the semantics. We'd need to determine keyboard interactions and other edge case behaviors too (for example, should / can the "Add new item" button be part of the same composite widget as the items?)

@jarekmorawski
Copy link

Thank you so much for a detailed response, @ciampo. The semantics are essential and I'm sure we can work out a good solution. I now see that merging all components may not be the way forward and that's perfectly fine as long as the different components we create share DNA, are linked together, and it's clear to consumers when and how they should be used.

As of today, the @wordpress/components package offers the TreeSelect component, but at a quick glance it doesn't support all the required features (and I'd need a deeper look to understand if it's worth improving the existing component vs creating a new one from scratch).

It is one of the reasons why we built a custom tree select component at WooCommerce. I believe now is our best chance to improve the one that already exists.

I will also note that building such component can be tricky, especially in terms of adherence to the WAI-aria specs and the accessibility requirements, and that as of today, most libraries (including ariakit/ariakit#4059 (comment)) don't offer a TreeView base component.

That is unfortunate, but I really hope we can find an approach that strikes a good balance between usability and accessibility. There are several important flows in WooCommerce and other products I work on that would benefit from a robust tree select component. Creating a version of it that meets all the accessibility requirements at the expense of speed and efficiency would be suboptimal for complex flows in Woo where every click matters and either takes you closer or further away from your goal.

Since you mentioned that "This is a standard feature in many established design systems", it would be great if you could point to UI libraries or products implementing these patterns, which can help us find inspiration on how to implement it on our side.

Here are examples from Carbon (IBM), Atlassian, Helsinki, and Elvia. All of them also feature other improvements I'm pitching, like nesting and grouping. Atlassian's select component comes with a creatable flag.

I'm not sure it is ok to add a new "option" to the list

Here's an example of a potentially accessible implementation.

we'd need to find a way to render an additional button that is not actually part of the list of items in the popover, without breaking the semantics.

Not sure if it helps, but most implementations I reviewed used a group parent item to replicate the functionality of a select/deselect button. Because it's a regular list item with custom functionality, it didn't require any changes to the semantics. Perhaps the 'Create' button could work similarly, or appear as a single button only when there are no results in the list (in such a case, we'd no longer mix components because it wouldn't be part of the same widget).

@jarekmorawski
Copy link

Example of a combobox where the listbox is combined with an additional footer containing contextual buttons.

Image

@ciampo
Copy link
Contributor

ciampo commented Dec 9, 2024

Thank you for sharing more examples! Here are a few more notes:

  • from what I can see, the "Select all" and "Create new item" functionality are done by adding an extra option item in the list — which fixes the "semantics" part, although it feels a bit hacky. We'll need to test the approach and see if we can make it work well;
  • While the "select all" option makes sense both in a listbox and in a combobox, the "add new item" seems to be a feature only implemented in comboboxes (since, as mentioned before, they already have an input field)
  • Few examples implement a "clear all" button:
    • I haven't found any doing so on with a listbox widget (likely due to the reasons I explained above)
    • Some do with a combobox (for example, Ariakit), although there are concerns in terms of accessibility and keyboard navigation (see the "Remove clear control" section here). For example, MUI's combobox excluded the clear button from tab order.
  • I couldn't find code examples of a nested, collapsible tree view in the links that you posted (please point me to the right examples in case I missed them!). I took some time to research existing implementation, and here's what I found:
    • MUI has a tree view component as part of their premium (paid) MUI x suite of components. The closest version to what we're discussing here is this example (note that some of the features are still in beta)
    • React aria has a Tree component, but it's still in beta and quite incomplete:

@jarekmorawski
Copy link

While the "select all" option makes sense both in a listbox and in a combobox, the "add new item" seems to be a feature only implemented in comboboxes (since, as mentioned before, they already have an input field)

That's true and I think a combobox can be a good start. It's my primary focus as most data in the admin is text or numbers. I'm curious about the other widget types you mentioned. Do we already use them somewhere?

I haven't found any doing so on with a listbox widget (likely due to the reasons I explained above)

Salesforce's design system replaces the input's contents with the selected item. It looks like they're stretching the pill component to fill the container, which isn't great, but does the job, I suppose.

Displaying the X button next to the chevron could work for single-select items (example from an existing DS, another one).

Image

please point me to the right examples in case I missed them!

Here's one and another. Not sure they're great references, though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design System Issues related to the system of combining components according to best practices. Needs Design Feedback Needs general design feedback. [Package] Components /packages/components [Type] Enhancement A suggestion for improvement.
Projects
Status: No status
Status: 🏗️ In Progress
Development

No branches or pull requests

6 participants