Customize handleOptionClick in renderOption of MUI Autocomplete - reactjs

So this is my code:
{/* Search suggestions Autocomplete Field */}
{suggestionsFetched && <Autocomplete
disablePortal
id="combo-box-demo"
options={searchSuggestions?.salons.map((salon) => ({
type:'salon',
id:salon.id,
label:salon.name.toLowerCase()
}))
.concat(searchSuggestions?.services.map((service) => ({
type:'service',
id:service.id,
label:service.name.value.toLowerCase()
})))}
renderOption={(props, option) => {
return (
<li
{...props}
key={option.id}
>
<span style={{
fontWeight:'bolder',
textTransform:'capitalize'
}}>{option.type}:</span>
{"\u00A0"}
<span style={{
textTransform:'capitalize'
}}>{option.label}</span>
</li>
);
}}
popupIcon={<Search color='secondary' />}
sx={{
width: '100%',
[`& .${autocompleteClasses.popupIndicator}`]: {
transform: "none"
}
}}
renderInput={(params) => <TextField placeholder="Search" {...params} />}
/>}
The data I am displaying are of 2 types: salon and service. I want the default behaviour of selecting the option and displaying it. But I also want to set a state value to the type. The purpose of this is to direct to specific route based on the type.
Now since I know that the onClick function is set in the props, how do I override it. This is what I did and it did not work:
{suggestionsFetched && <Autocomplete
disablePortal
id="combo-box-demo"
options={searchSuggestions?.salons.map((salon) => ({
type:'salon',
id:salon.id,
label:salon.name.toLowerCase()
}))
.concat(searchSuggestions?.services.map((service) => ({
type:'service',
id:service.id,
label:service.name.value.toLowerCase()
})))}
renderOption={(props, option) => {
return (
<li
{...props}
onClick={()=>{
setSearchRoute(option.type)
props.onClick()
}}
key={option.id}
>
<span style={{
fontWeight:'bolder',
textTransform:'capitalize'
}}>{option.type}:</span>
{"\u00A0"}
<span style={{
textTransform:'capitalize'
}}>{option.label}</span>
</li>
);
}}
popupIcon={<Search color='secondary' />}
sx={{
width: '100%',
[`& .${autocompleteClasses.popupIndicator}`]: {
transform: "none"
}
}}
renderInput={(params) => <TextField placeholder="Search" {...params} />}
/>}
In the above code, I tried to reset the onClick prop, set my state and then call the default onClick function. But it threw this error:
TypeError: Cannot read properties of undefined (reading 'currentTarget')
How to resolve this and override default onClick prop? Or is it impossible?

So I found the solution. I just needed to write an onChange function
value={selectedSuggestion}
onChange={(event, newValue) => {
setSearchRoute(newValue.type)
setSelectedSuggestion(newValue);
}}
The newValue argument is actually the object passed in the options props.
This is so obvious and I don't know why it took me so long to figure out

Related

MUI AUTOCOMPLETE : How to render two result lists based on option properties?

i'm trying to display results with the MUI Autocomplete component but i need to
separate them in two different list side by side.
I currently have a functionnal searchbar with one list, but i want to separate the results based on a specific property from each option who will refer as their type.
Here's what i have as a beginner :
<Autocomplete
freeSolo={true}
id="equipment-searchbar"
onOpen={() => {
setOpen(true);
}}
onClose={() => {
setOpen(false);
}}
clearOnBlur={false}
isOptionEqualToValue={(option, value) => option.name === value.name}
getOptionLabel={(option) => option.title || ''}
filterOptions={x => x}
options={options}
groupBy={(option) => option.type}
onInputChange={onInputChange}
open={open}
onChange={(event: any, option: any) => {
let searchData = (document.getElementById("equipment-searchbar") as HTMLInputElement).value
if (option !== null && option.globalSearch !== true) {
switch (option.type) {
case bikeSmooveboxType:
redirect("bike/" + option.title)
break;
case stationTransmiterType:
redirect("station/" + option.code)
break;
}
}
}}
loading={loading}
renderInput={(params) => (
<TextField
className="inputRounded"
variant="outlined"
label={"Rechercher dans les équipements"}
{...params}
InputProps={{
...params.InputProps,
endAdornment: (
<React.Fragment>
<SearchIcon/>
{params.InputProps.endAdornment}
</React.Fragment>
),
}}
/>
)}
/>
Any suggestions for solving this problem ?
Thank's a lot !
Basically what you need to do is:
use renderGroup prop and put your jsx for columns.
use ListboxProps prop to apply display:flex for the results.
if needed you can check out PaperComponent and PopperComponent for further customization.
This is a snippet of what probably you need:
<Autocomplete
renderGroup={(item) => {
const { group, children } = item;
if (group === "type1")
return (
<Box
sx={{ width: "50%" }}
/*custom props for type 1*/
>
{children}
</Box>
);
return (
<Box
sx={{ width: "50%" }}
/*custom props for type 2*/
>
{children}
</Box>
);
}}
ListboxProps={{
sx: { display: "flex" },
}}
/>

