Drawer Material-ui Close Drawer Function Issue - reactjs

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}>
...

Related

Navigating to routes does not render page component in Next.js

I am very new to Next.js, and am trying to create a basic app where we have a header, and the header has 3 buttons clicking which the users should be redirected to the required route. The header should be present in all the pages, hence have put it in _app.js itself. Clicking on either of the 3 buttons does show a change in URL, but the component doesn't get rendered, and I am struggling to understand why that's happening. All the 3 files which we need to route to have been created under the 'pages' folder, and have their names set as expected.
The _app.js:
import Head from "next/head";
import Header from "./components/header";
function MyApp({ Component, pageProps }) {
return (
<>
<Head>
<title>GameZop Assignment</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<Header />
</>
);
}
export default MyApp;
The header.js(from MUI):
const drawerWidth = 240;
const navItems = [
{ value: "users", label: "Users" },
{ value: "news", label: "News" },
{ value: "topUsers", label: "Top Users" },
];
function Header(props) {
const { window } = props;
const router = useRouter();
const [mobileOpen, setMobileOpen] = React.useState(false);
const showComponent = (comp) => {
router.push(`/${comp}`)
}
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
};
const drawer = (
<Box onClick={handleDrawerToggle} sx={{ textAlign: "center" }}>
<Typography variant="h6" sx={{ my: 2 }}>
MUI
</Typography>
<Divider />
<List>
{navItems.map((item) => (
<ListItem key={item.value} disablePadding>
<ListItemButton sx={{ textAlign: "center" }}>
<ListItemText primary={item.label} />
</ListItemButton>
</ListItem>
))}
</List>
</Box>
);
const container =
window !== undefined ? () => window().document.body : undefined;
return (
<Box sx={{ display: "flex" }}>
<AppBar component="nav">
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
edge="start"
onClick={handleDrawerToggle}
sx={{ mr: 2, display: { sm: "none" } }}
>
<MenuIcon />
</IconButton>
<Typography
variant="h6"
component="div"
sx={{ flexGrow: 1, display: { xs: "none", sm: "block" } }}
>
Assignment
</Typography>
<Box sx={{ display: { xs: "none", sm: "block" } }}>
{navItems.map((item) => (
<Button
key={item}
sx={{ color: "#fff" }}
onClick={() => showComponent(item.value)}
>
{item.label}
</Button>
))}
</Box>
</Toolbar>
</AppBar>
<Box component="nav">
<Drawer
container={container}
variant="temporary"
open={mobileOpen}
onClose={handleDrawerToggle}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
sx={{
display: { xs: "block", sm: "none" },
"& .MuiDrawer-paper": {
boxSizing: "border-box",
width: drawerWidth,
},
}}
>
{drawer}
</Drawer>
</Box>
</Box>
);
}
Header.propTypes = {
window: PropTypes.func,
};
export default Header;
Finally, the users.js, which I want to load below this header:
function Users(){
return (
<>
<h2>Hello from users</h2>
</>
)
}
export default Users;
I found the mistake, I had removed the rendering of 'Component' from _app.js, hence the components weren't loading. The _app.js now becomes:
import Head from "next/head";
import Header from "./components/header";
function MyApp({ Component, pageProps }) {
return (
<>
<Head>
<title>GameZop Assignment</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<Header />
<Component {...pageProps} />
</>
);
}
export default MyApp;

Chakra ui - page shell - how to populate the page based on sidebar menu

