105

I'm new to Material UI and just started fiddling around with their App bar with Menu example. When toggling the menu dropdown, it opens up over the Appbar itself, whereas I'd like it to open below the Navbar.

From the docs, I understand that changing the position can be done with anchorEl as explained in the Popover positioning guide. But when I implement this to my menu component nothing happens. What is "the right Material UI way" to take care of this?

class Header extends React.Component {
  state = {
    auth: true,
    anchorEl: null,
    anchorOriginVertical: 'bottom',
    anchorOriginHorizontal: 'right',
    transformOriginVertical: 'top',
    transformOriginHorizontal: 'right',
    anchorReference: 'anchorEl',
  };

  handleChange = (event, checked) => {
    this.setState({ auth: checked });
  };

  handleMenu = event => {
    this.setState({ anchorEl: event.currentTarget });
  };

  handleClose = () => {
    this.setState({ anchorEl: null });
  };

  render() {
    const { classes } = this.props;
    const { auth, anchorEl } = this.state;
    const open = Boolean(anchorEl);

    return (
      <div className={classes.root}>
        <AppBar position="static">
          <Toolbar>
            <Typography type="title" color="inherit" className={classes.flex}>
              Title
            </Typography>
            {auth && (
              <div>
                <IconButton
                  aria-owns={open ? 'menu-appbar' : null}
                  aria-haspopup="true"
                  onClick={this.handleMenu}
                  color="contrast"
                >
                  <AccountCircle />
                </IconButton>
                <Menu
                  id="menu-appbar"
                  anchorEl={anchorEl}
                  open={open}
                  onClose={this.handleClose}
                >
                  <MenuItem onClick={this.handleClose}>Profile</MenuItem>
                  <MenuItem onClick={this.handleClose}>My account</MenuItem>
                </Menu>
              </div>
            )}
          </Toolbar>
        </AppBar>
      </div>
    );
  }
}
Olivier Tassinari
  • 8,238
  • 4
  • 23
  • 23
Stiño
  • 2,663
  • 7
  • 25
  • 47

8 Answers8

219

The way I got it to work is by setting the props on the menu itself like this:

  <Menu
    id="menu-appbar"
    anchorEl={anchorEl}
    getContentAnchorEl={null}
    anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
    transformOrigin={{ vertical: 'top', horizontal: 'center' }}
    open={open}
    onClose={this.handleClose}
    className={props.classes.menu}
  >

The positioning props (anchorOrigin, transformOrigin) come from the Popover component, see the demo: https://mui.com/material-ui/react-popover/#anchor-playground.

I also had to put in getContentAnchorEl={null} to get the vertical props to work, which I eventually learned from this issue https://github.com/mui/material-ui/issues/7961.
Edit: The getContentAnchorEl prop was removed in Material UI v5, it's no longer needed.

Not sure how to do this when using the state to set the properties of the anchor element, but maybe this will get you started.

Olivier Tassinari
  • 8,238
  • 4
  • 23
  • 23
