diff --git a/docs/pages/api-docs/autocomplete.md b/docs/pages/api-docs/autocomplete.md index 672684ba09d3c2..9b9b34aed76166 100644 --- a/docs/pages/api-docs/autocomplete.md +++ b/docs/pages/api-docs/autocomplete.md @@ -34,6 +34,7 @@ The `MuiAutocomplete` name can be used for providing [default props](/customizat | blurOnSelect | 'mouse'
| 'touch'
| bool
| false | Control if the input should be blurred when an option is selected:
- `false` the input is not blurred. - `true` the input is always blurred. - `touch` the input is blurred after a touch event. - `mouse` the input is blurred after a mouse event. | | ChipProps | object | | Props applied to the [`Chip`](/api/chip/) element. | | classes | object | | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. | +| clearOnBlur | bool | !props.freeSolo | If `true`, the input's text will be cleared on blur if no value is selected.
Set to `true` if you want to help the user enter a new value. Set to `false` if you want to help the user resume his search. | | clearOnEscape | bool | false | If `true`, clear all values when the user presses escape and the popup is closed. | | clearText | string | 'Clear' | Override the default text for the *clear* icon button.
For localization purposes, you can use the provided [translations](/guides/localization/). | | closeIcon | node | <CloseIcon fontSize="small" /> | The icon to display in place of the default close icon. | diff --git a/docs/src/modules/components/MarkdownElement.js b/docs/src/modules/components/MarkdownElement.js index bd60873db434bc..c6df252d97220a 100644 --- a/docs/src/modules/components/MarkdownElement.js +++ b/docs/src/modules/components/MarkdownElement.js @@ -196,6 +196,18 @@ const styles = (theme) => ({ flexShrink: 0, backgroundColor: theme.palette.divider, }, + '& kbd': { + // Style taken from GitHub + padding: '2px 5px', + font: '11px Consolas,Liberation Mono,Menlo,monospace', + lineHeight: '10px', + color: '#444d56', + verticalAlign: 'middle', + backgroundColor: '#fafbfc', + border: '1px solid #d1d5da', + borderRadius: 3, + boxShadow: 'inset 0 -1px 0 #d1d5da', + }, }, }); diff --git a/docs/src/pages/components/autocomplete/ControllableStates.js b/docs/src/pages/components/autocomplete/ControllableStates.js new file mode 100644 index 00000000000000..0400ec88cce01a --- /dev/null +++ b/docs/src/pages/components/autocomplete/ControllableStates.js @@ -0,0 +1,32 @@ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete from '@material-ui/lab/Autocomplete'; + +const options = ['Option 1', 'Option 2']; + +export default function ControllableStates() { + const [value, setValue] = React.useState(options[0]); + const [inputValue, setInputValue] = React.useState(''); + + return ( +
+
{`value: ${value}`}
+
{`inputValue: ${inputValue}`}
+
+ { + setValue(newValue); + }} + inputValue={inputValue} + onInputChange={(event, newInputValue) => { + setInputValue(newInputValue); + }} + id="controllable-states-demo" + options={options} + style={{ width: 300 }} + renderInput={(params) => } + /> +
+ ); +} diff --git a/docs/src/pages/components/autocomplete/ControllableStates.tsx b/docs/src/pages/components/autocomplete/ControllableStates.tsx new file mode 100644 index 00000000000000..9f29501561957c --- /dev/null +++ b/docs/src/pages/components/autocomplete/ControllableStates.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete from '@material-ui/lab/Autocomplete'; + +const options = ['Option 1', 'Option 2']; + +export default function ControllableStates() { + const [value, setValue] = React.useState(options[0]); + const [inputValue, setInputValue] = React.useState(''); + + return ( +
+
{`value: ${value}`}
+
{`inputValue: ${inputValue}`}
+
+ { + setValue(newValue); + }} + inputValue={inputValue} + onInputChange={(event, newInputValue) => { + setInputValue(newInputValue); + }} + id="controllable-states-demo" + options={options} + style={{ width: 300 }} + renderInput={(params) => } + /> +
+ ); +} diff --git a/docs/src/pages/components/autocomplete/FreeSoloCreateOption.js b/docs/src/pages/components/autocomplete/FreeSoloCreateOption.js index 8dab8ef0de46f4..823e82e1151843 100644 --- a/docs/src/pages/components/autocomplete/FreeSoloCreateOption.js +++ b/docs/src/pages/components/autocomplete/FreeSoloCreateOption.js @@ -12,6 +12,7 @@ export default function FreeSoloCreateOption() { { + // Create a new value from the user input if (newValue && newValue.inputValue) { setValue({ title: newValue.inputValue, @@ -25,6 +26,7 @@ export default function FreeSoloCreateOption() { filterOptions={(options, params) => { const filtered = filter(options, params); + // Suggest the creation of a new value if (params.inputValue !== '') { filtered.push({ inputValue: params.inputValue, @@ -34,16 +36,20 @@ export default function FreeSoloCreateOption() { return filtered; }} + selectOnFocus + clearOnBlur id="free-solo-with-text-demo" options={top100Films} getOptionLabel={(option) => { - // e.g value selected with enter, right from the input + // Value selected with enter, right from the input if (typeof option === 'string') { return option; } + // Add "xxx" option created dynamically if (option.inputValue) { return option.inputValue; } + // Regular option return option.title; }} renderOption={(option) => option.title} diff --git a/docs/src/pages/components/autocomplete/FreeSoloCreateOption.tsx b/docs/src/pages/components/autocomplete/FreeSoloCreateOption.tsx index 68883e5a01d99c..e266fbb3fea0f5 100644 --- a/docs/src/pages/components/autocomplete/FreeSoloCreateOption.tsx +++ b/docs/src/pages/components/autocomplete/FreeSoloCreateOption.tsx @@ -12,6 +12,7 @@ export default function FreeSoloCreateOption() { { + // Create a new value from the user input if (newValue && newValue.inputValue) { setValue({ title: newValue.inputValue, @@ -24,6 +25,7 @@ export default function FreeSoloCreateOption() { filterOptions={(options, params) => { const filtered = filter(options, params) as FilmOptionType[]; + // Suggest the creation of a new value if (params.inputValue !== '') { filtered.push({ inputValue: params.inputValue, @@ -33,16 +35,20 @@ export default function FreeSoloCreateOption() { return filtered; }} + selectOnFocus + clearOnBlur id="free-solo-with-text-demo" options={top100Films as FilmOptionType[]} getOptionLabel={(option) => { - // e.g value selected with enter, right from the input + // Value selected with enter, right from the input if (typeof option === 'string') { return option; } + // Add "xxx" option created dynamically if (option.inputValue) { return option.inputValue; } + // Regular option return option.title; }} renderOption={(option) => option.title} diff --git a/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.js b/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.js index ab5ec617e4aa52..db412fd04e4d3b 100644 --- a/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.js +++ b/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.js @@ -92,6 +92,8 @@ export default function FreeSoloCreateOptionDialog() { } return option.title; }} + selectOnFocus + clearOnBlur renderOption={(option) => option.title} style={{ width: 300 }} freeSolo diff --git a/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.tsx b/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.tsx index 19de8ffd897a6d..36b55416cf8e37 100644 --- a/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.tsx +++ b/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.tsx @@ -89,6 +89,8 @@ export default function FreeSoloCreateOptionDialog() { } return option.title; }} + selectOnFocus + clearOnBlur renderOption={(option) => option.title} style={{ width: 300 }} freeSolo diff --git a/docs/src/pages/components/autocomplete/Playground.js b/docs/src/pages/components/autocomplete/Playground.js index f98a595d867dd5..fd118d1029a0ea 100644 --- a/docs/src/pages/components/autocomplete/Playground.js +++ b/docs/src/pages/components/autocomplete/Playground.js @@ -114,6 +114,18 @@ export default function Playground() { blurOnSelect renderInput={(params) => } /> + } + /> + } + /> ); } diff --git a/docs/src/pages/components/autocomplete/Playground.tsx b/docs/src/pages/components/autocomplete/Playground.tsx index caf4c047aea996..9c43250af40ffc 100644 --- a/docs/src/pages/components/autocomplete/Playground.tsx +++ b/docs/src/pages/components/autocomplete/Playground.tsx @@ -112,6 +112,18 @@ export default function Playground() { blurOnSelect renderInput={(params) => } /> + } + /> + } + /> ); } diff --git a/docs/src/pages/components/autocomplete/autocomplete.md b/docs/src/pages/components/autocomplete/autocomplete.md index 6ef7597dbc978e..fed08b4f67f927 100644 --- a/docs/src/pages/components/autocomplete/autocomplete.md +++ b/docs/src/pages/components/autocomplete/autocomplete.md @@ -36,23 +36,26 @@ Choose one of the 248 countries. The component has two states that can be controlled: -1. the "value" state with the `value`/`onChange` props combination. -2. the "input value" state with the `inputValue`/`onInputChange` props combination. +1. the "value" state with the `value`/`onChange` props combination. This state represents the value selected by the user, for instance when pressing Enter. +2. the "input value" state with the `inputValue`/`onInputChange` props combination. This state represents the value displayed in the textbox. > ⚠️ These two state are isolated, they should be controlled independently. -## Free solo +{{"demo": "pages/components/autocomplete/ControllableStates.js"}} -Set `freeSolo` to true so the textbox can contain any arbitrary value. The prop is designed to cover the primary use case of a search box with suggestions, e.g. Google search. +## Free solo -However, if you intend to use it for a [combo box](#combo-box) like experience (an enhanced version of a select element) we recommend setting `selectOnFocus` (it helps the user clear the selected value). +Set `freeSolo` to true so the textbox can contain any arbitrary value. The prop is designed to cover the primary use case of a **search box** with suggestions, e.g. Google search or react-autowhatever. {{"demo": "pages/components/autocomplete/FreeSolo.js"}} -### Helper message +### Creatable + +If you intend to use this mode for a [combo box](#combo-box) like experience (an enhanced version of a select element) we recommend setting: -Sometimes you want to make explicit to the user that he/she can add whatever value he/she wants. -The following demo adds a last option: `Add "YOUR SEARCH"`. +- `selectOnFocus` to helps the user clear the selected value. +- `clearOnBlur` to helps the user to enter a new value. +- A last option, for instance `Add "YOUR SEARCH"`. {{"demo": "pages/components/autocomplete/FreeSoloCreateOption.js"}} diff --git a/packages/material-ui-lab/src/Autocomplete/Autocomplete.js b/packages/material-ui-lab/src/Autocomplete/Autocomplete.js index 6a7243a42128c6..3fd03bfc45aace 100644 --- a/packages/material-ui-lab/src/Autocomplete/Autocomplete.js +++ b/packages/material-ui-lab/src/Autocomplete/Autocomplete.js @@ -244,6 +244,7 @@ const Autocomplete = React.forwardRef(function Autocomplete(props, ref) { ChipProps, classes, className, + clearOnBlur = !props.freeSolo, clearOnEscape = false, clearText = 'Clear', closeIcon = , @@ -541,6 +542,13 @@ Autocomplete.propTypes = { * @ignore */ className: PropTypes.string, + /** + * If `true`, the input's text will be cleared on blur if no value is selected. + * + * Set to `true` if you want to help the user enter a new value. + * Set to `false` if you want to help the user resume his search. + */ + clearOnBlur: PropTypes.bool, /** * If `true`, clear all values when the user presses escape and the popup is closed. */ diff --git a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts index a6f2efbeb1009a..22d3e470954b7c 100644 --- a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts +++ b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts @@ -45,6 +45,13 @@ export interface UseAutocompleteCommonProps { * - `mouse` the input is blurred after a mouse event. */ blurOnSelect?: 'touch' | 'mouse' | true | false; + /** + * If `true`, the input's text will be cleared on blur if no value is selected. + * + * Set to `true` if you want to help the user enter a new value. + * Set to `false` if you want to help the user resume his search. + */ + clearOnBlur?: boolean; /** * If `true`, clear all values when the user presses escape and the popup is closed. */ diff --git a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js index a3fd31eae27111..8ee0afab331902 100644 --- a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js +++ b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js @@ -78,6 +78,7 @@ export default function useAutocomplete(props) { autoHighlight = false, autoSelect = false, blurOnSelect = false, + clearOnBlur = !props.freeSolo, clearOnEscape = false, componentName = 'useAutocomplete', debug = false, @@ -762,7 +763,7 @@ export default function useAutocomplete(props) { selectNewValue(event, filteredOptions[highlightedIndexRef.current], 'blur'); } else if (autoSelect && freeSolo && inputValue !== '') { selectNewValue(event, inputValue, 'blur', 'freeSolo'); - } else { + } else if (clearOnBlur) { resetInputValue(event, value); }