Reputation: 223
I am struggling on how to properly set up a long list of Menu Items on a Select control that uses sticky SubHeaders. The problem is that when the items scroll they obscure the subheaders.
I looked at the Material UI examples of grouped Select items as a start. I wanted behavior that looked like the Material UI example with pinned subHeader Lists.
Here is a codeSandbox of what I'm trying.
Below is a snippet of my code:
<Select
className={classes.root}
MenuProps={{ className: classes.menu }}
value="Pick one"
onChange={e => {}}
>
{subHeaders.map(header => (
<li key={header}>
<ul>
<ListSubheader>{header}</ListSubheader>
{items.map(item => (
<MenuItem key={item} value={item}>
{item}
</MenuItem>
))}
</ul>
</li>
))}
</Select>
Here is a snapshot of the problem:
Upvotes: 4
Views: 6541
Reputation: 31
I had the same problem. To make it work you need to have only one list. The list must be something like:
[
{
value,
text,
isParent (is section)
}
]
IMPORTANT! It has to be sorted like this: Parent 1 - Child 1 (parent 1) - Child 2 (parent 1) - Parent 2 - Child 1 (parent 2)....
Then:
const array = [
{ text: 'parent 1', isParent: true },
{ text: 'child 1 parent 1', value: 2, isParent: false },
{ text: 'child 2 parent 1', value: 3, isParent: false },
{ text: 'parent 2', isParent: true },
{ text: 'child 1 parent 2', value: 4, isParent: false },
{ text: 'child 2 parent 2', value: 5, isParent: false },
]
<Select>
{array.map(option => {
if(option.isParent)
return (
<ListSubheader>
{option.text}
</ListSubheader>
)
else
return (
<MenuItem
value={option.value}
>
{option.text}
</MenuItem>
)
})}
</Select>
Upvotes: 0
Reputation: 1709
I managed to make a working solution of Material-ui select with and sticky MenuItems.
use MaterialUI MenuItem instead of all the <li> <ul> <ListSubheader>
const [isOpen, setIsOpen] = useState(false);
const [value, setValue] = useState();
const onToggle = () => {
setIsOpen((prev) => !prev);
};
const onClose = () => {
setIsOpen(false);
};
const _onChange = (event: React.ChangeEvent<{ value: unknown }>) => {
const valueToSelect = event.target.value as Value;
if (
isResetSeletced(valueToSelect) ||
(multiple
? !valueToSelect.length ||
valueToSelect.length < minSelections ||
(valueToSelect as string[]).some((option) => !option)
: !valueToSelect?.length && minSelections > 0)
) {
return;
}
event.persist();
onChange(valueToSelect);
};
const renderValue = (selected: any) => {
if (!selected.length) {
return '';
}
if (multiple) {
const isReachedLimit = selected.length > MAX_SELECTIONS;
const hiddenTags = isReachedLimit ? (
<span>+{value.length - MAX_SELECTIONS}</span>
) : null;
const selectionsToShow = isReachedLimit
? selected.slice(0, MAX_SELECTIONS)
: selected;
return (
<StyledTagsContainer>
<Tags values={selectionsToShow} onRemoveTag={onRemoveTag} />
{hiddenTags}
</StyledTagsContainer>
);
}
return selected;
};
const resetMenuItem = secondaryOptions?.map((resetItem, index) => {
return (
<MenuItem
key={resetItem.value + index}
onClick={() => {
resetItem.onClick();
}}
isLast={!index}
isSelected={
resetItem.value === resetSelected?.value ||
resetItem.value === value ||
(multiple && resetItem.value === value[0])
}
value={resetItem.value}
icon={<RadioIcon />}
>
{resetItem.text}
</MenuItem>
);
});
<Select
displayEmpty
onClose={onClose}
value={value}
onChange={_onChange}
renderValue={renderValue}
open={isOpen}
>
{menuItems}
<div style={{ position: 'sticky', bottom: 0 }}>
{resetMenuItem}
</div>
</Select>
Upvotes: 0
Reputation: 323
Using the Select component we can even reproduce the behavior with some corrections. But it won't work for you. The Select component does not expect items nested within your child's elements. That way, we will never be able to identify the element that is selected.
Alternatively, we have the Autocomplete component. It can better supply what you need.
Regarding the example you provided, we can do something, but again, we will not be able to maintain the state of the selected item. To achieve the same behavior as the list, we need to apply the same behavior to the list that the Menu will render. Select will render a Menu that inherits List, so we can apply the same behavior as the list example through the prop MenuListProps property.
I applied the fixes to your example
I hope it helps.
Upvotes: 1