How to use SubHeaders in Material UI Select Menu

Viewed 2496

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:

enter image description here

2 Answers

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.

enter image description here

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

Edit Button

I hope it helps.

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>
Related