How to make a dropdown menu open below the Appbar using Material-UI?
ReactjsMaterial UiReactjs Problem Overview
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 this can be done with anchorEl
as explained here. 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>
);
}
}
Reactjs Solutions
Solution 1 - Reactjs
I had a similar issue and the way I got it to work is by setting the properties 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}
>
I had to put in getContentAnchorEl={null}
to get the vertical properties to work, which I eventually learned from this issue https://github.com/mui-org/material-ui/issues/7961 .
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.
Solution 2 - Reactjs
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,
},
}}
>
Solution 3 - Reactjs
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>
);
Solution 4 - Reactjs
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.
<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>
Solution 5 - Reactjs
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
)
Solution 6 - Reactjs
An easy way to achieve that is using useRef hook
- Create the ref
- insert your ref were you want to show the menu in your case a Div or other
- 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
Solution 7 - Reactjs
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>
</>
);
}
Solution 8 - Reactjs
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>