I'm trying to figure out how to use the Chakra UI page shell, so that I can add components to render in the main segment of the shell, for each sidebar menu item.
The sidebar menu has NavButton items as follows:
<NavButton label="Settings" />
I'm trying to find a way to add a component (say, Settings) which has the content to render in the main segment of the shell.
How can I do that?
The full dashboard is as follows:
import {
// As,
Avatar,
Box,
BoxProps,
Button,
ButtonProps,
chakra,
Container,
Divider,
Drawer,
DrawerContent,
DrawerOverlay,
Flex,
Heading,
HStack,
Icon,
Input,
InputLeftElement,
InputGroup,
// Progress,
// SimpleGrid,
Stack,
Text,
useBreakpointValue,
useColorModeValue,
useDisclosure,
IconButton,
IconButtonProps
} from '#chakra-ui/react'
import * as React from 'react'
import { FiDownloadCloud } from 'react-icons/fi'
// import { CgWorkAlt } from 'react-icons/cg'
import {
// FiBarChart2,
// FiBookmark,
// FiCheckSquare,
// FiHelpCircle,
// FiHome,
FiSearch,
// FiSettings
} from 'react-icons/fi'
const Card = (props: BoxProps) => (
<Box
minH="3xs"
bg="bg-surface"
boxShadow={useColorModeValue('sm', 'sm-dark')}
borderRadius="lg"
{...props}
/>
)
const Bar = chakra('span', {
baseStyle: {
display: 'block',
pos: 'absolute',
w: '1.25rem',
h: '0.125rem',
rounded: 'full',
bg: 'currentcolor',
mx: 'auto',
insetStart: '0.125rem',
transition: 'all 0.12s',
},
})
const ToggleIcon = (props: { active: boolean }) => {
const { active } = props
return (
<Box
className="group"
data-active={active ? '' : undefined}
as="span"
display="block"
w="1.5rem"
h="1.5rem"
pos="relative"
aria-hidden
pointerEvents="none"
>
<Bar top="0.4375rem" _groupActive={{ top: '0.6875rem', transform: 'rotate(45deg)' }} />
<Bar bottom="0.4375rem" _groupActive={{ bottom: '0.6875rem', transform: 'rotate(-45deg)' }} />
</Box>
)
}
interface ToggleButtonProps extends IconButtonProps {
isOpen: boolean
}
const ToggleButton = (props: ToggleButtonProps) => {
const { isOpen, ...iconButtonProps } = props
return (
<IconButton
position="relative"
variant="unstyled"
size="sm"
color={isOpen ? 'white' : 'muted'}
zIndex="skipLink"
icon={<ToggleIcon active={isOpen} />}
{...iconButtonProps}
/>
)
}
interface UserProfileProps {
name: string
// image: string
title: string
}
const UserProfile = (props: UserProfileProps) => {
const { name, title } = props
return (
<HStack spacing="3" ps="2">
<Avatar name={name} boxSize="10" />
<Box>
<Text fontWeight="medium" fontSize="sm">
{name}
</Text>
<Text color="muted" fontSize="sm">
{title}
</Text>
</Box>
</HStack>
)
}
const Sidebar = () => (
<Flex as="section" minH="100vh" bg="bg-canvas" maxW="100%" p="0">
<Flex
flex="1"
bg="brand.white"
// overflowY="auto"
maxW="100%" p="0"
// maxW={{ base: 'full', sm: 'xs' }}
py={{ base: '6', sm: '8' }}
pl={4}
pr={6}
mr={4}
// px={{ base: '4', sm: '6' }}
>
<Stack justify="space-between" spacing="1">
<Stack spacing={{ base: '5', sm: '6' }} shouldWrapChildren>
<InputGroup>
<InputLeftElement pointerEvents="none">
<Icon as={FiSearch} color="muted" boxSize="5" />
</InputLeftElement>
<Input placeholder="Search" />
</InputGroup>
<Stack spacing="1" >
<NavButton label="Home" fontWeight="normal"/>
<NavButton label="Dashboard" aria-current="page" fontWeight="normal" />
<NavButton label="Tasks" fontWeight="normal" />
<NavButton label="ddd" fontWeight="normal"/>
</Stack>
</Stack>
<Stack spacing={{ base: '5', sm: '6' }}>
<Stack spacing="1">
<NavButton label="Library" fontWeight="normal" />
<NavButton label="Help" fontWeight="normal"/>
<NavButton label="Settings" fontWeight="normal"/>
</Stack>
{/* <Box bg="bg-subtle" px="4" py="5" borderRadius="lg">
<Stack spacing="4">
<Stack spacing="1">
<Text fontSize="sm" fontWeight="medium">
Almost there
</Text>
<Text fontSize="sm" color="muted">
Fill in some more information about you and your person.
</Text>
</Stack>
<Progress value={80} size="sm" aria-label="Profile Update Progress" />
<HStack spacing="3">
<Button variant="link" size="sm">
Dismiss
</Button>
<Button variant="link" size="sm" colorScheme="blue">
Update profile
</Button>
</HStack>
</Stack>
</Box> */}
<Divider />
<UserProfile
name="Asdfon"
// image="adsf"
title="adsf, asdf"
/>
</Stack>
</Stack>
</Flex>
</Flex>
)
const Navbar = () => {
const { isOpen, onToggle, onClose } = useDisclosure()
return (
<Box
width="full"
maxW="100%"
py="4"
px={0}
// px={{ base: '4', md: '8' }}
bg="bg-surface"
boxShadow={useColorModeValue('sm', 'sm-dark')}
>
<Flex justify="space-between">
<ToggleButton isOpen={isOpen} aria-label="Open Menu" onClick={onToggle} />
<Drawer
isOpen={isOpen}
placement="left"
onClose={onClose}
isFullHeight
preserveScrollBarGap
// Only disabled for showcase
trapFocus={false}
>
<DrawerOverlay />
<DrawerContent>
<Sidebar />
</DrawerContent>
</Drawer>
</Flex>
</Box>
)
}
interface NavButtonProps extends ButtonProps {
// icon: As
label: string
}
const NavButton = (props: NavButtonProps) => {
const { label, ...buttonProps } = props
return (
<Button variant="ghost" justifyContent="start" {...buttonProps}>
<HStack spacing="3">
{/* <Icon as={icon} boxSize="6" color="subtle" /> */}
<Text>{label}</Text>
</HStack>
</Button>
)
}
const DashBase = () => {
const isDesktop = useBreakpointValue({ base: false, lg: true })
return (
<Flex
as="section"
direction={{ base: 'column', lg: 'row' }}
height="100vh"
bg="white"
mb="100px"
overflowY="auto"
minW="100%"
px={0}
mx="0px"
// minW="120em"
// margin="auto"
>
{isDesktop ? <Sidebar /> : <Navbar />}
<Container py="8" flex="1">
<Stack spacing={{ base: '8', lg: '6' }}>
<Stack
spacing="4"
direction={{ base: 'column', lg: 'row' }}
// justify="space-between"
align={{ base: 'start', lg: 'center' }}
>
<Stack spacing="1">
<Heading size={useBreakpointValue({ base: 'xs', lg: 'sm' })} fontWeight="medium">
Dashboard
</Heading>
<Text color="muted">All important metrics at a glance</Text>
</Stack>
<HStack spacing="3">
<Button variant="secondary" leftIcon={<FiDownloadCloud fontSize="1.25rem" />}>
Download
</Button>
<Button variant="primary">Create</Button>
</HStack>
</Stack>
{/* <Stack spacing={{ base: '5', lg: '6' }} maxW="100%">
<SimpleGrid columns={{ base: 1, md: 3 }} gap="6">
<Card />
<Card />
<Card />
</SimpleGrid>
</Stack> */}
<Card minH="sm" />
</Stack>
</Container>
</Flex>
)
}
export default DashBase;
You could wrap your NavButton in Link
<Link href="/settings">
<NavButton label="Home" fontWeight="normal"/>
</Link>
In pages/settings.tsx, have a component that renders your settings page.
In _app.tsx, pass Component as a child to Sidebar
<Sidebar>
<Component {...pageProps} />
</Sidebar>
In Sidebar, add a param for children. Component will be in the children props. Render children prop after the menu.
const Sidebar = ({ children }) => (
...
</Flex>
{children}
</Flex>
)

