I am running MaterialUI-Next and I've tried all variants of the drawer but I can't get it to close when an item in the drawer has been clicked. I know the temporary variant allows me to do that but it closes when anything is clicked. I need it to close only when a ListItem is clicked.
In my case, I have a ExpansionPanel in the drawer with Lists and ListItems in it. Clicking the ExpansionPanel shouldn't close the drawer, the drawer should remain open because I am not ready to navigate away. However when I click a ListItem in the drawer (They contain component="a"
items) then the drawer should close and the component="a" navigated to its href.
My code is extremely basic and vanilla, practically the exact demo from MaterialUI-Next Drawers. They also have a playground to test code. But anyhow, here is some of my relevant code
My drawer:
<Drawer
variant="persistent" //I tried "temporary", and "permanent" as well
elevation={16}
anchor="right"
open={this.state.open}
// onClick={(open) => this.setState({ open: false })}
// onKeyDown={(open) => this.setState({ open: false })}>
<SystemMenu />
</Drawer>
My Content stored in SystemMenu:
import React from 'react';
import { withStyles } from 'material-ui-next/styles';
import List, { ListItem, ListItemText } from 'material-ui-next/List';
import ExpansionPanel, { ExpansionPanelDetails, ExpansionPanelSummary } from 'material-ui-next/ExpansionPanel';
import ListSubheader from 'material-ui-next/List/ListSubheader';
import {ChevronDown} from 'mdi-material-ui';
import Grid from 'material-ui-next/Grid';
const OTHERPAGES = require('data/pages.js');
const systemMenuData = OTHERPAGES['systemMenuPagesItems'];
class SystemMenu extends React.Component {
constructor(props) {
super(props);
this.state = {
expanded: null,
};
}
handleChange = panel => (event, expanded) => {
this.setState({
open: false,
expanded: expanded ? panel : false,
});
};
render() {
const { expanded, open } = this.state;
return (
<div>
<Grid
container>
<Grid item xs={12} style={{padding: 0}}>
<ListSubheader component="div">MENU ITEMS</ListSubheader>
</Grid>
</Grid>
<Grid
container>
<Grid item xs={12}>
<ExpansionPanel
expanded={expanded === 'panel1'}
onChange={this.handleChange('panel1')}>
<ExpansionPanelSummary
expandIcon={<ChevronDown />}>
Players
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<List>
<ListItem
button
component="a"
href="/app/page1">
<ListItemText primary="Page 1" />
</ListItem>
<ListItem
button
component="a"
href="/app/page2">
<ListItemText primary="Page 2" />
</ListItem>
<ListItem
button
component="a"
href="/app/page3">
<ListItemText primary="Page 3" />
</ListItem>
</List>
</ExpansionPanelDetails>
</ExpansionPanel>
</Grid>
</Grid>
</div>
);
}
}
export default withStyles(styles)(SystemMenu);
I read in other posts that you can pass the onKeyDown={(open) => this.setState({ open: false })}
to its children with props. But I am new in React and having some problems with this concept.
Basically, I need to make the onKeyDown work for the ListItems, not the actual drawer. I may or may not have to change my Drawer variant back to temporary for the onKeyDown property to work. Not too sure.
(Remember the link above offers a playground to test code. Much better than me trying to build a jFiddle for React)
EDIT
I worked out a different way but not my favorite.
By moving the content in the SystemMenu to the Menu.js file (where my Drawer imports, functions, and states live) I was able to create a handleClose function with timeout so that it closes. This is the function:
handleClose = () => {
if (!this.state.open) {
return;
}
this.timeout = setTimeout(() => {
this.setState({ open: false });
this.setState({ expanded: null });
});
};
This requires the persistent variant and a handleOpen()
function to keep the drawer open until I click a List that has the handleClose()
function. So, my Drawer looks like this now
<Drawer
variant="persistent"
elevation={16}
anchor="right"
open={this.state.open}
onClick={() => { this.handleOpen(); }}>
...
My System Menu code in here, not too happy about this part. :(
...
</Drawer>
Next, in my System Menu code that contains the Expansion Panels and Lists, I added the handleClose()
function to the List and it navigates as well as closing the drawer and expansion panels. Like this:
<List
className="quickpanel-navigation"
onClick={() => { this.handleClose(); }}>
.... ListItems code here ....
</List>
This is an Ok method, but I would much rather I had the SystemMenu code in another file and imported to this file and using props to manipulate the closing functions in the Menu.js file from the SystemMenu.js file. If anyone else wants to keep helping me I'd appreciate it.
In the demo, they're wrapping all of the ListItems in a div that handles closing the drawer:
<div
tabIndex={0}
role="button"
onClick={this.toggleDrawer('left', false)}
onKeyDown={this.toggleDrawer('left', false)}
>
{sideList}
</div>
In this code, toggleDrawer
is a function that sets the state used for the Drawer's open
prop. So when a key is pressed or a click occurs in one of the List's children, the event bubbles up to these handlers (as long as event.stopPropagation()
is not used) and state is changed.
In your code, you don't want the expander's click to close the drawer, so you have to handle these events differently.
You can add an onClick
handler to the List:
<ExpansionPanelDetails>
<List onClick={ () => { /* close the drawer here */ } }>
<ListItem
button
component="a"
href="/app/page1">
<ListItemText primary="Page 1" />
</ListItem>
<ListItem
button
component="a"
href="/app/page2">
<ListItemText primary="Page 2" />
</ListItem>
<ListItem
button
component="a"
href="/app/page3">
<ListItemText primary="Page 3" />
</ListItem>
</List>
</ExpansionPanelDetails>
Then, when the ListItems are clicked, the event will bubble to the List's handler, which sets state, closing the Drawer.
Based on the structure of your code, you may need to modify your implementation of Drawer to pass a function to your SystemMenu
component so that the click handler on its List can set the state of its parent because this.setState
affects the state of this
, which is the current instance of that component. It would not set the state of the Drawer.
UPDATE:
Based on your comments, I'm adding this bit.
In your SystemMenu component:
- accept a prop called closeDrawer
.
- use this function, from props, as the onClick
handler on List
closeDrawer
for now.this.closeDrawer
as `closeDrawer <SystemMenu closeDrawer={this.closeDrawer} ...>
This is one way to alter the local state of Menu, the parent component, from within SystemMenu, the child.
Place the close handler on the MuiPaper via PaperProps
:
const handleClose = () => console.log('closing drawer');
const MyDrawer = () => <Drawer PaperProps={{onClick: handleClose}} />
This will close the drawer when clicked anywhere inside the drawer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With