How to add only single value from Autocomplete in Material UI?

I have an Autocomplete component for adding country calling code, but, the relevant country is added too with it. Like when clicked on Autocomplete drop-down list, all the countries and its specific codes are displayed. My intention is to add only the country code. See the code below for reference
const [countryData, setCountryData] = useState({});
const [newValue, setNewValue] = useState("");
useEffect(() => {
setCountryData(codes);
}, []);
<Autocomplete
id="size-small-standard"
size="small"
options={cities}
onChange={(event, value) => setNewValue(value)}
autoSelect={true}
getOptionLabel={(option) =>
option.country + " " + ` +` + option.calling_code {/* <-- How to display only calling code, but, should show both country name and calling code in drop down */}
}
defaultValue={cities[98]}
style={{ width: "100%" }}
renderInput={(params) => (
<TextField
{...params}
variant="standard"
placeholder="Search your country"
style={{ width: "40%" }}
/>
)}
/>
What would be best possible solution?
Following is the CodeSandbox link: https://codesandbox.io/s/material-demo-forked-3ljj2
You can change your getOptionLabel to show only the phone prefix and then use renderOption to show the name with the country's phone prefix
return (
<div className={classes.root}>
<Autocomplete
id="size-small-standard"
size="small"
options={cities}
onChange={(event, value) => setNewValue(value)}
autoSelect={true}
getOptionLabel={(option) => `+ ${option.calling_code}`}
renderOption={(option) => (
<>{`${option.country} + ${option.calling_code}`}</>
)}
defaultValue={cities[98]}
style={{ width: "100%" }}
renderInput={(params) => (
<TextField
{...params}
variant="standard"
placeholder="Search your country"
style={{ width: "40%" }}
/>
)}
/>
</div>
);
Live Demo
UPDATE:
Here is a better example, where you can still search for the country.
Live Demo

How to set default value in Autocomplete Material UI?

