How to contain the width of the main component in Material UI Drawer component?

Viewed 1411

I'm trying to use a Drawer in Material UI.

I want the width of the main div to take up whatever available space is left in the viewport after the menu drawer has used its space.

This is my component. I've tried setting width to auto and inherit on the content class, but it doesn't change anything.

I want the main div (where the content for a list item in the drawer) is rendered, to take only as much space is available without scrolling horizontally.

This code sandbox shows the problem: https://codesandbox.io/s/material-tab-scrolling-h13pi?file=/test.jsx

import React, {  useState, useEffect } from 'react';
import { Switch, Route, Link, BrowserRouter } from "react-router-dom";
import clsx from 'clsx';
import { makeStyles, useTheme } from '@material-ui/core/styles';
import Drawer from '@material-ui/core/Drawer';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import List from '@material-ui/core/List';
import CssBaseline from '@material-ui/core/CssBaseline';
import Typography from '@material-ui/core/Typography';
import Divider from '@material-ui/core/Divider';
import IconButton from '@material-ui/core/IconButton';
import Menu from '@material-ui/core/Menu';
import MenuItem from '@material-ui/core/MenuItem';
import Container from '@material-ui/core/Container'

import MenuIcon from '@material-ui/icons/Menu';
import ChevronLeftIcon from '@material-ui/icons/ChevronLeft';
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';
import InboxIcon from '@material-ui/icons/MoveToInbox';
import MailIcon from '@material-ui/icons/Mail';
import AccountCircle from '@material-ui/icons/AccountCircle';
import PaletteIcon from '@material-ui/icons/Palette';
import CenterFocusWeakIcon from '@material-ui/icons/CenterFocusWeak';
import RoomServiceIcon from '@material-ui/icons/RoomService';
import StorefrontIcon from '@material-ui/icons/Storefront';
import AssignmentIcon from '@material-ui/icons/Assignment';
import NotificationsIcon from '@material-ui/icons/Notifications';
import AccountBoxIcon from '@material-ui/icons/AccountBox';
import ContactSupportIcon from '@material-ui/icons/ContactSupport';
import BookIcon from '@material-ui/icons/Book';
import TuneIcon from '@material-ui/icons/Tune';
import SettingsIcon from '@material-ui/icons/Settings';
import CloseIcon from '@material-ui/icons/Close';
import DashboardFooter from "./DashboardFooter";

const drawerWidth = 240;

const useStyles = makeStyles((theme) => ({
  root: {
    display: 'flex',
  },
  appBar: {
    zIndex: theme.zIndex.drawer + 1,
    transition: theme.transitions.create(['width', 'margin'], {
      easing: theme.transitions.easing.sharp,
      duration: theme.transitions.duration.leavingScreen,
    }),
  },
  appBarShift: {
    marginLeft: drawerWidth,
    width: `calc(100% - ${drawerWidth}px)`,
    transition: theme.transitions.create(['width', 'margin'], {
      easing: theme.transitions.easing.sharp,
      duration: theme.transitions.duration.enteringScreen,
    }),
  },
  menuButton: {
    marginRight: 36,
  },
  hide: {
    display: 'none',
  },
  drawer: {
    width: drawerWidth,
    flexShrink: 0,
    whiteSpace: 'nowrap',
  },
  drawerOpen: {
    width: drawerWidth,
    transition: theme.transitions.create('width', {
      easing: theme.transitions.easing.sharp,
      duration: theme.transitions.duration.enteringScreen,
    }),
  },
  drawerClose: {
    transition: theme.transitions.create('width', {
      easing: theme.transitions.easing.sharp,
      duration: theme.transitions.duration.leavingScreen,
    }),
    overflowX: 'hidden',
    width: theme.spacing(7) + 1,
    [theme.breakpoints.up('sm')]: {
      width: theme.spacing(9) + 1,
    },
  },
  toolbar: {
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'flex-end',
    padding: theme.spacing(0, 1),
    // necessary for content to be below app bar
    ...theme.mixins.toolbar,
  },
  content: {
    flexGrow: 1,
    padding: theme.spacing(3),
    // autoWidth: false
    
  },
  container: {
    paddingTop: theme.spacing(4),
    paddingBottom: theme.spacing(4),
  },
}));

