4

I tried to use multiple popover within a component. For ex, in below code I have two popovers, but when clicking any of the two button, both the popovers are opened. How can we handle the onclick to open corresponding popover?

const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
    const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
        setAnchorEl(event.currentTarget);
    };

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

<Tabs variant="fullWidth" value={value} onChange={onNavChange} indicatorColor="transparent">
                        <Tab label="Menu 1" className={classes.navTab} component={Link} to="./Menu1"></Tab>
                        <Tab label="Menu 2" onClick={handleClick} aria-describedby="menu2Popover" aria-haspopup="true"></Tab>
                        <Tab label="Menu 3" component={Link} to="./Menu3"></Tab>
                        <Tab label="Menu 4"onClick={handleClick} aria-describedby="menu4Popover" aria-haspopup="true"></Tab>
                    </Tabs>
                    <Popover 
                        id="menu2Popover" open={Boolean(anchorEl)} onClose={handleClose}
                        anchorEl = {anchorEl}
                        anchorOrigin={{
                            vertical: 'top',
                            horizontal: 'left',
                        }}
                        transformOrigin={{
                            vertical: 'bottom',
                            horizontal: 'left',
                        }}>
                        <MenuList>
                            <MenuItem>Submenu 1</MenuItem>
                            <MenuItem>Submenu 2</MenuItem>
                        </MenuList>
                    </Popover>
                    <Popover 
                        id="menu4Popover" open={Boolean(anchorEl)} onClose={handleClose}
                        anchorEl = {anchorEl}
                        anchorOrigin={{
                            vertical: 'top',
                            horizontal: 'left',
                        }}
                        transformOrigin={{
                            vertical: 'bottom',
                            horizontal: 'left',
                        }}>
                        <MenuList>
                            <MenuItem>Submenu 3</MenuItem>
                            <MenuItem>Submenu 4</MenuItem>
                        </MenuList>
                    </Popover>
shiva
  • 41
  • 1
  • 2

2 Answers2

4

You need to use different anchorEl's for each Popover:

import * as React from "react";
import { render } from "react-dom";
import { MenuList, MenuItem, Popover, Tabs, Tab } from "@material-ui/core";

import "./styles.css";

interface CustomMenuItem {
  anchorEl: null | HTMLElement;
  child: any;
}

function Popover1() {
  return (
    <MenuList>
      <MenuItem>Submenu 1</MenuItem>
      <MenuItem>Submenu 2</MenuItem>
    </MenuList>
  );
}

function Popover2() {
  return (
    <MenuList>
      <MenuItem>Submenu 3</MenuItem>
      <MenuItem>Submenu 4</MenuItem>
    </MenuList>
  );
}

function App() {
  const [popover1, setPopover1] = React.useState<CustomMenuItem>({
    anchorEl: null,
    child: <Popover1 />
  });
  const [popover2, setPopover2] = React.useState<CustomMenuItem>({
    anchorEl: null,
    child: <Popover2 />
  });

  return (
    <div className="App">
      <Tabs variant="fullWidth" indicatorColor="transparent">
        <Tab label="Menu 1" />
        <Tab
          value="Tab2"
          label="Menu 2"
          onClick={(event: React.MouseEvent<HTMLButtonElement>) =>
            setPopover1({ ...popover1, anchorEl: event.currentTarget })
          }
          aria-describedby="menu2Popover"
          aria-haspopup="true"
        />
        <Tab label="Menu 3" />
        <Tab
          label="Menu 4"
          onClick={(event: React.MouseEvent<HTMLButtonElement>) =>
            setPopover2({ ...popover2, anchorEl: event.currentTarget })
          }
          aria-describedby="menu4Popover"
          aria-haspopup="true"
        />
      </Tabs>

      <Popover
        id="menu2Popover"
        open={Boolean(popover1.anchorEl)}
        onClose={() => setPopover1({ ...popover1, anchorEl: null })}
        anchorEl={popover1.anchorEl}
        anchorOrigin={{
          vertical: "top",
          horizontal: "left"
        }}
        transformOrigin={{
          vertical: "bottom",
          horizontal: "left"
        }}
      >
        {popover1.child}
      </Popover>
      <Popover
        id="menu4Popover"
        open={Boolean(popover2.anchorEl)}
        onClose={() => setPopover2({ ...popover2, anchorEl: null })}
        anchorEl={popover2.anchorEl}
        anchorOrigin={{
          vertical: "top",
          horizontal: "left"
        }}
        transformOrigin={{
          vertical: "bottom",
          horizontal: "left"
        }}
      >
        {popover2.child}
      </Popover>
    </div>
  );
}