In a React project, I have an Autocomplete component which has country name and its relevant calling codes displayed in drop-down list. When selected on list renders the desired data, but, when refreshed, data is null and doesn't show the default data.
const [newValue, setNewValue] = useState({});
const [textToggle, textToggleState] = useState(false);
render(
<div
style={{ cursor: "pointer" }}
onClick={() => {
textToggleState(!textToggle);
}}
>
<h5>+{newValue == null ? "91" : newValue.calling_code}</h5> {/* <-- Value gets null when refreshed */}
</div>
{textToggle ? (
<Autocomplete
id="size-small-standard"
size="small"
options={cities}
onChange={(event, value) => {
setNewValue(value);
textToggleState(!textToggle);
}}
autoSelect={true}
getOptionLabel={(option) =>
`${option.country}` + `+ ${option.calling_code}`
}
renderOption={(option) => (
<>{`${option.country} + ${option.calling_code}`}</>
)}
//defaultValue={cities[98]}
style={{ width: "100%" }}
renderInput={(params) => (
<TextField
{...params}
variant="standard"
placeholder="Search your country"
style={{ width: "40%" }}
/>
)}
/>
) : (
""
)}
)
Following is the CodeSandbox link: https://codesandbox.io/s/how-to-add-only-single-value-from-autocomplete-in-material-ui-forked-tu218
You can pass a value prop to autocomplete component with the newValue. But newValue is an empty object by default that doesn't exist in the options array you are passing to the autocomplete so it will show undefined + undefined in the default case. You can make that null and add null check there in the autocomplete itself or you can assign a value directly in your useState So as you are having a check for null and using 91 code so instead you can assign that value to the newValue itself directly. Check the code below I have added the value field here
<Autocomplete
id="size-small-standard"
size="small"
options={cities}
value={newValue !== null ? newValue : cities[98]}
onChange={(event, value) => {
setNewValue(value);
textToggleState(!textToggle);
}}
autoSelect={true}
getOptionLabel={(option) =>
`${option.country}` + `+ ${option.calling_code}`
}
renderOption={(option) => (
<>{`${option.country} + ${option.calling_code}`}</>
)}
//defaultValue={cities[98]}
style={{ width: "100%" }}
renderInput={(params) => (
<TextField
{...params}
variant="standard"
placeholder="Search your country"
style={{ width: "40%" }}
/>
)}
/>
Or you can just pass newValue to the value field and assign the default value in the newValue useState as shown below
const [newValue, setNewValue] = useState(cities[98]);
.
.
.
.
<Autocomplete
id="size-small-standard"
size="small"
options={cities}
value={newValue}
onChange={(event, value) => {
setNewValue(value);
textToggleState(!textToggle);
}}
autoSelect={true}
getOptionLabel={(option) =>
`${option.country}` + `+ ${option.calling_code}`
}
renderOption={(option) => (
<>{`${option.country} + ${option.calling_code}`}</>
)}
//defaultValue={cities[98]}
style={{ width: "100%" }}
renderInput={(params) => (
<TextField
{...params}
variant="standard"
placeholder="Search your country"
style={{ width: "40%" }}
/>
)}
/>

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 to open the dropdown list in Autocomplete Material-UI by default?

In an Autocomplete Form, the data for country and its relevant codes are displayed. When the input text is focused the drop-down list is populated. My intention is to pre load array of data without focus requirement. See the code for example
const [newValue, setNewValue] = useState(null);
const [textToggle, textToggleState] = useState(false);
render(
<div
style={{ cursor: "pointer" }}
onClick={() => {
textToggleState(!textToggle);
}}
>
<h5>+{newValue == null ? "91" : newValue.calling_code}</h5>
</div>
{textToggle ? (
<Autocomplete
id="size-small-standard"
size="small"
options={cities}
onChange={(event, value) => {
setNewValue(value);
textToggleState(!textToggle);
}}
autoSelect={true}
getOptionLabel={(option) =>
`${option.country}` + `+ ${option.calling_code}`
}
renderOption={(option) => (
<>{`${option.country} + ${option.calling_code}`}</>
)}
//defaultValue={cities[98]}
style={{ width: "100%" }}
renderInput={(params) => (
<TextField
{...params}
variant="standard"
placeholder="Search your country"
style={{ width: "40%" }}
/>
)}
/>
) : (
""
)}
</div>
)
Here you can see data is displayed on input text focus. What could be the best possible solution to
pre-load data?
CodeSandbox Link: https://codesandbox.io/s/how-to-add-only-single-value-from-autocomplete-in-material-ui-forked-tu218
Just take control of the open state of the Autocomplete and set the default value to true:
export default function ComboBox() {
// default value to true to open at first render
const [open, setOpen] = useState(true);
return (
<Autocomplete
open={open}
onOpen={() => setOpen(true)}
onClose={() => setOpen(false)}
options={top100Films}
getOptionLabel={(option) => option.title}
renderInput={(params) => (
<TextField {...params} label="Combo box" variant="outlined" />
)}
/>
);
}
Live Demo
I think what you need is to use AutoComplete as Combobox.
<Autocomplete
id="combo-box-demo"
options={top100Films}
getOptionLabel={(option) => option.title}
style={{ width: 300 }}
renderInput={(params) => <TextField {...params} label="Combo box" variant="outlined" />}
/>
This will pre-populate it.
Visit this: https://material-ui.com/components/autocomplete/#combo-box
Hope it helps. if not, please elaborate your question if you want something else.

Resources