react-combobox: add `allowFreeform` and update default input behavior
After chatting with design, we landed on the following behaviors for Combobox with typability:
- All Comboboxes allow free user input while the combobox is focused
- Combobox with
allowFreeform={true}allows any user input to remain in the combobox input as the value when blurred. Unlike v8, this does not automatically add a new entry to the options list. - Comboboxes without
allowFreeformwill revert to the last selected value or no value when blurred - The current plan is to not support
allowFreeformwithmultiselect, pending discussion (not included in this PR, and does not apply to pill select/pickers)
This is pretty different from Combobox in v8, which prevented user input without allowFreeform, at which point it also added options to the listbox. The rationale for changing it is that:
- Dropdown already covers the use case for a selection widget without typing input, and we want to be able to clearly define when to use each of our components and avoid blurred lines and overlap.
- Adding options to the combobox automatically with
allowFreeformseemed weird to both Yvonne and myself, and it's very possible for authors to re-add that behavior if they want it.
Code-specific changes in this PR:
- activeOption changes based on typing, and first tries to match options after the current active option
-
onOptionClickwas moved to the option component, which allowed me to extend functions likesetOpenandselectOptionreturned by theuseComboboxBaseStatehook from withinuseCombobox(this is why the context values changed) - I added a
clearSelectionfunction touseSelection, which is used when typing clears the current selection (e.g. fully erasing the input value). This will can be used by the "clear selection" button currently being prototyped in design, if we end up adding it.
📊 Bundle size report
| Package & Exports | Baseline (minified/GZIP) | PR | Change |
|---|---|---|---|
| react-combobox Combobox (including child components) |
71.457 kB23.361 kB |
72.723 kB23.746 kB |
1.266 kB 385 B |
| react-combobox Dropdown (including child components) |
70.585 kB23.22 kB |
70.792 kB23.233 kB |
207 B 13 B |
Unchanged fixtures
| Package & Exports | Size (minified/GZIP) |
|---|---|
| react-components react-components: Accordion, Button, FluentProvider, Image, Menu, Popover |
189.024 kB51.924 kB |
| react-components react-components: FluentProvider & webLightTheme |
32.876 kB10.773 kB |
Perf Analysis (@fluentui/react-components)
No significant results to display.
All results
| Scenario | Render type | Master Ticks | PR Ticks | Iterations | Status |
|---|---|---|---|---|---|
| Avatar | mount | 1112 | 1149 | 5000 | |
| Button | mount | 840 | 844 | 5000 | |
| FluentProvider | mount | 1435 | 1399 | 5000 | |
| FluentProviderWithTheme | mount | 560 | 555 | 10 | |
| FluentProviderWithTheme | virtual-rerender | 591 | 564 | 10 | |
| FluentProviderWithTheme | virtual-rerender-with-unmount | 560 | 563 | 10 | |
| MakeStyles | mount | 1723 | 1693 | 50000 | |
| SpinButton | mount | 2176 | 2271 | 5000 |
Asset size changes
Size Auditor did not detect a change in bundle size for any component!
Baseline commit: f17b8604209a099d354c212077832516dcfbbae2 (build)
This pull request is automatically built and testable in CodeSandbox.
To see build info of the built libraries, click here or the icon next to each commit SHA.
Latest deployment of this branch, based on commit ccd4dbf0aab87920d375a1809e8ace4d0432b5e1:
| Sandbox | Source |
|---|---|
| @fluentui/react 8 starter | Configuration |
| @fluentui/react-components 9 starter | Configuration |
Hey Sarah, this looks great! Really happy with this direction for allowFreeform. Here is some design feedback/questions!
1. Selection by typing only works once
Here's what happens for me: I type "Cat" + EnterKey and it selects Cat and closes the popup. I clear the selection and type "Hamster" + EnterKey. Instead of selecting Hamster, it just opens the popup without selecting Hamster. If I blur away, the selection is cleared. When allowFreeform is on, the selection is a custom option even though there's an option that matches the typed value.
I played around with this a lot, and sometimes it works, sometimes it doesn't. No idea what causes the issue haha...
2. I can't use Space in the input area
Since SpaceKey is used as a selection key, I'm not able to type an actual space into the input area. Looks like v8 Combobox solves this by only having EnterKey as the selection key? Is that an ok solution?
3. Chevron should be clickable to trigger popup
Less about the typing interactions, but the user should be able to click the chevron to invoke the popup menu. If they don't invoke the popup via the chevron, the popup should only appear once the user starts typing in the input area or once they use Up/Down ArrowKeys.
Here's the Figma reference for this: https://www.figma.com/file/pI5m9e9hsNkl1odWzxCxNd/Dropdown?node-id=3205%3A28033
4. When allowFreeform is on, how do we account for custom options that match the first few characters of an existing option?
Currently, if I type "Ham" and tab away, it automatically selects Hamster. I'm wondering if we should only select an existing option on EnterKey, so that if a user wants to input "Ham" they can do so without it being overridden when they tab away. Right now, it seems like the only way it keeps "Ham" is if I blur by clicking away.
When allowFreeform is off, it makes sense to override input value with the closest matching option. But not sure if it's weird to have different interactions based on if this prop is on/off.
