base-ui
base-ui copied to clipboard
[Select] Create new `Select` component
Preview: https://deploy-preview-541--base-ui.netlify.app/components/react-select/
- Closes https://github.com/mui/base-ui/issues/547
- Closes https://github.com/mui/base-ui/issues/476
- Closes https://github.com/mui/base-ui/issues/327
- Closes https://github.com/mui/base-ui/issues/328
- Closes https://github.com/mui/base-ui/issues/217
- Closes https://github.com/mui/base-ui/issues/8
- Closes https://github.com/mui/base-ui/issues/82
- Closes https://github.com/mui/base-ui/issues/66
- Closes https://github.com/mui/base-ui/issues/65
- Closes https://github.com/mui/base-ui/issues/64
- Closes https://github.com/mui/base-ui/issues/56
- Closes https://github.com/mui/base-ui/issues/54 (solved via
Select.Valueplaceholderprop) - Closes https://github.com/mui/base-ui/issues/7 (TODO: provide a warning if
defaultValue/valuedon't match any of the options) - Closes https://github.com/mui/base-ui/issues/37 (seem the same as #54)
- Closes https://github.com/mui/base-ui/issues/14 (now invalid)
TODO:
- [x] Integrate with
useField - [x] Renamed
alignMethodprop toalignOptionToTrigger
Olivier's edit: the v2 of https://github.com/mui/material-ui/pull/8023 😄
Netlify deploy preview
https://deploy-preview-541--base-ui.netlify.app/
Generated by :no_entry_sign: dangerJS against 18ef2e85670a055904241085b10e90fd599e7612
Could you please review the list of reported Select bugs and set this PR to close the ones that will be fixed by it?
When you open the select, the page scrollbar disappears causing a layout shift on the right navbar in the doc (https://deploy-preview-541--base-ui.netlify.app/components/react-select/)
The @mui/material select also removes the scrollbar but there is no layout shift (https://mui.com/material-ui/react-select/)
@flaviendelangle interesting. The padding-right: 15px for the scrollbar width offset once it disappears prevents the main content from shifting but not the right navbar content (and end of the top navbar)
I'm wondering if we should prevent scrolling by default at all. I find it annoying when I open a select and something else changes appearance (=the scrollbar disappears). Native implementations differ in this matter - iOS prevents scrolling but doesn't hide the scrollbar, while Windows closes the popup when the page scrolls. IMO locking the scroll is more important in modal windows.
I found an issue that's likely related to scroll locking: https://codesandbox.io/p/sandbox/q7qfjg - when you open the select, there's a small layout shift and you're able to scroll the page horizontally until the whole trigger disappears.
I'm wondering if we should prevent scrolling by default at all. I find it annoying when I open a select and something else changes appearance (=the scrollbar disappears).
The scroll locking changes we made in #604 prevents layout shift issues from disappearing scrollbars. The scroll lock is necessary with the item align anchoring because you can wheel the popup to reveal more items, but this can cause the page to scroll. It's not locked for the trigger align method.
I found an issue that's likely related to scroll locking: https://codesandbox.io/p/sandbox/q7qfjg - when you open the select, there's a small layout shift and you're able to scroll the page horizontally until the whole trigger disappears.
This looks like a bug with the .SelectPopup min-width style that doesn't limit itself by the --available-width.
This solves it:
min-width: min(var(--available-width), calc(var(--anchor-width) + 20px));
There's a thread discussing default "functional styles" to apply to these elements and an API to override them in a thread in the base-ui channel. This could help prevent certain issues like this, including:
- Making the popup scrollable by default
- Limiting the popup's size to the available space (but the above calculation requires the user to use their own custom padding value)
This looks like a bug with the .SelectPopup min-width style that doesn't limit itself by the --available-width.
This solves it:
The repro was taken directly from the demos, so they'll need to have the styles updated.
The Select.Root should be generic over its value (as in the previous implementation), so that value, defaultValue, and onValueChange parameter types are the same.
I imagine we won't support https://github.com/mui/material-ui/issues/20402#issuecomment-619275339 just yet:
https://github.com/user-attachments/assets/95dc7af4-6413-4053-8ddf-21ecd022968c
https://codesandbox.io/p/sandbox/magical-sara-zqhf6x. I think we should open a new issue for it, e.g. https://x.com/devongovett/status/1248306411508916224. One mistake I did there back in the days is I think to not compare values with autocompelte on toLowerCase() mode, like Chrome native select seems to do.
@oliviertassinari actually I did try to add support for it but I couldn't seem to get even the native select to autofill in my browser. I'll try to work off this demo instead.
By default, the selected option inside the popup is aligned to the trigger element. This can be disabled with the alignOptionToTrigger prop
Maybe it's because I am coming from Mateiral UI, but I would prefer aligning the popup to the trigger to be default behavior.
Maybe it's because I am coming from Mateiral UI, but I would prefer aligning the popup to the trigger to be default behavior.
This is the most common default, though Radix uses the alignOptionToTrigger default, as does macOS. Figma also uses this for their selects, so it does exist on the web outside Radix. With scrollable content (large select option lists), it results in the best UX since selection can be done in one single pointer cycle. The docs will list reasoning for certain choices. This will be explained in a documentation template doc on Notion.
- What's the heuristic about giving up on the align option to trigger when close to the edge? I am asking because we have kind of a different behavior based on the options, e.g.
I would assume these two to behave the same way (we have space for the scrolling arrow in both scenarios).
-
Can we explain in this section why the change of the aligning affects whether the scroll is blocked or not. It could be surprising that just because you want things to be aligned differently it affects whether the scroll on the page is available or not.
-
Based on https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-select-only/, we shouldn't support circular navigation when using Up/Down Arrow => we should set
loop: falsein theuseListNavigation.
- Based on https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-select-only/ we should open the listbox when user types printable characters with setting active descendant on the right item:
If the combobox is collapsed and the user types printable characters, the listbox is displayed and receives accessibility focus via aria-activedescendant. This enables users to perceive the presence of the options, and enables assistive technology users to comprehend the size of the list of options.
- Are we handling Home and End keys?
- What's the heuristic about giving up on the align option to trigger when close to the edge? I am asking because we have kind of a different behavior based on the options, e.g.
If the min-height is 100px or less (can be configured in their CSS), or if the reference is within 20px of the viewport edge, it falls back to standard anchoring. I felt the UX was much better in this case, matching macOS' fallback behavior.
Fair point!
Can change this, not sure if we should provide an option if so.
This seems to be a new pattern or suggestion that I haven't seen before, and the native select doesn't appear to do this. It doesn't seem critical to implement for now.
Yes, Home/End are handled