Material UI Menu with subitems won't close whole menu

Viewed 1816

I have a Material UI menu component with custom MenuItems. Now when the menu and a submenu is opened I would like to be able to close the whole menu when clicking outside of the menu. I still need to do two clicks, first closing the submenu and then the actual menu.

The documentation MUI menu refers to using the anchorEl as the boolean to determine if menu is open but even i send the close callback function from the custom menu items it only closes itself with the handleClose() when clicking outside the component. So in the picture attached only the Container containing Corporate finance is closed and not also the menu.

Can't understand, as the anchorEl is turning to null in the closeFunction the core menu still stays open. Tried with menu and MenuItems and Popover that are built on the Modal component.

MenuComponent:

const MoreMenu = ({
  userTagData,
  onChangeItemName,
  selectedTags,
  onTagSelected,
  onRemoveItem,
  data,
  item,
}) => {
  const [anchorEl, setAnchorEl] = useState(null);
  const [subMenuPopUpName, setSubMenuPopUpName] = useState('');


  const renderChangeTitlePopUp = () => (
    <ChangeTitleContainer
      onChangeItemName={onChangeItemName}
      closeMenuItem={handleClose}
      item={item}
    />
  );


  const renderAddTagPopUp = () => (
    <AddTagContainer
      item={item}
      userTagData={userTagData}
      selectedTags={selectedTags}
      onTagSelected={onTagSelected}
    />
  );

  const renderRemoveItemContainer = () => (
    <RemoveItemContainer
      item={item}
      onRemoveItem={onRemoveItem}
      closeMenuItem={handeleClose}
    />
  );

  const renderDefaultPopup = () => (
    <div><p>Standard menu post</p></div>
  );


  const menuItemPopUpSwitcher = (name) => {
    switch (name) {
      case ADDTAG:
        return renderAddTagPopUp();
      case CHANGETITLE:
        return renderChangeTitlePopUp();
      case REMOVEITEM:
        return renderRemoveItemContainer();
      default:
        return renderDefaultPopup();
    }
  };

  const handleMenuItemClick = (item, event) => {
    setAnchorEl(event.currentTarget);
    setSubMenuPopUpName(item.title);
    setAnchorEl(event.currentTarget);
    menuItemPopUpSwitcher(item);
  };

  const handleClose = () => {
    setAnchorEl(null);
  };

  return (
    <Fragment>
      <MoreButton
        data={data}
        handleMenuItemClick={handleMenuItemClick}
      />
      <Menu
        open={Boolean(anchorEl)}
        anchorEl={anchorEl}
        onClose={handleClose}
        anchorOrigin={{
          vertical: 'top',
          horizontal: 'left',
        }}
        transformOrigin={{
          vertical: 'top',
          horizontal: 'right',
        }}
      >

        {menuItemPopUpSwitcher(subMenuPopUpName)}

      </Menu>
    </Fragment>
  );
};custom 

subme nuopen

1 Answers

Was able to solve problem by adding another state for the parent anchor. So the parent menu has its own anchor and the child popup its own anchor and then handleClose() disables them both. See example and 5 lines marked with //THIS IS NEW:

const MoreMenu = ({
  userTagData,
  onChangeItemName,
  selectedTags,
  onTagSelected,
  onRemoveItem,
  data,
  item,
}) => {
  const [parentAnchorEl, setParentAnchorEl] = useState(null); // THIS IS NEW
  const [anchorEl, setAnchorEl] = useState(null);
  const [subMenuPopUpName, setSubMenuPopUpName] = useState('');

  const handleMoreButtonClick = (event) => { // THIS IS NEW
    setParentAnchorEl(event.currentTarget);
  };

  const renderChangeTitlePopUp = () => (
    <ChangeTitleContainer
      onChangeItemName={onChangeItemName}
      closeMenuItem={handleClose}
      item={item}
    />
  );


  const renderAddTagPopUp = () => (
    <AddTagContainer
      item={item}
      userTagData={userTagData}
      selectedTags={selectedTags}
      onTagSelected={onTagSelected}
    />
  );

  const renderRemoveItemContainer = () => (
    <RemoveItemContainer
      item={item}
      onRemoveItem={onRemoveItem}
      closeMenuItem={handeleClose}
    />
  );

  const renderDefaultPopup = () => (
    <div><p>Standard menu post</p></div>
  );


  const menuItemPopUpSwitcher = (name) => {
    switch (name) {
      case ADDTAG:
        return renderAddTagPopUp();
      case CHANGETITLE:
        return renderChangeTitlePopUp();
      case REMOVEITEM:
        return renderRemoveItemContainer();
      default:
        return renderDefaultPopup();
    }
  };

  const handleMenuItemClick = (item, event) => {
    setAnchorEl(event.currentTarget);
    setSubMenuPopUpName(item.title);
    setAnchorEl(event.currentTarget);
    menuItemPopUpSwitcher(item);
  };

  const handleClose = () => {
    setAnchorEl(null);
    setParentAnchorEl(null); //THIS IS NEW
  };

  return (
    <Fragment>
      <MoreButton
        anchorEl={parentAnchorEl} //THIS IS NEW
        data={data}
        handleMenuItemClick={handleMenuItemClick}
        handleClick={handleMoreButtonClick}    //THIS IS NEW
      />
      <Menu
        open={Boolean(anchorEl)}
        anchorEl={anchorEl}
        onClose={handleClose}
        anchorOrigin={{
          vertical: 'top',
          horizontal: 'left',
        }}
        transformOrigin={{
          vertical: 'top',
          horizontal: 'right',
        }}
      >

        {menuItemPopUpSwitcher(subMenuPopUpName)}

      </Menu>
    </Fragment>
  );
};

const MoreButton = ({
  data, 
  handleMenuItemClick, 
  anchorEl, 
  handleClick, 
  handleClose, 
}) => (
  <div>     
    <MoreIconMUI onClick={handleClick} />     
    <Menu 
      id="menu-appbar" 
      anchorEl={anchorEl} 
      open={Boolean(anchorEl)}
      onClose={handleClose}
    >
      {data.map(item => (
        <MenuItem 
          key={item.title} 
          onClick={event => handleMenuItemClick(item, event)}
        >
         {item.title}
        </MenuItem>
      ))}
    </Menu>
  </div>
);

Related