I'm using Material-UI at my React Project and Im trying create a component that render a drawer and some other components inside it. But I'm having a lot of issues with the open drawer function... I tried passing simply as a state props but then my drawer won't close...
interface Props {
cartOpen: boolean;
}
const CartDrawer: React.FunctionComponent<Props> = ({ cartOpen }) => {
const classes = useStyles();
return (
<Drawer anchor="right" open={cartOpen}>
<div className={classes.cartDrawerDiv}>
<div className={classes.cartTitle}>
<List>
<ListItem>
<ShoppingCartIcon className={classes.icon} />
<Typography
style={{
fontFamily: "Montserrat, sans-serif",
fontSize: "15px",
fontWeight: 800,
marginLeft: "8px",
}}
>
1 Item
</Typography>
</ListItem>
</List>
</div>
<Divider />
</div>
</Drawer>
);
};
export default CartDrawer;
Example of a component using it...
...
<Button
color="secondary"
size="large"
variant="contained"
className={classes.buyButton}
style={{ fontWeight: 600 }}
onClick={() => setCartOpen(true)}
>
COMPRAR
</Button>
</div>
</div>
</div>
<CartDrawer cartOpen={cartOpen} />
You should add onClose to close Drawer
<CartDrawer cartOpen={cartOpen} onClose={() => setCartOpen(false)} />
interface Props {
cartOpen: boolean;
onClose: () => void
}
const CartDrawer: React.FunctionComponent<Props> = ({ cartOpen, onClose }) => {
const classes = useStyles();
return (
<Drawer anchor="right" open={cartOpen} onClose={onClose}>
...
Hey I have a background image for Home component. I've placed the background image in Header Component. In my Header component there is a navbar and the background image. I've place the Header component outside switch inside React Router so that some pages can access the navbar. Now what I want to do is when I go to a food description page I want to keep the navbar but I want to change/remove the background Image . How can I achieve that ?
App.js
function App() {
return (
<div>
<DataContextProvider>
<LoginContextProvider>
<Router>
<Header></Header>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/login" component={Login} />
<Route path="/food/:id" component={FoodDetail} />
</Switch>
</Router>
</LoginContextProvider>
</DataContextProvider>
</div>
);
}
Header.js
<div className={classes.grow}>
<AppBar position="fixed" style={{ backgroundColor: "white" }}>
<Container>
<Toolbar>
<Link to="/">
<Typography className={classes.title} variant="h6" noWrap>
Material-UI
</Typography>
</Link>
<div className={classes.grow} />
<div className={classes.sectionDesktop}>
{loggedInUser.email ? (
<MenuItem onClick={handleProfileMenuOpen}>
<IconButton
aria-label="account of current user"
aria-controls="primary-search-account-menu"
aria-haspopup="true"
color="inherit"
>
<AccountCircleIcon style={{ fill: "black" }} />
</IconButton>
<p className={classes.textColor}>{loggedInUser.name}</p>
</MenuItem>
) : (
<MenuItem onClick={handleLogin}>
<IconButton
aria-label="account of current user"
aria-controls="primary-search-account-menu"
aria-haspopup="true"
color="inherit"
>
<AccountCircleIcon style={{ fill: "black" }} />
</IconButton>
<p className={classes.textColor}>Login</p>
</MenuItem>
)}
<IconButton
aria-label="show 11 new notifications"
color="inherit"
>
<Badge badgeContent={0} color="secondary">
<ShoppingCart style={{ fill: "black" }}></ShoppingCart>
</Badge>
</IconButton>
</div>
<div className={classes.sectionMobile}>
<IconButton
aria-label="show more"
aria-controls={mobileMenuId}
aria-haspopup="true"
onClick={handleMobileMenuOpen}
color="inherit"
>
<MenuIcon style={{ fill: "black" }} />
</IconButton>
</div>
</Toolbar>
</Container>
</AppBar>
{renderMobileMenu}
{renderMenu}
<div style={{ backgroundImage: `url(${bg})` }} className="header">
<h2>Whatever you need, we will deliver it to you!</h2>
<Paper component="form" className="search">
<InputBase
className={classes.input}
placeholder="Search Keywords"
inputProps={{ "aria-label": "search google maps" }}
onChange={(e) => setSearch(e.target.value)}
/>
<IconButton
type="submit"
className={classes.iconButton}
aria-label="search"
>
<SearchIcon />
</IconButton>
</Paper>
</div>
</div>
);
};
FoodDetail.js
const FoodDetail = () => {
const { id } = useParams();
const [foodDetails, setFoodDetails] = useState({});
useEffect(() => {
fetch(`http://localhost:5000/api/foods/${id}`)
.then((res) => res.json())
.then((data) => {
setFoodDetails(data);
console.log(data);
});
}, [id]);
return (
<div>
<h1>Details</h1>
<h1>Name: {foodDetails.name} </h1>
</div>
);
};
You need to use 'useLocation' to get the page path and then add it in a className.
Header.js
import React from "react";
import { useLocation } from "react-router-dom";
import "./Header.css";
import Navigation from "../Navigation/Navigation";
export default function Header() {
const path = useLocation().pathname;
const location = path.split("/")[1];
return (
<div className={"header " + location}>
<Navigation />
</div>
);
}
Header.css
.header {
height: 300px;
}
.header.home {
background: grey
}
.header.about {
background: red
}
.header.contact {
background: yellow
}
Check my complete demo : Stackblitz
I am trying to render the Material UI drawer on all pages except the login page but it's not working accordingly.
In short
My App component has 2 routes login and dashboard (Both Working)
Then my dashboard page has multiple routes for home and about pages (None of them working.)
Below is my code separately. Also, I'm attaching codeSandBox link for the same
Expected Output To understand my desired output
On login URL = <Login/> should render
On dashboard URL = <AppDrawer/> should render
inside AppDrawer I am having more routes in
<main>
<Route path="/home" component={home}/>
Route path="/about" component={about}/>
</main>
My actual code is here:
App.js
import { Switch, Route } from "react-router-dom";
import AppDrawerBar from "./compponents/AppDrawerBar";
import Login from "./pages/Login";
import "./styles.css";
export default function App() {
return (
<div className="App">
<Switch>
<Route path="/login" exact component={Login} />
<Route path="/dashboard" component={AppDrawerBar} />
</Switch>
</div>
);
}
Login.js
import React from "react";
const Login = () => {
return <h1>Login Page</h1>;
};
export default Login;
AppDrawerBar.js
import React from "react";
import clsx from "clsx";
import { makeStyles, useTheme } from "#material-ui/core";
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";
import Home from "../pages/Home";
import About from "../pages/About";
import { Route, Switch } from "react-router-dom";
const drawerWidth = 240;
const useStyles = makeStyles((theme) => ({
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
}
}));
export default function PersistentDrawerLeft() {
const classes = useStyles();
const theme = useTheme();
const [open, setOpen] = React.useState(true);
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, open && classes.hide)}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap>
Persistent drawer
</Typography>
</Toolbar>
</AppBar>
<Drawer
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>
{["Inbox", "Starred", "Send email", "Drafts"].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
<Divider />
<List>
{["All mail", "Trash", "Spam"].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</Drawer>
<main
className={clsx(classes.content, {
[classes.contentShift]: open
})}
>
<div className={classes.drawerHeader} />
// More Routes for main pages
<Switch>
<Route path="/home" exact component={Home} />
<Route path="/about" component={About} />
</Switch>
</main>
</div>
);
}
Home.js
import React from "react";
const Home = () => {
return <h1>Home Page</h1>;
};
export default Home;
About.js
import React from "react";
const About = () => {
return <h1>About Page</h1>;
};
export default About;
You are mainly missing two steps to get your router working and updating your main content:
Propagate route change upon items click: The side Drawer component should be wrapped with a Router component and individual ListItems should be declared with Link as their component so whenever an item is clicked on the side menu, the change is propagated.
Updating your main content based on the navigated route: The main element should make use only of Route components to have the content dynamically loaded upon URL location change.
Here down the updated AppDrawerBar component (note that I omitted the redundant ListItems to bring focus to the important changes:
return (
<BrowserRouter>
<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, open && classes.hide)}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap>
Persistent drawer
</Typography>
</Toolbar>
</AppBar>
<Drawer
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>
<ListItem button key="home" to="/home" component={Link}>
<ListItemIcon>
<MailIcon />
</ListItemIcon>
<ListItemText primary="Home" />
</ListItem>
</List>
<Divider />
<List>
<ListItem button key="about" to="/about" component={Link}>
<ListItemIcon>
<InboxIcon />
</ListItemIcon>
<ListItemText primary="About" />
</ListItem>
</List>
</Drawer>
<main
className={clsx(classes.content, {
[classes.contentShift]: open
})}
>
<div className={classes.drawerHeader} />
<Route path="/home" exact component={Home} />
<Route path="/about" component={About} />
</main>
</div>
</BrowserRouter>
);
Edit:
The Link component mentioned above is the one from react-router-dom since it will redirect to the new /home and /about locations allowing the main directive content to update accordingly.
Here you can find a forked (working) copy of your AppDrawerBar component.
I saw you use react-router so if you want your drawer is displayed all the time except on the login page you can simply add conditional render for your drawer using the useHistory hook from "react-router-dom".
const history = useHistory
console.log(history.location.pathname) // give the current path name
So here is a solution :
export default function PersistentDrawerLeft() {
const history = useHistory(); // from react-router-dom
const classes = useStyles();
const theme = useTheme();
const [open, setOpen] = React.useState(true);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
return (
<div className={classes.root}>
<CssBaseline />
{history.location.pathname !== "/login" &&
<>
<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, open && classes.hide)}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap>
Persistent drawer
</Typography>
</Toolbar>
</AppBar>
<Drawer
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>
{["Inbox", "Starred", "Send email", "Drafts"].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
<Divider />
<List>
{["All mail", "Trash", "Spam"].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</Drawer>
</>
}
<main
className={clsx(classes.content, {
[classes.contentShift]: open
})}
>
<div className={classes.drawerHeader} />
// More Routes for main pages
<Switch>
<Route path="/home" exact component={Home} />
<Route path="/about" component={About} />
</Switch>
</main>
</div>
);
}
How can I get the Link from the <MenuItem> to render within <main> . I have been trying this for a long time now and can't seem to find an answer.
Here is the code Hope my problem is understandable. A link to a working example or so would also be fine.
Any help would be much appreciated. Thanks!
render() {
const { classes, children } = this.props;
const { mobileOpen} = this.state;
const drawer = (
<div>
<Hidden smDown>
<div className={classes.toolbar} />
</Hidden>
<MenuList>
<MenuItem component={Link} to="/">
Home
</MenuItem>
<MenuItem component={Link} to="/PatientdataPatient" >
Users
</MenuItem>
</MenuList>
</div>
);
return (
<div className={classes.root}>
<CssBaseline />
<AppBar position="fixed" className={classes.appBar}>
......code
<Drawer
container={this.props.container}
variant="temporary"
open={mobileOpen}
onClose={this.handleDrawerToggle}
classes={{
paper: classes.drawerPaper,
}}
>
{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} />
{children}
</main>
</div>
);
}
}
I decided to implement Drawer component to my react project from material-ui library, like so:
class RightDrawer extends React.Component {
state = {
open: false,
};
handleDrawerOpen = () => {
this.setState({ open: true });
};
handleDrawerClose = () => {
this.setState({ open: false });
};
render() {
const { classes, children, theme } = this.props;
return (
<div className={classes.root}>
<AppBar
position="absolute"
className={classNames(classes.appBar, this.state.open && classes.appBarShift)}
>
<Toolbar disableGutters={!this.state.open}>
<IconButton
color="inherit"
aria-label="Open drawer"
onClick={this.handleDrawerOpen}
className={classNames(classes.menuButton, this.state.open && classes.hide)}
>
<MenuIcon />
</IconButton>
<Typography variant="title" color="inherit" noWrap>
Mini variant drawer
</Typography>
</Toolbar>
</AppBar>
<Drawer
variant="permanent"
classes={{
paper: classNames(classes.drawerPaper, !this.state.open && classes.drawerPaperClose),
}}
open={this.state.open}
>
<div className={classes.toolbar}>
<IconButton onClick={this.handleDrawerClose}>
{theme.direction === 'rtl' ? <ChevronRightIcon /> : <ChevronLeftIcon />}
</IconButton>
</div>
<Divider />
<List>{mailFolderListItems}</List>
</Drawer>
<main className={classes.content}>
<div className={classes.toolbar} />
{children}
</main>
</div>
);
}
}
RightDrawer.propTypes = {
classes: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
};
export default withStyles(styles, { withTheme: true })(RightDrawer);
So I am wrapping all my components in RightDrawer component and simply inject them via {children}.
Final result is:
So it is cropped to like 1/3 of a page and when the table is filled:
Should I insert Drawer directly to App.js rather than wrapping every component inside? Maybe it is cause by className={classes.root} ?
The question description and code that you provide is not enough to answer this question.
As I found so far, If you want to have a full height drawer you might add height to your root class.
For example:
root: {
height: '100vh',
}