React MUI Autocomplete - Customizing renderInput content - reactjs

I'm using the React MUI Autocomplete component like in the countries example from the official doc.
My goal is to display in bold the country code, as I already did in the renderOption by simply enclosing the option.code value with HTML tags.
import * as React from 'react';
import Box from '#mui/material/Box';
import TextField from '#mui/material/TextField';
import Autocomplete from '#mui/material/Autocomplete';
export default function CountrySelect() {
return (
<Autocomplete
id="country-select-demo"
sx={{ width: 300 }}
options={countries}
autoHighlight
getOptionLabel={(option) => `${option.code} ${option.label}`} // DISPLAY THE CODE
renderOption={(props, option) => (
<Box component="li" sx={{ '& > img': { mr: 2, flexShrink: 0 } }} {...props}>
<img
loading="lazy"
width="20"
src={`https://flagcdn.com/w20/${option.code.toLowerCase()}.png`}
srcSet={`https://flagcdn.com/w40/${option.code.toLowerCase()}.png 2x`}
alt=""
/>
{option.label} (<b>{option.code}</b>) +{option.phone}
</Box>
)}
renderInput={(params) => (
<TextField
{...params}
label="Choose a country"
inputProps={{
...params.inputProps,
autoComplete: 'new-password', // disable autocomplete and autofill
}}
/>
)}
/>
);
}
I cannot find a way to reference the option.code inside the renderInput property, so I cannot figure out how to display the country code in bold also in the renderInput, since the bold is only visible when choosing an option, but not when that option is selected.
Is there a solution for this?

The main problem with this is that MUI Textfields consist of HTML <input/> tags.
Its value can only be of type string which prohibits any direct value styling but you can make use of an startAdornment like so:
...
renderInput={(params) => (
<TextField
{...params}
label="Choose a country"
inputProps={{
...params.inputProps,
autoComplete: "new-password" // disable autocomplete and autofill
}}
InputProps={{
...params.InputProps,
startAdornment: <strong>{params.inputProps.value.split(" ")[0]}</strong>
}}
/>
)}
...
Your next challenge would be to remove the additional country code from the input-value or even better, move to a controlled value approach.

Related

MUI textfield label not floating to the top left properly

I try to use the MUI in my project, and all the input fields I use don't behave correctly.
it supposed to look like this
but when I use it it will look like this
import Box from '#mui/material/Box'
import TextField from '#mui/material/TextField'
import Autocomplete from '#mui/material/Autocomplete'
<Autocomplete
className="mt-3"
size="small"
id="country-select-demo"
options={arr}
autoHighlight
getOptionLabel={(option) => option.label}
renderOption={(props, option) => (
<Box
component="li"
sx={{ '& > img': { mr: 2, flexShrink: 0 } }}
{...props}
>
{option.label}
</Box>
)}
renderInput={(params) => (
<TextField
{...params}
label="Choose.."
inputProps={{
...params.inputProps,
}}
/>
)}
I just found out that uninstalling the react-bootstrap fix this problem.

How to remove the second x button, the left one inside a TextField inside a Autocomplete?

I have a search bar that uses Autocomplete from material ui to provide suggestions, and inside of it i have text field where I take text as input.
The only problem is that when i type anything in the TextField I can see 2 clear buttons (x button) one on the left of the loading screen and one on the right, and when the loading screen disappears i get 2 clear buttons next to each other. I want to remove the one on the left as it looks bad, and I don't know why it's there.
Search.jsx:
<div className={searchClasses.search}>
<Autocomplete
options={isEmpty ? [] : suggestionsList}
freeSolo
style={{width: "100%"}}
getOptionLabel={(option) => option.title}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
margin="normal"
required
fullWidth
autoFocus
loading={loading}
style={{margin: 0}}
classes={{ notchedOutline: classes.input }}
onChange={handleOnChange}
onKeyDown={e => handleKeyDown(e)}
placeholder="Search..."
type="search"
InputProps={{
...params.InputProps,
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
endAdornment: (
<React.Fragment>
{loading ? <CircularProgress color="inherit" size={20} /> : null}
{params.InputProps.endAdornment}
</React.Fragment>
),
classes: { notchedOutline: classes.noBorder }
}}
/>
)}
renderOption={(option, { inputValue }) => {
const matches = match(option.title, inputValue);
const parts = parse(option.title, matches);
return (
<div>
{parts.map((part, index) => (
<span
key={index}
style={{ fontWeight: part.highlight ? 700 : 400 }}
>
{part.text}
</span>
))}
</div>
);
}}
/>
</div>
Solution
Create a new CSS stylesheet (let say styles.css) and add the following code:
input[type="search"]::-webkit-search-cancel-button {
-webkit-appearance: none;
}
Next, import this stylesheet at the beginning of your Search.jsx:
import "./styles.css";
/* Your code here ... */
Explanation
The left "X" cancel button is present because Webkit-based browsers such as Chrome and Safari automatically adds the cancel button to all <input type="search"> elements. Since your TextField element has the prop type="search", it renders <input type="search"> to the screen and hence your browser automatically adds the "X" button.
The default "X" button can be selected using the ::-webkit-search-cancel-button psuedo-element selector. In our style.css, we select all default "X" buttons in all <input type="search"> elements and we hide them using -webkit-appearance: none;

