I’m using MUI Autocomplete and Formik and I wanted to group this into categories. If it has no sub_accounts
, then it shouldn’t have a header label. Something like this: https://mui.com/material-ui/react-autocomplete/#grouped
CODESANDBOX ——>
CLICK HERE
Expected outcome on the UI is something like:
-
Petty Cash
-
Cash In Bank – Bank of America
- Bank of America – Single Proprietor
- Bank of America – Corporate Entity
-
Cash
-
CIB – Bank of Vietnam
- Bank of Vietnam Personal
- Bank of Vietnam Checking Acc
Petty Cash
, Cash In Bank - Bank of America
, Cash
and CIB - Bank of Vietnam
should be aligned.
Cash In Bank - Bank of America
and CIB - Bank of Vietnam
cannot be clicked/selected – only its sub_accounts
can be selected as well as Petty Cash
and Cash
.
CODE
export const CashAccountAutocomplete = ({
field,
form: { touched, errors, setFieldValue, values },
disabled,
...props
}) => {
const [inputValue, setInputValue] = useState("");
const handleChange = (_, newValue, reason) => {
if (reason === "clear") {
setFieldValue(field.name, { id: "", name: "" });
return;
}
setFieldValue(field.name, newValue);
};
const handleInputChange = (_, newInputValue) => {
setInputValue(newInputValue);
};
const extractSubAccounts = (accounts) => {
if (!Array.isArray(accounts)) {
console.error("Invalid accounts data. Expected an array.");
return [];
}
return accounts.flatMap(
({ id, name, sub_accounts }) =>
sub_accounts && sub_accounts.length > 0
? extractSubAccounts(sub_accounts) // Recursively extract sub-accounts
: [{ id, name }] // Include the account if it has no sub-accounts
);
};
const filteredData = extractSubAccounts(accounts);
return (
<Autocomplete
{...field}
disabled={disabled}
getOptionLabel={(option) =>
typeof option === "string" ? option : option?.name || ""
}
renderOption={(props, option) => {
return (
<li {...props} key={option.id}>
{option?.name}
</li>
);
}}
filterOptions={(x) => x}
options={filteredData || []}
autoComplete
includeInputInList
filterSelectedOptions
noOptionsText={"No data"}
onChange={handleChange}
inputValue={inputValue}
onInputChange={handleInputChange}
renderInput={(params) => (
<TextField
{...params}
{...props}
error={touched[field.name] && errors[field.name] ? true : false}
helperText={
touched[field.name] &&
errors[field.name] &&
String(errors[field.name].id)
}
/>
)}
fullWidth
/>
);
};
2
Answers
I have implemented a solution here using hooks.
You need to sort the
options
property value ofAutocomplete
after filtering.Update the
extractSubAccounts
helper function to only set theisHeader
property on elements are will be headers, and then also set anisSelectable
property if there are no sub-array options.Update the
renderOptions
callback to check both each list item’sinHeader
andisSelectable
properties, the idea being that the list items that are headers and also options should be selectable and receive the"MuiAutocomplete-option"
CSS classes and then also conditionally undo the padding that options have so the headers remain aligned.