How do I link to another page in my MUI mini drawer sidebar?

I've tried to encapsulate my ListItem component with <Link to"/create> but it doesn't work, i've also tried using the history option but that confused me.
My codesandbox:
codesandbox.io/s/funny-einstein-deuqhi?file=/src/App.js
This is my sidebar.js (without all the imports and the creation of mixin&drawerheader and co, the link on the appbar to my createsite works, i want the same for my sidebar listitems. i want my listitem with the text ."add to-do" to link to the "\create" page):
const openedMixin = (theme) => (...);
const closedMixin = (theme) => (...);
const DrawerHeader = styled(...);
const AppBar = styled(...);
const Drawer = styled(...);
export default function MiniDrawer() {
const theme = useTheme();
const [open, setOpen] = React.useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
const itemsList = [
{
text: "Calendar",
icon: <EventAvailableOutlinedIcon style={{ fill: 'white' }} />,
},
{
text: "Add To-do",
icon: <AddBoxTwoToneIcon style={{ fill: 'white' }} />
}
]
return (
<Router>
<Box sx={{ display: 'flex' }}>
<CssBaseline />
<AppBar position="fixed" open={open} style={{ background: 'white' }}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={handleDrawerOpen}
edge="start"
sx={{
marginRight: 5,
...(open && { display: 'none' }),
}}
>
<MenuIcon style={{ fill: '#85CEE9' }} />
</IconButton>
<Link to="/create">create</Link>
<Typography variant="h6" noWrap component="div" style={{ color: "#85CEE9" }}>
project
</Typography>
</Toolbar>
</AppBar>
<Drawer variant="permanent" open={open}>
<DrawerHeader>
<IconButton onClick={handleDrawerClose}>
{theme.direction === 'rtl' ? <ChevronRightIcon style={{ fill: 'white' }} /> : <ChevronLeftIcon style={{ fill: 'white' }} />}
</IconButton>
</DrawerHeader>
<Divider />
<List>
{itemsList.map((item, index) => {
const { text, icon } = item;
return (
// <Link to="/create">
<Link to="/create">
<ListItem button component={Link} to="/create" onClick={onItemClick('Page 2')} key={text}>
{icon && <ListItemIcon >{icon}</ListItemIcon>}
// <ListItemText primary={text} style={{ color: "white" }} />
<Link to="/create">create</Link>
</ListItem>
</Link>
// </Link>
);
})}
</List>
</Drawer>
</Box>
</Router>
);
}```
In the sandbox you linked you weren't using the Link component at all in the drawer, so nothing was being linked to.
Render the ListItem component as a Link component and pass the appropriate link props.
Example:
const itemsList = [
{
text: "Calendar",
icon: <EventAvailableOutlinedIcon style={{ fill: "white" }} />,
to: "/create" // <-- add link targets
},
{
text: "Add To-do",
icon: <AddBoxTwoToneIcon style={{ fill: "white" }} />,
to: "/add-todo"
}
];
To the ListItem component, specify the Link component to be the component to render the list item as, and pass the current mapped item's to property.
<List>
{itemsList.map((item, index) => {
const { text, icon } = item;
return (
<ListItem component={Link} to={item.to} button key={text}>
{icon && <ListItemIcon>{icon}</ListItemIcon>}
<ListItemText primary={text} style={{ color: "white" }} />
</ListItem>
);
})}
</List>
Instead of using Link within your List component, use ListItem, ListItemButton, and ListItemText:
<List>
{['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => (
<ListItem key = {text} disablePadding>
<ListItemButton onClick = {handleNav} >
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItemButton>
</ListItem>
))}
</List>
Create the method to pass to each ListItemButton's onClick:
import { useHistory } from 'react-router-dom'
const history = useHistory()
const handleNav = (event) =>
{
// Do whatever you need to do then push to the new page
history.push('/create')
}
Depending on what version of react-router you're using, your actual method to push to a new page may vary (useHistory for v5, useNavigate for v6, etc.).

Show component when onClick method is called

I have imported a schedular component from devexpress and have modified the appointments in this with the constant MyAppointment. Now i want to be able to delete and modify apointment data with a dialog when clicking on these. Therefore i added an onClick method to the MyAppointment const and tried to return the dialog however nothing happens when pressing the appointments.
const MyAppointment = ({ children, style, ...restProps }) => {
return (
<Appointments.Appointment
{...restProps}
style={{
...style,
backgroundColor: "#a02d37",
borderRadius: "8px",
}}
onClick={()=>{
return (
<SimpleDialogDemo />
)
}}
>
{children}
</Appointments.Appointment>
);
}
const emails = ['username#gmail.com', 'user02#gmail.com'];
function SimpleDialog(props) {
const { onClose, selectedValue, open } = props;
const handleClose = () => {
onClose(selectedValue);
};
const handleListItemClick = (value) => {
onClose(value);
};
return (
<Dialog onClose={handleClose} open={open}>
<DialogTitle>Set backup account</DialogTitle>
<List sx={{ pt: 0 }}>
{emails.map((email) => (
<ListItem button onClick={() => handleListItemClick(email)} key={email}>
<ListItemAvatar>
<Avatar sx={{ bgcolor: blue[100], color: blue[600] }}>
<PersonIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary={email} />
</ListItem>
))}
<ListItem autoFocus button onClick={() => handleListItemClick('addAccount')}>
<ListItemAvatar>
<Avatar>
<AddIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="Add account" />
</ListItem>
</List>
</Dialog>
);
}
SimpleDialog.propTypes = {
onClose: PropTypes.func.isRequired,
open: PropTypes.bool.isRequired,
selectedValue: PropTypes.string.isRequired,
};
export default function SimpleDialogDemo() {
const [open, setOpen] = React.useState(true);
const [selectedValue, setSelectedValue] = React.useState(emails[1]);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = (value) => {
setOpen(false);
setSelectedValue(value);
};
handleClickOpen();
return (
<div>
<SimpleDialog
selectedValue={selectedValue}
open={open}
onClose={handleClose}
/>
</div>
);
}
onClick={()=>{
return (
<SimpleDialogDemo />
)
}}
isn't actually doing anything. All you're doing is returning a component in an event handler that doesn't do anything with that return value. The onClick event handler is react-agnostic.
If you want to display a component when something is clicked, you'll need to use local state variables to show/hide it based on a click action like below:
const MyAppointment = ({ children, style, ...restProps }) => {
const [showDialog, setShowDialog] = useState(false);
return (
<Appointments.Appointment
{...restProps}
style={{
...style,
backgroundColor: "#a02d37",
borderRadius: "8px",
}}
onClick={()=> setShowDialog(true)}
>
{children}
{showDialog && <SimpleDialogDemo onClose=(() => setShowDialog(false))/>}
</Appointments.Appointment>
);
}
Note the onClose prop I added to your SimpleDialog component so that you have a way of setting the showDialog state back to hidden (you will need to add this propr if you use it).

My react component return statement fails to render but console.log shows exactly what I need

I am new to react and working on a react video player. I'm having issue implementing the comment section.
This is my videoplayer component itself.
export default function VidPlayer() {
// useStates
const [state, setState] = useState({
playing: true,
});
const [comments, setComments] = useState([]);
const [comment, setComment] = useState("");
const { playing } = state;
const playerRef = useRef(null);
// declaring functions for video player buttons
const handlePlayPause = () => {
setState({ ...state, playing: !state.playing });
};
const handleRewind = () => {
playerRef.current.seekTo(playerRef.current.getCurrentTime() - 5);
};
const handleFoward = () => {
playerRef.current.seekTo(playerRef.current.getCurrentTime() + 5);
};
const handleStop = () => {
playerRef.current.seekTo(0);
setState({ playing: !state.playing });
};
// declaring functions for comment section
const addComments = () => {
if (comment) {
setComments({...comments, comment});
setComment("");
console.log("Hello", comments);
}
};
const handleComment = (e) => {
setComment(e.target.value);
};
return (
<div>
<AppBar style={{ background: "#e6880e" }} position="static">
<Toolbar>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
Favour's Video Player
</Typography>
</Toolbar>
</AppBar>
{/* container for the videoplayer, buttons and comment section */}
<div className="container">
<>
{/* videoplayer */}
<div className="reactPlayer one">
<ReactPlayer
ref={playerRef}
url="https://www.youtube.com/watch?v=1_ATK0BLc8U&t=3s"
playing={playing}
controls
/>
</div>
{/* buttons */}
<div className="btn-stack two">
<Stack spacing={2} direction="row">
<Button
style={{ background: "#e6880e" }}
size="small"
variant="contained"
onClick={handlePlayPause}
>
Play
</Button>
<Button
style={{ background: "#e6880e" }}
size="small"
variant="contained"
onClick={handleRewind}
>
Rewind{" "}
</Button>
<Button
style={{ background: "#e6880e" }}
size="small"
variant="contained"
onClick={handleFoward}
>
Forward{" "}
</Button>
<Button
style={{ background: "#e6880e" }}
size="small"
variant="contained"
onClick={handleStop}
>
Stop
</Button>
</Stack>
</div>
{/* comment section */}
<div className="comment three">
<Comment userComs={comments} />
<TextField
placeholder="add comment"
size="small"
variant="outlined"
onChange={handleComment}
value={comment}
/>
<Button
style={{ background: "#e6880e", marginLeft: '1em' }}
onClick={addComments}
variant="contained"
size="small"
>
Send
</Button>
</div>
</>
</div>
</div>
);
}
It takes in this comments component towards the end.
export default function commentList(props) {
console.log("Hello brian", props.userComs);
const { userComs } = props;
if (Object.keys(userComs).length > 0) {
console.log(userComs);
// userComs.forEach((element) => {
// console.log("Im in", userComs);
Object.values(userComs).forEach(val => {
// console.log("Im in", userComs);
// console.log(val);
return (
<div>
<List
sx={{ width: "100%", maxWidth: 360, bgcolor: "background.paper" }}
>
<ListItem alignItems="flex-start">
<ListItemAvatar>
<Avatar alt="Remy Sharp" src="/static/images/avatar/1.jpg" />
</ListItemAvatar>
<ListItemText
secondary={
<React.Fragment>
<Typography
sx={{ display: "inline" }}
component="span"
variant="body2"
color="text.primary"
>
Ali Connors
</Typography>
{val}
</React.Fragment>
}
/>
</ListItem>
<Divider variant="inset" component="li" />
</List>
</div>
);
});
} else {
return <div></div>;
}
}
This is the Front-End enter image description here
Unfortunately, when I enter a comment and click send, the screen goes blank and console throws a "nothing was returned from render" error. Can someone help me check what is wrong and how I can fix this please?
As the error says, the component isn't returning anything.
Object.values(userComs).forEach(val => {
should be
return Object.values(userComs).map(val => {
because forEach doesn't return anything and the JSX returned in each iteration will not be used anywhere, but map returns a new array that React can use.
BTW make sure to give a key prop to each div that is returned from the callback.
<div key={val}> // assuming `val` is unique

Resources