Emmy
  • 3,493
  • 2
  • 16
  • 25
  • 13
    This solution still works in April 2020, though [MUI should be more explicit about it](https://github.com/mui-org/material-ui/issues/20755). Also @Emmy, you can edit questions and fix links :) I've just [fixed the link](https://stackoverflow.com/posts/48157863/revisions) to the popover demo for you. – Dan Dascalescu Apr 26 '20 at 05:53
  • 1
    `getContentAnchorEl` prop worked for me. I had menu popped up way below desired anchor origin. This happened when using two menu popovers for two buttons inline – Waleed93 Nov 03 '20 at 03:54
  • 1
    Top get the menu just below a button, I use ```anchorOrigin={{ vertical: 'bottom',horizontal: 'left' }} transformOrigin={{ vertical: 'top', horizontal: 'left' }}``` – Dave Howson Mar 19 '21 at 15:26
  • 1
    thanks a lot, I have struggled with this issue for some hours until I found this answer! – T.arar Sep 14 '21 at 16:21
10

This was my solution, hope this will help someone.

<Menu
  open={this.state.openProps}
  anchorEl={this.state.anchorEl}
  onClose={onClose}
  className={classes.styles}
  disableAutoFocusItem
  PaperProps={{
    style: {
      left: '50%',
      transform: 'translateX(-77%) translateY(32%)',
    }
  }}
  MenuListProps={{
    style: {
      padding: 0,
    },
  }}
>
Davit Mkrtchyan
  • 449
  • 5
  • 15
6

There is an error in Matheus answer, the type of anchorEl is not boolean, in ReactHooks it would need to be:

  const [menuOpen, setMenuOpen] = useState<boolean>(false);
  const [anchorEl, setAnchorEl] = useState()

  const recordButtonPosition = (event: any) => {
      setAnchorEl(event.currentTarget);
      setMenuOpen(true);
  }

  let closeMenu = () => {
      setMenuOpen(false);
  }

  return (
      <React.Fragment>
          <Button onClick={recordButtonPosition}>
              OPEN MENU
          </Button>
          <Menu
              anchorEl={anchorEl}
              open={menuOpen}
              onClose={closeMenu}>
              <MenuItem onClick={closeMenu}> ExampleMenuItem </MenuItem> 
          </Menu>
      </React.Fragment>
  );
Angel Igareta
  • 61
  • 1
  • 1
5

For me i had issue of jumping effect , after clicking first time .. i had to make keepMounted={false}

and you can get x and y value by adjusting translateX(10px) translateY(50px) from devTool.

enter image description here

                      <div>
                         <Menu
                                    id="simple-menu"
                                    anchorEl={anchorEl}
                                    keepMounted={false}
                                    open={Boolean(anchorEl)}
                                    onClose={this.handleClose}
                                    PaperProps={{
                                        style: {
                                            transform: 'translateX(10px) translateY(50px)',
                                        }
                                    }}
                                >
                                    <MenuItem onClick={this.handleClose}>My Account</MenuItem>
                                    <Divider variant="middle"/>
                                    <MenuItem onClick={this.handleClose}>Logout</MenuItem>
                                </Menu>
                            </div>
Afsar Ali
  • 555
  • 6
  • 17
4

An easy way to achieve that is using useRef hook

  1. Create the ref
  2. insert your ref were you want to show the menu in your case a Div or other
  3. inser the ref in the anchor prop of the menu

enter code here

const inputEl = useRef(null);
<Mycomponent ref={inputEl}/>
<Menu
  id="simple-menu"
  anchorEl={inputEl.current}
  keepMounted
  open={Boolean(anchorEl)}
  onClose={handleClose}
>

for me this is by far the easier way

2

it is because you have not defined the anchor.

The Menu attribute - anchorEl, is responsible for passing the location of the button that called it, not true to say this, but only to be easy to understand.

In this way, you should refer whenever there is a click. I suggest you use the reaction hooks, which makes the component clean.

React Stateful

const [menuOpen, setMenuOpen] = useState(false)
const [anchorEl, setAnchorEl] = useState(false)

const handleClick = (event) => {
const anchorEl = event.currentTarget
this.setState({ ...this.state, menuOpen: !menuOpen , anchorEl })

React Hooks

const [menuOpen, setMenuOpen] = useState(false)
const [anchorEl, setAnchorEl] = useState(false)

const handleClick = (event) => {
setAnchorEl(event.currentTarget)
}

render

return (
 <Menu
   anchorEl={anchorEl}
   open={menuOpen}
   onClose={handleClick }              
 </Menu 
)
Matheus
  • 13
  • 1
  • 6
    No wonder OP hasn't defined the anchor. The [page for the menu component](https://material-ui.com/api/menu/) says nothing about how to actually use it. – Dan Dascalescu Apr 26 '20 at 03:09
0

In the New Material-ui version 5, I don't understand why this weird thing happens. But it works when I create a separate menu component in the app bar.

import * as React from 'react';
import { useRouter } from 'next/router'
import { Menu, IconButton, } from '@mui/material';
import MenuItem from '@mui/material/MenuItem';
import AccountCircle from '@mui/icons-material/AccountCircle';

export default function AppBarMenu() {
    const router = useRouter()
    const [anchorEl, setAnchorEl] = React.useState(null);
    const open = Boolean(anchorEl);
    const handleClick = (event) => {
        setAnchorEl(event.currentTarget);
    };
    const handleClose = () => {
        setAnchorEl(null);
    };
    const handleLogout = () => {
        localStorage.clear()
        router.push('/auth/login')
    }
    return (
        <>
            <IconButton
                id="basic-button"
                size="large"
                aria-controls="basic-menu"
                aria-haspopup="true"
                aria-expanded={open ? 'true' : undefined}
                onClick={handleClick}
            >
                <AccountCircle sx={{ color: 'white' }} />
            </IconButton>
            <Menu
                id="basic-menu"
                anchorEl={anchorEl}
                open={open}
                onClose={handleClose}
                MenuListProps={{
                    'aria-labelledby': 'basic-button',
                }}
            >
                <MenuItem onClick={handleClose}>Profile</MenuItem>
                <MenuItem onClick={handleClose}>My account</MenuItem>
                <MenuItem onClick={handleLogout}>Logout</MenuItem>
            </Menu>
        </>
    );
}
Shahnad S
  • 983
  • 10
  • 17
-1

All that stuff didn't work for me. So i gave the button an id and put it on the menu using javascript:

<IconButton id="btnPerfil" onClick={this.toggleMenuPerfilOpen}>
    <AccountCircle color="inherit" />
</IconButton>
<Menu
  anchorPosition={{
    vertical: 'top',
    horizontal: 'center'
  }}
  anchorEl={document.getElementById('btnPerfil')}
  keepMounted
  open={this.state.isMenuPerfilOpen}
  onClose={this.toggleMenuPerfilOpen}
>...</Menu>
Anderson Bressane
  • 378
  • 1
  • 5
  • 15