How can I set the placeholder of Autocomplete in a new line?

I'm using Autocomplete of material-ui and I have the list of the selected values and the placeholder in the same area. I would like to set the placeholder in a new line. I'm able to change the style of placeholder using makeStyles but I didn't find a way to going in a new line.
const useStyles = makeStyles ({
placeholder: {
"& input::placeholder": {
display: "block",
},
})
renderInput={(params) => (
<TextField
/* eslint-disable react/jsx-props-no-spreading */
{...params}
classes={{root: classes.placeholder}}
label="Selected Options"
placeholder="Availables Options"
variant="outlined"
color="primary"
/>
)
You can override the renderTags method, add a wrapper component around all selected tags, and set the wrapper width to 100% to push the placeholder element down like this:
<Autocomplete
multiple
options={top100Films.map((option) => option.title)}
renderTags={(value: string[], getTagProps) => (
<div style={{ width: "100%" }}>
{value.map((option: string, index: number) => (
<Chip
variant="outlined"
label={option}
{...getTagProps({ index })}
/>
))}
</div>
)}
renderInput={(params) => <TextField {...params} />}
/>
Live Demo

How can I change the width of Material UI Autocomplete popover

When using the Select from Material-UI, there's a prop there called 'autoWidth' which sets the width of the popover to match the width of the items inside the menu.
Is there a similar option for the Autocomplete component?
what i'm trying to achieve is that the width of the TextField is independent of the width of the menu, and the width of the menu is determined by the menu items rather than a hard-coded 'width.
What I managed to find so far is an option to provide width to the 'paper' component using classes (see code below), but it's independent of the actual items' width and the position of the paper isn't adjusted to stay inside the window.
const styles = (theme) => ({
paper: {
width: "450px"
}
});
function ComboBox(props) {
return (
<Autocomplete
id="combo-box-demo"
options={top100Films}
classes={{
paper: props.classes.paper
}}
getOptionLabel={(option) => option.title}
style={{
width: 300,
paddingLeft: "100px"
}}
renderInput={(params) => (
<TextField {...params} label="Combo box" variant="outlined" />
)}
/>
);
}
link to codesandbox
What i'm trying to achieve is a similar behavior to this codesandbox but using the Autocomplete component. Notice that width of the pop-up menu is taken from the menu items while the width of the Select component is hard-coded.
To have a dynamic menu based on elements inside the menu itself you need to customize the Autocomplete's PopperComponent property in this way:
Define a custom Popper:
const PopperMy = function (props) {
return <Popper {...props} style={styles.popper} placement="bottom-start" />;
};
In Popper style, set the width to "fit-content":
const styles = (theme) => ({
popper: {
width: "fit-content"
}
});
Pass the component PopperMy to Autocomplete:
<Autocomplete
PopperComponent={PopperMy}
...
/>
Here is your codesandbox modified.
Did you look into 'fullWidth' attribute of autocomplete api?
https://material-ui.com/api/autocomplete/
Here is an example which I think will fulfill your requirement
https://codesandbox.io/s/full-width-autocomplete-ph30d?file=/demo.js
fullWidth if true, will take the width of its container:
<Autocomplete fullWidth sx={{ width: 600 }} />}
in MUI v5 you can use the sx prop.
According to material official documentaion you can pass style props:
<Autocomplete
id="combo-box-demo"
options={top100Films}
style={{ width: "450px" }}
getOptionLabel={(option) => option.title}
renderInput={(params) => (
<TextField {...params} label="Combo box" variant="outlined" />
)}
/>
Here is the demo:https://codesandbox.io/s/material-demo-forked-qon1b

Using react material-ui autocomplete with loading and freeSolo props

I have been using material-ui's autocomplete in freeSolo mode. I would like to show a loading text while options are being loaded. It isn't working as expected currently. But loading text is shown when freeSolo is disabled. The same behaviour is seen for no-options text also. Is it possible to show loading and no options text in freeSolo mode?
Ref: https://codesandbox.io/s/material-demo-mqtk8
In the above example, if freeSolo is set as false, loading text and no options text is shown as expected.
Using this prop loadingText can you set your own loading text.
You can see this MaterialUi documentation, here is the link Material UI AutoComlete API
<Autocomplete
freeSolo
id="asynchronous-demo"
style={{ width: 300 }}
open={open}
onOpen={() => {
setOpen(true);
}}
onClose={() => {
setOpen(false);
}}
getOptionSelected={(option, value) => option.name === value.name}
getOptionLabel={(option) => option.name}
options={options}
loading={loading}
loadingText="Your loading text here"
renderInput={(params) => (
<TextField
{...params}
label="Asynchronous"
fullWidth
variant="outlined"
InputProps={{
...params.InputProps,
endAdornment: (
<React.Fragment>
{loading ? (
<CircularProgress color="inherit" size={20} />
) : null}
{params.InputProps.endAdornment}
</React.Fragment>
)
}}
/>
)}
/>

Resources