Categories
Material UI

Material UI — Steppers

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to add steppers with Material UI.

Stepper

A stepper lets us show the steps that a user has to go through to complete a process.

To use it, we can use the Stepper component add the stepper:

import React from "react";
import Stepper from "@material-ui/core/Stepper";
import Step from "@material-ui/core/Step";
import StepLabel from "@material-ui/core/StepLabel";
import Button from "@material-ui/core/Button";

function getSteps() {
  return ["step 1", "step 2", "step 3"];
}

function getStepContent(step) {
  switch (step) {
    case 0:
      return "do step 1";
    case 1:
      return "do step 2";
    case 2:
      return "do steo 3";
    default:
      return "unknown step";
  }
}

export default function App() {
  const [activeStep, setActiveStep] = React.useState(0);
  const [skipped, setSkipped] = React.useState(new Set());
  const steps = getSteps();

  const isStepOptional = step => {
    return step === 1;
  };

  const isStepSkipped = step => {
    return skipped.has(step);
  };

  const handleNext = () => {
    let newSkipped = skipped;
    if (isStepSkipped(activeStep)) {
      newSkipped = new Set(newSkipped.values());
      newSkipped.delete(activeStep);
    }

  setActiveStep(prevActiveStep => prevActiveStep + 1);
    setSkipped(newSkipped);
  };

  const handleBack = () => {
    setActiveStep(prevActiveStep => prevActiveStep - 1);
  };

  const handleSkip = () => {
    if (!isStepOptional(activeStep)) {
      throw new Error("You can't skip a step that isn't optional.");
    }

  setActiveStep(prevActiveStep => prevActiveStep + 1);
    setSkipped(prevSkipped => {
      const newSkipped = new Set(prevSkipped.values());
      newSkipped.add(activeStep);
      return newSkipped;
    });
  };

  const handleReset = () => {
    setActiveStep(0);
  };

  return (
    <div>
      <Stepper activeStep={activeStep}>
        {steps.map((label, index) => {
          const stepProps = {};
          const labelProps = {};
          if (isStepOptional(index)) {
            labelProps.optional = "optional";
          }
          if (isStepSkipped(index)) {
            stepProps.completed = false;
          }
          return (
            <Step key={label} {...stepProps}>
              <StepLabel {...labelProps}>{label}</StepLabel>
            </Step>
          );
        })}
      </Stepper>
      <div>
        {activeStep === steps.length ? (
          <div>
            All steps completed
            <Button onClick={handleReset}>Reset</Button>
          </div>
        ) : (
          <div>
            {getStepContent(activeStep)}
            <div>
              <Button disabled={activeStep === 0} onClick={handleBack}>
                Back
              </Button>
              {isStepOptional(activeStep) && (
                <Button
                  variant="contained"
                  color="primary"
                  onClick={handleSkip}
                >
                  Skip
                </Button>
              )}

              <Button variant="contained" color="primary" onClick={handleNext}>
                {activeStep === steps.length - 1 ? "Finish" : "Next"}
              </Button>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

We create the step headings with the getSteps function.

The content of each step is in the getStepContent function.

When we click the Next button, handleNext is called.

Then we add the steps that are added to the skipped state.

handleBack is run when the back button is clicked.

We just set the activeStep to the previous step.

The handleSkip function is called when the skip button is clicked.

We check if an active step is optional.

If it’s optional, then we let them skip it and add it to the skipped set.

In the JSX code, we use the Stepper component with the Step prop to add the steps.

Then we add the content for the step and the buttons below it.

The content is shown according to the activeStep value.

Alternative Label

We can change the step labels to what we want with the StepLabel component.

For instance, we can write:

import React from "react";
import Stepper from "@material-ui/core/Stepper";
import Step from "@material-ui/core/Step";
import StepLabel from "@material-ui/core/StepLabel";
import Button from "@material-ui/core/Button";

function getSteps() {
  return ["step 1", "step 2", "step 3"];
}

function getStepContent(step) {
  switch (step) {
    case 0:
      return "do step 1";
    case 1:
      return "do step 2";
    case 2:
      return "do steo 3";
    default:
      return "unknown step";
  }
}

export default function App() {
  const [activeStep, setActiveStep] = React.useState(0);
  const steps = getSteps();

  const handleNext = () => {
    setActiveStep(prevActiveStep => prevActiveStep + 1);
  };

  const handleBack = () => {
    setActiveStep(prevActiveStep => prevActiveStep - 1);
  };

  const handleReset = () => {
    setActiveStep(0);
  };

  return (
    <div>
      <Stepper activeStep={activeStep} alternativeLabel>
        {steps.map(label => (
          <Step key={label}>
            <StepLabel>{label}</StepLabel>
          </Step>
        ))}
      </Stepper>
      <div>
        {activeStep === steps.length ? (
          <div>
            All steps completed
            <Button onClick={handleReset}>Reset</Button>
          </div>
        ) : (
          <div>
            {getStepContent(activeStep)}

<div>
              <Button disabled={activeStep === 0} onClick={handleBack}>
                Back
              </Button>
              <Button variant="contained" color="primary" onClick={handleNext}>
                {activeStep === steps.length - 1 ? "Finish" : "Next"}
              </Button>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

We added the alternativeLabel prop to let us override the label.

Then in the render children prop, we return our own Step component with the StepLabel to display the step number.

Conclusion

We can add a stepper with the Stepper component.

We can make steps optional and customize the labels.

The content can be displayed below it.

Categories
Material UI

Material UI — Stepper Customization

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to customize steppers with Material UI.

Non-Linear Steppers

We can add non-linear steppers to add a stepper that allows users to navigate to any step they want.

For example, we can write:

import React from "react";
import Stepper from "@material-ui/core/Stepper";
import Step from "@material-ui/core/Step";
import StepButton from "@material-ui/core/StepButton";
import Button from "@material-ui/core/Button";

function getSteps() {
  return ["step 1", "step 2", "step 3"];
}

function getStepContent(step) {
  switch (step) {
    case 0:
      return "do step 1";
    case 1:
      return "do step 2";
    case 2:
      return "do step 3";
    default:
      return "unknown step";
  }
}

export default function App() {
  const [activeStep, setActiveStep] = React.useState(0);
  const [completed, setCompleted] = React.useState({});
  const steps = getSteps();

  const totalSteps = () => {
    return steps.length;
  };

  const completedSteps = () => {
    return Object.keys(completed).length;
  };

  const isLastStep = () => {
    return activeStep === totalSteps() - 1;
  };

  const allStepsCompleted = () => {
    return completedSteps() === totalSteps();
  };

  const handleNext = () => {
    const newActiveStep =
      isLastStep() && !allStepsCompleted()
        ? steps.findIndex((step, i) => !(i in completed))
        : activeStep + 1;
    setActiveStep(newActiveStep);
  };

  const handleBack = () => {
    setActiveStep(prevActiveStep => prevActiveStep - 1);
  };

  const handleStep = step => () => {
    setActiveStep(step);
  };

  const handleComplete = () => {
    const newCompleted = completed;
    newCompleted[activeStep] = true;
    setCompleted(newCompleted);
    handleNext();
  };

  const handleReset = () => {
    setActiveStep(0);
    setCompleted({});
  };

return (
    <div>
      <Stepper nonLinear activeStep={activeStep}>
        {steps.map((label, index) => (
          <Step key={label}>
            <StepButton
              onClick={handleStep(index)}
              completed={completed[index]}
            >
              {label}
            </StepButton>
          </Step>
        ))}
      </Stepper>
      <div>
        {allStepsCompleted() ? (
          <div>
            All steps completed - you&apos;re finished
            <Button onClick={handleReset}>Reset</Button>
          </div>
        ) : (
          <div>
            {getStepContent(activeStep)}

            <div>
              <Button disabled={activeStep === 0} onClick={handleBack}>
                Back
              </Button>
              <Button variant="contained" color="primary" onClick={handleNext}>
                Next
              </Button>
              {activeStep !== steps.length &&
                (completed[activeStep] ? (
                  `Step {activeStep + 1} already completed`
                ) : (
                  <Button
                    variant="contained"
                    color="primary"
                    onClick={handleComplete}
                  >
                    {completedSteps() === totalSteps() - 1
                      ? "Finish"
                      : "Complete Step"}
                  </Button>
                ))}
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

We add the nonLinear prop to let users click on any step they want.

The Next button calls handleNext to go to the next step or go to the step that isn’t completed.

handleBack is run when the back button is clicked.

It sets the step to a previous step.

handleStep is run when we click on a step.

We also set the completed prop to indicate which step is completed.

handleReset is run when the reset button is clicked.

It sets the activeStep state to 0 and set the completed state to an empty object to clear the completed steps.

For example, we can write:

import React from "react";
import Stepper from "@material-ui/core/Stepper";
import Step from "@material-ui/core/Step";
import StepButton from "@material-ui/core/StepButton";
import Button from "@material-ui/core/Button";

function getSteps() {
  return ["step 1", "step 2", "step 3"];
}

function getStepContent(step) {
  switch (step) {
    case 0:
      return "do step 1";
    case 1:
      return "do step 2";
    case 2:
      return "do step 3";
    default:
      return "unknown step";
  }
}

export default function App() {
  const [activeStep, setActiveStep] = React.useState(0);
  const [completed, setCompleted] = React.useState(new Set());
  const [skipped, setSkipped] = React.useState(new Set());
  const steps = getSteps();

const totalSteps = () => {
    return getSteps().length;
  };

const isStepOptional = step => {
    return step === 1;
  };

const handleSkip = () => {
    if (!isStepOptional(activeStep)) {
      throw new Error("You can't skip a step that isn't optional.");
    }

setActiveStep(prevActiveStep => prevActiveStep + 1);
    setSkipped(prevSkipped => {
      const newSkipped = new Set(prevSkipped.values());
      newSkipped.add(activeStep);
      return newSkipped;
    });
  };

const skippedSteps = () => {
    return skipped.size;
  };

const completedSteps = () => {
    return completed.size;
  };

const allStepsCompleted = () => {
    return completedSteps() === totalSteps() - skippedSteps();
  };

const isLastStep = () => {
    return activeStep === totalSteps() - 1;
  };

const handleNext = () => {
    const newActiveStep =
      isLastStep() && !allStepsCompleted()
        ? steps.findIndex((step, i) => !completed.has(i))
        : activeStep + 1;

setActiveStep(newActiveStep);
  };

const handleBack = () => {
    setActiveStep(prevActiveStep => prevActiveStep - 1);
  };

const handleStep = step => () => {
    setActiveStep(step);
  };

const handleComplete = () => {
    const newCompleted = new Set(completed);
    newCompleted.add(activeStep);
    setCompleted(newCompleted);

if (completed.size !== totalSteps() - skippedSteps()) {
      handleNext();
    }
  };

const handleReset = () => {
    setActiveStep(0);
    setCompleted(new Set());
    setSkipped(new Set());
  };

const isStepSkipped = step => {
    return skipped.has(step);
  };

function isStepComplete(step) {
    return completed.has(step);
  }

return (
    <div>
      <Stepper alternativeLabel nonLinear activeStep={activeStep}>
        {steps.map((label, index) => {
          const stepProps = {};
          const buttonProps = {};
          if (isStepOptional(index)) {
            buttonProps.optional = `Optional`;
          }
          if (isStepSkipped(index)) {
            stepProps.completed = false;
          }
          return (
            <Step key={label} {...stepProps}>
              <StepButton
                onClick={handleStep(index)}
                completed={isStepComplete(index)}
                {...buttonProps}
              >
                {label}
              </StepButton>
            </Step>
          );
        })}
      </Stepper>
      <div>
        {allStepsCompleted() ? (
          <div>
            All steps completed
            <Button onClick={handleReset}>Reset</Button>
          </div>
        ) : (
          <div>
            {getStepContent(activeStep)}

<div>
              <Button disabled={activeStep === 0} onClick={handleBack}>
                Back
              </Button>
              <Button variant="contained" color="primary" onClick={handleNext}>
                Next
              </Button>
              {isStepOptional(activeStep) && !completed.has(activeStep) && (
                <Button
                  variant="contained"
                  color="primary"
                  onClick={handleSkip}
                >
                  Skip
                </Button>
              )}

                {activeStep !== steps.length &&
                (completed.has(activeStep) ? (
                  `Step ${activeStep + 1} already completed`
                ) : (
                  <Button
                    variant="contained"
                    color="primary"
                    onClick={handleComplete}
                  >
                    {completedSteps() === totalSteps() - 1
                      ? "Finish"
                      : "Complete Step"}
                  </Button>
                ))}
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

We have our Step and StepButton to add the step button.

We apply all the props and allow users to click on each link regardless of status.

buttonProps.optioal lets us render a string or component to indicate that the step is optional.

We can also set the completed property of stepProps to false .

Conclusion

We can customize non-linear steppers to our liking.

The labels and content can be changed with various components.

Categories
Material UI

Material UI — Snack Bars

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to add snack bars with Material UI.

Snackbar

Snack bars let us display messages about something happening in our app.

To create a simple one, we can use the Snackbar component.

For example, we can write:

import React from "react";
import Button from "@material-ui/core/Button";
import Snackbar from "@material-ui/core/Snackbar";
import IconButton from "@material-ui/core/IconButton";
import CloseIcon from "@material-ui/icons/Close";

export default function App() {
  const [open, setOpen] = React.useState(false);

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

  const handleClose = (event, reason) => {
    if (reason === "clickaway") {
      return;
    }

    setOpen(false);
  };

  return (
    <div>
      <Button onClick={handleClick}>Open snackbar</Button>
      <Snackbar
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "left"
        }}
        open={open}
        autoHideDuration={6000}
        onClose={handleClose}
        message="hello world"
        action={
          <React.Fragment>
            <Button color="secondary" size="small" onClick={handleClose}>
              ok
            </Button>
            <IconButton size="small" color="inherit" onClick={handleClose}>
              <CloseIcon fontSize="small" />
            </IconButton>
          </React.Fragment>
        }
      />
    </div>
  );
}

We created a snack bar with the message is set to 'hello world' .

And we have more content in the action prop.

They include a button that we can click and an IconButton to let us close the snack bar.

Customized Snack Bars

We can style snack bars with various styles.

To style it, we add the severity prop.

For instance, we can write:

import React from "react";
import MuiAlert from "@material-ui/lab/Alert";

function Alert(props) {
  return <MuiAlert elevation={6} variant="filled" {...props} />;
}
export default function App() {
  return (
    <div>
      <Alert severity="error">error!</Alert>
      <Alert severity="warning">warning!</Alert>
      <Alert severity="info">info!</Alert>
      <Alert severity="success">success!</Alert>
    </div>
  );
}

Instead of using the Snackbar component, we used the Alert component instead.

It’s in the @material-ui/lab package instead of the @material-ui/core package.

Positioned Snack Bars

We can position the snack bar with the anchorOrigin prop.

To position it, we can write:

import React from "react";
import Snackbar from "@material-ui/core/Snackbar";
import Button from "@material-ui/core/Button";

export default function App() {
  const [state, setState] = React.useState({
    open: false,
    vertical: "top",
    horizontal: "center"
  });

  const { vertical, horizontal, open } = state;

  const handleClick = newState => () => {
    setState({ open: true, ...newState });
  };

  const handleClose = () => {
    setState({ ...state, open: false });
  };

  return (
    <div>
      <Button onClick={handleClick({ vertical: "top", horizontal: "center" })}>
        open
      </Button>

      <Snackbar
        anchorOrigin={{ vertical, horizontal }}
        open={open}
        onClose={handleClose}
        message="hello world"
      />
    </div>
  );
}

to set the position of the snack bar with the anchorOrigin prop.

It’s set to an object with the vertical and horizontal properties.

We set it to 'top' and 'center' respectively to show it at the top of the screen and centered.

Message Length

The width can vary in back bars.

We can write a long message by writing text in multiple lines:

import React from "react";
import Snackbar from "@material-ui/core/Snackbar";
import Button from "@material-ui/core/Button";

export default function App() {
  const [state, setState] = React.useState({
    open: false
  });

  const { open } = state;

  const handleClick = () => {
    setState({ open: true });
  };

  const handleClose = () => {
    setState({ ...state, open: false });
  };

  return (
    <div>
      <Button onClick={handleClick}>open</Button>

      <Snackbar
        open={open}
        onClose={handleClose}
        message={
          "Lorem ipsum dolor sit amet, consectetur adipiscing elit.
          Aenean mollis, metus vitae vestibulum suscipit, eros nisi ultrices urna."
        }
        action={
          <Button color="secondary" size="small">
            lorem ipsum dolorem
          </Button>
        }
      />
    </div>
  );
}

We have the message prop with a string expression that spans multiple lines but without line breaks.

Also, we have the action prop to show a button.

Conclusion

We can use snack bars to display different messages.

They can be styled and we can apply transitions to it.

Categories
Material UI

Material UI — Snack Bar Customization

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to customize snack bars with Material UI.

Consecutive Snack Bars

We can display multiple snack bars consecutively with some logic.

For example, we can write:

import React from "react";
import Button from "@material-ui/core/Button";
import Snackbar from "@material-ui/core/Snackbar";
import IconButton from "@material-ui/core/IconButton";
import CloseIcon from "@material-ui/icons/Close";

export default function App() {
  const [snackPack, setSnackPack] = React.useState([]);
  const [open, setOpen] = React.useState(false);
  const [messageInfo, setMessageInfo] = React.useState(undefined);

  React.useEffect(() => {
    if (snackPack.length && !messageInfo) {
      setMessageInfo({ ...snackPack[0] });
      setSnackPack(prev => prev.slice(1));
      setOpen(true);
    } else if (snackPack.length && messageInfo && open) {
      setOpen(false);
    }
  }, [snackPack, messageInfo, open]);

  const handleClick = message => () => {
    setSnackPack(prev => [...prev, { message, key: new Date().getTime() }]);
  };

  const handleClose = (event, reason) => {
    if (reason === "clickaway") {
      return;
    }
    setOpen(false);
  };

  const handleExited = () => {
    setMessageInfo(undefined);
  };

  return (
    <div>
      <Button onClick={handleClick("Message A")}>Show A</Button>
      <Button onClick={handleClick("Message B")}>Show B</Button>
      <Snackbar
        open={open}
        autoHideDuration={6000}
        onClose={handleClose}
        onExited={handleExited}
        message={messageInfo ? messageInfo.message : undefined}
        action={
          <React.Fragment>
            <Button color="secondary" size="small" onClick={handleClose}>
              foo
            </Button>
            <IconButton color="inherit" onClick={handleClose}>
              <CloseIcon />
            </IconButton>
          </React.Fragment>
        }
      />
    </div>
  );
}

The snackPack state has the items to display.

We set the message with setMessageInfo .

This is done in the useEffect callback.

When a button is clicked, then the old message is removed from the snackPack with setSnackPack and then we add the new one.

The handleClick function lets us change the message .

Now when we click the button, then old is gone and the new one is displayed.

Snack Bars and Floating Action Buttons

We should make the snack bar display above floating action buttons in mobile.

To do that, we can write:

import React from "react";
import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@material-ui/core/Toolbar";
import Typography from "@material-ui/core/Typography";
import Button from "@material-ui/core/Button";
import Fab from "@material-ui/core/Fab";
import AddIcon from "@material-ui/icons/Add";
import Snackbar from "@material-ui/core/Snackbar";
import { makeStyles } from "@material-ui/core/styles";

const useStyles = makeStyles(theme => ({
  fab: {
    position: "absolute",
    bottom: theme.spacing(2),
    right: theme.spacing(2)
  },
  snackbar: {
    [theme.breakpoints.down("xs")]: {
      bottom: 90
    }
  }
}));

export default function App() {
  const classes = useStyles();

  return (
    <div>
      <AppBar position="static" color="primary">
        <Toolbar>
          <Typography variant="h6" color="inherit">
            App
          </Typography>
        </Toolbar>
      </AppBar>
      <Fab color="secondary" className={classes.fab}>
        <AddIcon />
      </Fab>
      <Snackbar
        open
        autoHideDuration={6000}
        message="hello"
        action={
          <Button color="inherit" size="small">
            ok
          </Button>
        }
        className={classes.snackbar}
      />
    </div>
  );
}

to move the snack bar above the floating action button with some styles.

We made the snack bar display at the bottom with the snackbar class set to bottom: 90 when the breakpoint is xs.

Also, we set the floating action button to have an absolute position with the fab class.

These are all in the makeStyles function.

Change Transition

We can add transition effects to our snack bar when it opens.

To do that, we pass in a value for the TransitionComponent .

For example, we write:

import React from "react";
import Button from "@material-ui/core/Button";
import Snackbar from "@material-ui/core/Snackbar";
import Grow from "@material-ui/core/Grow";

function GrowTransition(props) {
  return <Grow {...props} />;
}

export default function TransitionsSnackbar() {
  const [open, setOpen] = React.useState(false);

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

  const handleClose = () => {
    setOpen(false);
  };

  return (
    <div>
      <Button onClick={handleClick}>Grow</Button>
      <Snackbar
        open={open}
        onClose={handleClose}
        TransitionComponent={GrowTransition}
        message="lorem ipsum"
      />
    </div>
  );
}

We created the GrowTransition component and pass that into the TransitionComponent prop.

Alternatives include the Fade and Slide transitions.

Control Slide Direction

We can also change the slide direction if we use the Slide transition effect.

For example, we can write:

import React from "react";
import Button from "@material-ui/core/Button";
import Snackbar from "@material-ui/core/Snackbar";
import Slide from "@material-ui/core/Slide";

function TransitionLeft(props) {
  return <Slide {...props} direction="left" />;
}

export default function TransitionsSnackbar() {
  const [open, setOpen] = React.useState(false);

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

  const handleClose = () => {
    setOpen(false);
  };

  return (
    <div>
      <Button onClick={handleClick}>Slide</Button>
      <Snackbar
        open={open}
        onClose={handleClose}
        TransitionComponent={TransitionLeft}
        message="lorem ipsum"
      />
    </div>
  );
}

We have a Snackbar with the TransitionComponent set to TransitionLeft .

TransitionLeft has the Slide component to add the slide transition with the direction set to left to make it slide right to left.

Conclusion

We can add transitions to snack bars.

Also, we can display snack bars consecutively.

Categories
Material UI

Material UI — Responsive and Persistent Drawers

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to add drawers to Material UI.

Responsive Drawer

We can make a responsive drawer by adding some media queries to show and hide the drawer.

For example, we can write:

import React from "react";
import PropTypes from "prop-types";
import AppBar from "@material-ui/core/AppBar";
import CssBaseline from "@material-ui/core/CssBaseline";
import Divider from "@material-ui/core/Divider";
import Drawer from "@material-ui/core/Drawer";
import Hidden from "@material-ui/core/Hidden";
import IconButton from "@material-ui/core/IconButton";
import InboxIcon from "@material-ui/icons/MoveToInbox";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import MailIcon from "@material-ui/icons/Mail";
import MenuIcon from "@material-ui/icons/Menu";
import Toolbar from "@material-ui/core/Toolbar";
import Typography from "@material-ui/core/Typography";
import { makeStyles, useTheme } from "@material-ui/core/styles";

const drawerWidth = 240;

const useStyles = makeStyles(theme => ({
  root: {
    display: "flex"
  },
  drawer: {
    [theme.breakpoints.up("sm")]: {
      width: drawerWidth,
      flexShrink: 0
    }
  },
  appBar: {
    [theme.breakpoints.up("sm")]: {
      width: `calc(100% - ${drawerWidth}px)`,
      marginLeft: drawerWidth
    }
  },
  menuButton: {
    marginRight: theme.spacing(2),
    [theme.breakpoints.up("sm")]: {
      display: "none"
    }
  },

toolbar: theme.mixins.toolbar,
  drawerPaper: {
    width: drawerWidth
  },
  content: {
    flexGrow: 1,
    padding: theme.spacing(3)
  }
}));

export default function App(props) {
  const { window } = props;
  const classes = useStyles();
  const theme = useTheme();
  const [mobileOpen, setMobileOpen] = React.useState(false);

  const handleDrawerToggle = () => {
    setMobileOpen(!mobileOpen);
  };

  const drawer = (
    <div>
      <div className={classes.toolbar} />
      <List>
        <ListItem button key="home">
          <ListItemIcon>
            <MailIcon />
          </ListItemIcon>
          <ListItemText primary="home" />
        </ListItem>
      </List>
    </div>
  );

const container =
    window !== undefined ? () => window().document.body : undefined;

return (
    <div className={classes.root}>
      <CssBaseline />
      <AppBar position="fixed" className={classes.appBar}>
        <Toolbar>
          <IconButton
            color="inherit"
            edge="start"
            onClick={handleDrawerToggle}
            className={classes.menuButton}
          >
            <MenuIcon />
          </IconButton>
          <Typography variant="h6" noWrap>
            Responsive drawer
          </Typography>
        </Toolbar>
      </AppBar>
      <nav className={classes.drawer}>
        <Hidden smUp implementation="css">
          <Drawer
            container={container}
            variant="temporary"
            open={mobileOpen}
            onClose={handleDrawerToggle}
            classes={{
              paper: classes.drawerPaper
            }}
            ModalProps={{
              keepMounted: true
            }}
          >
            {drawer}
          </Drawer>
        </Hidden>
        <Hidden xsDown implementation="css">
          <Drawer
            classes={{
              paper: classes.drawerPaper
            }}
            variant="permanent"
            open
          >
            {drawer}
          </Drawer>
        </Hidden>
      </nav>
      <main className={classes.content}>
        <div className={classes.toolbar} />
        <Typography paragraph>Lorem ipsum dolor sit amet</Typography>
        <Typography paragraph>
          Consequat mauris nunc congue nisi vitae suscipit.
        </Typography>
      </main>
    </div>
  );
}

We use the Hidden component to display items with the given breakpoints.

We keep the drawer always open if the screen meets the breakpoint sm or up.

Otherwise, we show a menu with a button to open the drawer.

The main content is in the main element.

AppBar has the top bar which is always shown.

We have the mobileOpen state to track the open state.

It’s set as the value of the open prop.

Persistent Drawer

We can add a persistent drawer to show always show a button to open the drawer.

For example, we can write:

import React from "react";
import clsx from "clsx";
import { makeStyles, useTheme } from "@material-ui/core/styles";
import Drawer from "@material-ui/core/Drawer";
import CssBaseline from "@material-ui/core/CssBaseline";
import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@material-ui/core/Toolbar";
import List from "@material-ui/core/List";
import Typography from "@material-ui/core/Typography";
import Divider from "@material-ui/core/Divider";
import IconButton from "@material-ui/core/IconButton";
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";

export default function App() {
  const [open, setOpen] = React.useState(false);

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

const handleDrawerClose = () => {
    setOpen(false);
  };

return (
    <div>
      <CssBaseline />
      <AppBar position="fixed">
        <Toolbar>
          <IconButton color="inherit" onClick={handleDrawerOpen} edge="start">
            <MenuIcon />
          </IconButton>
          <Typography variant="h6" noWrap>
            Persistent drawer
          </Typography>
        </Toolbar>
      </AppBar>
      <Drawer variant="persistent" anchor="left" open={open}>
        <List>
          <ListItem button key="home">
            <ListItemIcon>
              <MailIcon />
            </ListItemIcon>
            <ListItemText primary="home" />
          </ListItem>
          <ListItem button key="close" onClick={handleDrawerClose}>
            <ListItemText primary="close" />
          </ListItem>
        </List>
      </Drawer>
      <main style={{ marginTop: 50 }}>
        <Typography paragraph>Lorem ipsum dolor sit amet</Typography>
        <Typography paragraph>
          Consequat mauris nunc congue nisi vitae suscipit
        </Typography>
      </main>
    </div>
  );
}

to create an app bar with a drawer that has the close button on the drawer.

We have the variant set to persistent to keep it always open unless we click close in the list item text.

The close button is at:

<ListItem button key="close" onClick={handleDrawerClose}>
  <ListItemText primary="close" />
</ListItem>

Conclusion

We can create a drawers in various ways.

We can make the drawer responsive or persistent.