Now that we have basic routing available for our projects, let's work on making the Dashboard a bit more functional. We'll start with the imports.
import React, { useState} from 'react';
import { Drawer as MUIDrawer,
ListItem,
List,
ListItemIcon,
ListItemText,
Theme,
useTheme,
makeStyles,
createStyles,
AppBar,
Toolbar,
IconButton,
Typography,
Divider,
Button
} from '@material-ui/core';
import CssBaseline from '@material-ui/core/CssBaseline';
import MenuIcon from '@material-ui/icons/Menu'
import ChevronLeftIcon from '@material-ui/icons/ChevronLeft';
import ChevronRightIcon from '@material-ui/icons/ChevronRight'
import clsx from 'clsx';
import { RouteComponentProps, withRouter, Switch, Route } from "react-router-dom";
<aside>
💡 The following imports give our project access to the components inside of Material-UI, our first react hook, Material-UI icons, and a small component called clsx
that allows our component the ability to parse class names that are nested inside of objects. Lastly, we will need access to the router for our dashboard, so we also import some of the needed components available there. (Specifically, we will need to use withRouter
to pass routing props down to our component).
</aside>
We will now need to add the following class declarations for our Material-UI styled component. Again, we will employ the use of the createStyles
hook, but this time we add a bit more flavor with the makeStyles
hook. The makeStyles
hook allows us to add an overall theme from Material-UI (we're using the default violet theme) for a better styling experience overall. However, we still need some custom CSS for the look and feel we're going for.
const drawerWidth = 240;
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
display: 'flex',
},
appBar: {
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
},
appBarShift: {
width: `calc(100% - ${drawerWidth}px)`,
marginLeft: drawerWidth,
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
},
menuButton: {
marginRight: theme.spacing(2),
},
hide: {
display: 'none',
},
drawer: {
width: drawerWidth,
flexShrink: 0,
},
drawerPaper: {
width: drawerWidth,
},
drawerHeader: {
display: 'flex',
alignItems: 'center',
padding: theme.spacing(0, 1),
// necessary for content to be below app bar
...theme.mixins.toolbar,
justifyContent: 'flex-end',
},
content: {
flexGrow: 1,
padding: theme.spacing(3),
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
marginLeft: -drawerWidth,
},
contentShift: {
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
marginLeft: 0,
},
toolbar:{
display: 'flex',
},
toolbar_button: {
marginLeft: 'auto'
}
}),
);
interface DashProps{
history: RouteComponentProps["history"];
location: RouteComponentProps['location'];
match: RouteComponentProps['match'];
}
<aside> 💡 This ensures that we are using the correct DataType inside of our Dashboard's props object. This is the benefit of using TypeScript. We can make sure our data will look a certain way...BEFORE we try to run it.
</aside>
Let's take a look at what our potential Dashboard props object could look like:
history: {length: 2, action: "PUSH", location: {…}, createHref: ƒ, push: ƒ, …}
location: {pathname: "/dashboard", search: "", hash: "", state: undefined, key: "w4tfi5"}
match: {path: "/dashboard", url: "/dashboard", isExact: true, params: {…}}
staticContext: undefined
__proto__: Object
Alright! With all of the other stuff out of the way now, we can finally add/update our Dashboard. We'll do this with the following code(There's a lot btw):
export const Dashboard = withRouter(( props:DashProps ) => {
console.log(props)
const { history } = props;
const classes = useStyles();
const theme = useTheme();
const [open, setOpen] = useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
const itemsList = [
{
text: 'Home',
onClick: () => history.push('/')
},
{
text: 'Sign In',
onClick: () => history.push('/signin')
}
]
return (
<div className={classes.root}>
<CssBaseline />
<AppBar
position="fixed"
className={clsx(classes.appBar, {
[classes.appBarShift]: open,
})}
>
<Toolbar className={classes.toolbar}>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={handleDrawerOpen}
edge="start"
className={clsx(classes.menuButton, open && classes.hide)}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap>
Dashboard
</Typography>
<Button className={classes.toolbar_button}>Create New Drone</Button>
</Toolbar>
</AppBar>
<MUIDrawer
className={classes.drawer}
variant="persistent"
anchor="left"
open={open}
classes={{
paper: classes.drawerPaper,
}}
>
<div className={classes.drawerHeader}>
<IconButton onClick={handleDrawerClose}>
{theme.direction === 'ltr' ? <ChevronLeftIcon /> : <ChevronRightIcon />}
</IconButton>
</div>
<Divider />
<List>
{itemsList.map((item, index) => {
const { text, onClick } = item;
return (
<ListItem button key={text} onClick={onClick}>
<ListItemText primary={text} />
</ListItem>
);
})}
</List>
</MUIDrawer>
<main
className={clsx(classes.content, {
[classes.contentShift]: open,
})}
>
<div className={classes.drawerHeader} />
<h1>Hello World Until Data Shows Up</h1>
{/* <DataTable /> We will add this shortly*/}
</main>
</div>
)
});
Ok, now let's break down what's going on. First, we will look at the beginning of our component function. There are a few things happening here.
console.log(props)
const { history } = props;
const classes = useStyles();
const theme = useTheme();
const [open, setOpen] = useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};