export default function MiniDrawer() {
  const classes = useStyles();
  const theme = useTheme();
  const [open, setOpen] = React.useState(false);
 //   const { performingAction, user, userData, roles } = this.props;

  
  const handleDrawerOpen = () => {
    setOpen(true);
  };

  const handleDrawerClose = () => {
    setOpen(false);
  };
 
  return (
    <div className={classes.root}>
      <CssBaseline />
      <AppBar
        position="fixed"
        className={clsx(classes.appBar, {
          [classes.appBarShift]: open,
        })}
      >
        <Toolbar>
          <IconButton
            color="inherit"
            aria-label="open drawer"
            onClick={handleDrawerOpen}
            edge="start"
            className={clsx(classes.menuButton, {
              [classes.hide]: open,
            })}
          >
            <MenuIcon />
          </IconButton>
          <Typography variant="h6" noWrap>
            {process.env.REACT_APP_TITLE}
            
          </Typography>
            
          <IconButton color="inherit"><CloseIcon /></IconButton>    
        </Toolbar>
      </AppBar>
      <BrowserRouter>
        <Drawer
            variant="permanent"
            className={clsx(classes.drawer, {
            [classes.drawerOpen]: open,
            [classes.drawerClose]: !open,
            })}
            classes={{
            paper: clsx({
                [classes.drawerOpen]: open,
                [classes.drawerClose]: !open,
            }),
            }}
        >
            <div className={classes.toolbar}>
            <IconButton onClick={handleDrawerClose}>
                {theme.direction === 'rtl' ? <ChevronRightIcon /> : <ChevronLeftIcon />}
            </IconButton>
            </div>
            
            <List>
            
                <ListItem button key="1" component={Link} to={"/DSM" }>
                    <ListItemIcon><PaletteIcon /></ListItemIcon>
                    <ListItemText primary="DS"></ListItemText> 
                </ListItem>
                <ListItem button key="2" component={Link} to={"/MMM" }>
                    <ListItemIcon><CenterFocusWeakIcon /></ListItemIcon>
                    <ListItemText>MM</ListItemText> 
                </ListItem>
                <ListItem button key="3" component={Link} to={"/RCM" }>
                    <ListItemIcon><StorefrontIcon /></ListItemIcon>
                    <ListItemText>Centre</ListItemText> 
                </ListItem>
                <ListItem button key="4" component={Link} to={"/TT" }>
                    <ListItemIcon><AssignmentIcon /></ListItemIcon>
                    <ListItemText>TT</ListItemText> 
                </ListItem>
                
            </List>
            <Divider />
            <List>
                <ListItem button key="5"  component={Link} to={"/Profile" }>
                <ListItemIcon> <AccountBoxIcon /></ListItemIcon>
                <ListItemText>Profile</ListItemText> 
                </ListItem>
                <ListItem button key="6"  component={Link} to={"/Account" }>
                <ListItemIcon> <SettingsIcon /></ListItemIcon>
                <ListItemText>Account</ListItemText> 
                </ListItem>
                <ListItem button key="7"  component={Link} to={"/Mail" }>
                <ListItemIcon> <MailIcon /></ListItemIcon>
                <ListItemText>Mail</ListItemText> 
                </ListItem>
                <ListItem button key="8"  component={Link} to={"/Notifications" }>
                <ListItemIcon> <NotificationsIcon /></ListItemIcon>
                <ListItemText>Notifications</ListItemText>
                </ListItem>
            
            </List>
            <Divider />
            <List>
                
                <ListItem button key="9"  component={Link} to={"/CM" }>
                    <ListItemIcon> <BookIcon /></ListItemIcon>
                    <ListItemText>Centre</ListItemText> 
                </ListItem>
                <ListItem button key="10"  component={Link} to={"/DCM" }>
                    <ListItemIcon><RoomServiceIcon /></ListItemIcon>
                    <ListItemText>Desk</ListItemText> 
                </ListItem>
                <ListItem button key="11"  component={Link} to={"/Support" }>
                <ListItemIcon> <ContactSupportIcon /></ListItemIcon>
                <ListItemText>Help and Support</ListItemText>
                </ListItem>
            
            
            </List>
        </Drawer>
        <main className={classes.content}>
          <div className={classes.toolbar} />

            <Switch>
                <Route path="/DCM" component={""} />
                <Route path="/MMM" render={() => <div>Page mm</div>} />
            </Switch>
            <DashboardFooter />
        </main>
     </BrowserRouter>   
     
    </div>
      
      
  );
}
2 Answers

The main issue is the following in dash.jsx:

  root: {
    display: 'flex',
  },

This is somehow defeating the mechanism in Tabs for its "scrollable tabs" functionality and results in the Tabs taking up as much width as is needed to display all of its tab buttons instead of showing scroll buttons. display: 'flex' makes it a little easier to manage the content width (such that it automatically adjusts based on the drawer width), but it isn't difficult to manage this in other ways.

Alternative 1

In my modified version of your sandbox, to remediate the effects of removing display: 'flex' from the root, I'm adding padding-left to the content with the same width as the drawer:

  content: {
    padding: theme.spacing(3),
    paddingLeft: theme.spacing(7) + 1,
    [theme.breakpoints.up("sm")]: {
      paddingLeft: theme.spacing(9) + 1
    }
  },
  contentDrawerOpen: {
    paddingLeft: drawerWidth
  },
...
<main className={clsx(classes.content, {[classes.contentDrawerOpen]: open})}>

Edit Material tab scrolling


Alternative 2

Another alternative is to leave display: 'flex', but specify the max-width appropriately on the content.

  content: {
    padding: theme.spacing(3),
    maxWidth: `calc(100vw - ${theme.spacing(7) + 1}px)`,
    [theme.breakpoints.up("sm")]: {
      maxWidth: `calc(100vw - ${theme.spacing(9) + 1}px)`
    }
  },
  contentDrawerOpen: {
    maxWidth: `calc(100vw - ${drawerWidth}px)`
  },

Edit Material tab scrolling


Alternative 3 (inspired by Ahmed Mokhtar's answer)

It also appears to be sufficient to just add overflow: "auto" to the content class for the <main> element:

  content: {
    padding: theme.spacing(3),
    overflow: "auto"
  },

Edit Material tab scrolling

This last alternative is definitely the simplest.

I fixed that by adding overflow: auto to main which I changed to use the Container component:

container: {
  overflow: "auto"
}

{/* I added overflow: auto to this container*/}
<Container
  component="main"
  maxWidth={false}
  className={classes.container}
>
  <div className={classes.toolbar} />

  <Switch>
    <Route path="/dash" component={Dash} />
    <Route path="/MatchmakerMenu" component={Dash} />
  </Switch>
</Container>

CodeSandbox

Related