const rootElement = document.getElementById("root");
render(<App />, rootElement);

Look at the codesandbox. If you have any further questions let me know and I can update my answer.

Klaus
  • 1,080
  • 2
  • 10
  • 27
  • Hey, Thanks!. Its worked. Also provide details on how to handle dynamically added menu and its sub menu popup? – shiva Dec 30 '19 at 14:02
  • @shiva I made the code more flexible and easier to create different submenus. If your menus, submenus always look the same it would be possible to refactor it into an own component. Could you accept my answer if it works for you now? – Klaus Dec 30 '19 at 14:14
  • I am getting menu and submenus from json, so if the submenus added later the component should work. – shiva Dec 30 '19 at 14:35
  • If you accept my answer (click green tick right of the answer) I could look into it – Klaus Jan 02 '20 at 11:11
1

another sample with one Popover

import React from "react";
import { withStyles } from "@material-ui/core/styles";
import AppBar from "@material-ui/core/AppBar";
import Tabs from "@material-ui/core/Tabs";
import Tab from "@material-ui/core/Tab";
import Popover from "@material-ui/core/Popover";
import MenuList from "@material-ui/core/MenuList";
import MenuItem from "@material-ui/core/MenuItem";

const styles = theme => ({
  root: {
    flexGrow: 1,
    backgroundColor: theme.palette.background.paper,
    textTransform: "none"
  }
});

class SimpleTabs extends React.Component {
  state = {
    value: 0,
    anchorEl: null,
    popno: -1
  };

  handlePopoverClose = () => {
    this.setState({ anchorEl: null, popno: -1 });
  };

  handleClick = (e, _popno) => {
    this.setState({ anchorEl: e.currentTarget, popno: _popno });
  };

  handleChange = (event, value) => {
    this.setState({ value });
  };

  render() {
    const { classes } = this.props;
    const { value } = this.state;

    return (
      <div className={classes.root}>
        <AppBar position="static">
          <Tabs value={value} onChange={this.handleChange}>
            <Tab label="Tab 1" onClick={e => this.handleClick(e, 1)} />
            <Tab label="Tab 2" onClick={e => this.handleClick(e, 2)} />
            <Tab label="Tab 3" onClick={e => this.handleClick(e, 3)} />
          </Tabs>
          <Popover
            id="menu2Popover"
            open={this.state.anchorEl !== null}
            onClose={this.handlePopoverClose}
            anchorEl={this.state.anchorEl}
          >
            {this.state.popno === 1 && (
              <MenuList>
                <MenuItem>Tab 1 - Submenu 1</MenuItem>
                <MenuItem>Tab 1 - Submenu 2</MenuItem>
              </MenuList>
            )}
            {this.state.popno === 2 && (
              <MenuList>
                <MenuItem>Tab 2 - Submenu 1</MenuItem>
                <MenuItem>Tab 2 - Submenu 2</MenuItem>
              </MenuList>
            )}
            {this.state.popno === 3 && (
              <MenuList>
                <MenuItem>Tab 3 - Submenu 1</MenuItem>
                <MenuItem>Tab 3 - Submenu 2</MenuItem>
              </MenuList>
            )}
          </Popover>
        </AppBar>
      </div>
    );
  }
}

export default withStyles(styles)(SimpleTabs);

answer output https://codesandbox.io/s/material-tabs-demo-tpugw

Babak Yaghoobi
  • 1,892
  • 9
  • 18