Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MaterialUI-Next Close drawer on children click

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.

like image 640
LOTUSMS Avatar asked Oct 20 '25 13:10

LOTUSMS


2 Answers

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

  • In your Menu component:
  • add a function to close the Drawer, let's call it closeDrawer for now.
  • when rendering SystemMenu, pass 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.

like image 139
Ken Gregory Avatar answered Oct 22 '25 03:10

Ken Gregory


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.

like image 37
luukvhoudt Avatar answered Oct 22 '25 04:10

luukvhoudt



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!